repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / cortex / TipManager / TipManagerImpl.cpp
blob29aaa5c9f8ca87167eb8739ba3cb57d27227eae2
1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // TipManagerImpl.cpp
33 // e.moon 13may99
35 #include <algorithm>
37 #include "TipManager.h"
38 #include "TipManagerImpl.h"
39 #include "TipWindow.h"
41 #include <Autolock.h>
42 #include <Debug.h>
43 #include <MessageRunner.h>
44 #include <Region.h>
45 #include <Screen.h>
47 //#include "debug_tools.h"
49 using namespace std;
51 __USE_CORTEX_NAMESPACE
53 // -------------------------------------------------------- //
55 // [e.moon 13oct99] now matches entry by pointer
56 class entry_target_matches_view { public:
57 const BView* pView;
58 entry_target_matches_view(const BView* p) : pView(p) {}
59 bool operator()(const _ViewEntry* view) const {
60 return view->target() == pView;
64 // -------------------------------------------------------- //
65 // _ViewEntry impl.
66 // -------------------------------------------------------- //
68 // [e.moon 13oct99] delete tips & children
69 _ViewEntry::~_ViewEntry() {
70 for(list<_ViewEntry*>::iterator it = m_childViews.begin();
71 it != m_childViews.end(); ++it) {
72 delete *it;
74 for(tip_entry_set::iterator it = m_tips.begin();
75 it != m_tips.end(); ++it) {
76 delete *it;
80 // add the given entry for the designated view
81 // (which may be the target view or a child.)
82 // returns B_OK on success, B_ERROR if the given view is
83 // NOT a child .
85 status_t _ViewEntry::add(BView* pView, const tip_entry& tipEntry) {
87 // walk up the view's parent tree, building a child-
88 // hierarchy list and looking for my target view.
89 // The list should be in descending order: the last
90 // entry is pView.
91 // +++++ move to separate method
92 list<BView*> parentOrder;
93 BView* pCurView = pView;
94 while(pCurView && pCurView != m_target) {
95 parentOrder.push_front(pCurView);
96 pCurView = pCurView->Parent();
98 if(pCurView != m_target)
99 return B_ERROR; // +++++ ever so descriptive
101 // walk down the child hierarchy, making ViewEntries as
102 // needed
103 _ViewEntry* viewEntry = this;
105 // [e.moon 13oct99] clone tipEntry
106 tip_entry* newTipEntry = new tip_entry(tipEntry);
108 for(list<BView*>::iterator itView = parentOrder.begin();
109 itView != parentOrder.end(); itView++) {
111 // look for this view in children of the current entry
112 list<_ViewEntry*>::iterator itEntry =
113 find_if(
114 viewEntry->m_childViews.begin(),
115 viewEntry->m_childViews.end(),
116 entry_target_matches_view(*itView));
118 // add new _ViewEntry if necessary
119 if(itEntry == viewEntry->m_childViews.end()) {
120 viewEntry->m_childViews.push_back(new _ViewEntry(*itView, viewEntry));
121 viewEntry = viewEntry->m_childViews.back();
122 } else
123 viewEntry = *itEntry;
126 // found a home; can it hold the tip?
127 if(viewEntry->m_tips.size() &&
128 !(*viewEntry->m_tips.begin())->rect.IsValid()) {
129 // [e.moon 13oct99] clean up
130 delete newTipEntry;
131 return B_ERROR; // +++++ error: full-view tip leaves no room
134 // remove matching tip if any, then add the new one:
135 // [e.moon 13oct99] ref'd by pointer
136 tip_entry_set::iterator itFound = viewEntry->m_tips.find(newTipEntry);
137 if(itFound != viewEntry->m_tips.end()) {
138 delete *itFound;
139 viewEntry->m_tips.erase(itFound);
142 pair<tip_entry_set::iterator, bool> ret;
143 ret = viewEntry->m_tips.insert(newTipEntry);
145 // something's terribly wrong if insert() failed
146 ASSERT(ret.second);
148 return B_OK;
151 // remove tip matching the given rect's upper-left corner or
152 // all tips if rect is invalid.
153 // returns B_OK on success, B_ERROR on failure
154 status_t _ViewEntry::remove(
155 BView* pView, const BRect& rect) {
157 // walk up the view's parent tree, building a child-
158 // hierarchy list and looking for my target view.
159 // The list should be in descending order: the last
160 // entry is pView.
161 // +++++ move to separate method
162 list<BView*> parentOrder;
163 BView* pCurView = pView;
164 while(pCurView && pCurView != m_target) {
165 parentOrder.push_front(pCurView);
166 pCurView = pCurView->Parent();
168 if(pCurView != m_target)
169 return B_ERROR; // +++++ ever so descriptive
171 // walk down the child tree to the entry for the
172 // target view
173 _ViewEntry* viewEntry = this;
174 for(list<BView*>::iterator itView = parentOrder.begin();
175 itView != parentOrder.end(); itView++) {
177 // look for this view in children of the current entry
178 list<_ViewEntry*>::iterator itEntry =
179 find_if(
180 viewEntry->m_childViews.begin(),
181 viewEntry->m_childViews.end(),
182 entry_target_matches_view(*itView));
184 // it'd better be there!
185 if(itEntry == viewEntry->m_childViews.end())
186 return B_ERROR;
188 viewEntry = *itEntry;
191 // remove matching entries:
192 // [13oct99 e.moon] now ref'd by pointer; find and erase all matching tips
193 if(rect.IsValid()) {
194 tip_entry matchEntry(rect);
195 tip_entry_set::iterator it = viewEntry->m_tips.lower_bound(&matchEntry);
196 tip_entry_set::iterator itEnd = viewEntry->m_tips.upper_bound(&matchEntry);
197 while(it != itEnd) {
198 // found one; erase it
199 delete *it;
200 viewEntry->m_tips.erase(it++);
203 else {
204 // invalid rect == wildcard
206 // PRINT((
207 // "### _ViewEntry::remove(): WILDCARD MODE\n"));
209 // [13oct99 e.moon] free all tip entries
210 for(
211 tip_entry_set::iterator it = viewEntry->m_tips.begin();
212 it != viewEntry->m_tips.end(); ++it) {
213 delete *it;
215 viewEntry->m_tips.clear();
217 // PRINT((
218 // "### - freed all tips\n"));
220 // [27oct99 e.moon] remove all child views
221 for(
222 list<_ViewEntry*>::iterator itChild = viewEntry->m_childViews.begin();
223 itChild != viewEntry->m_childViews.end(); ++itChild) {
225 delete *itChild;
227 viewEntry->m_childViews.clear();
229 // PRINT((
230 // "### - freed all child views\n"));
232 // remove the view entry if possible
233 if(viewEntry->m_parent) {
234 PRINT((
235 "### - removing view entry from %p\n",
236 viewEntry->m_parent));
238 list<_ViewEntry*>::iterator it =
239 find_if(
240 viewEntry->m_parent->m_childViews.begin(),
241 viewEntry->m_parent->m_childViews.end(),
242 entry_target_matches_view(pView));
243 ASSERT(it != viewEntry->m_parent->m_childViews.end());
245 _ViewEntry* parent = viewEntry->m_parent;
246 delete viewEntry;
247 parent->m_childViews.erase(it);
251 return B_OK;
254 // match the given point (in target's view coordinates)
255 // against tips in this view and child views. recurse.
257 pair<BView*, const tip_entry*> _ViewEntry::match(
258 BPoint point, BPoint screenPoint) {
260 // fetch this view's current frame rect
261 BRect f = Frame();
263 // check for a full-frame tip:
265 const tip_entry* pFront = fullFrameTip();
266 if(pFront) {
267 // match, and stop recursing here; children can't have tips.
268 m_target->ConvertFromParent(&f);
269 return make_pair(m_target, f.Contains(point) ? pFront : 0);
272 // match against tips for my target view
273 if(m_tips.size()) {
275 tip_entry matchEntry(BRect(point, point));
276 tip_entry_set::iterator itCur = m_tips.lower_bound(&matchEntry);
277 // tip_entry_set::iterator itCur = m_tips.begin();
278 tip_entry_set::iterator itEnd = m_tips.end();
280 while(itCur != itEnd) {
281 // match:
282 const tip_entry* entry = *itCur;
283 if(entry->rect.Contains(point))
284 return pair<BView*, const tip_entry*>(m_target, entry);
286 ++itCur;
290 // recurse through children
291 for(list<_ViewEntry*>::iterator it = m_childViews.begin();
292 it != m_childViews.end(); it++) {
294 _ViewEntry* entry = *it;
295 BPoint childPoint =
296 entry->target()->ConvertFromParent(point);
298 pair<BView*, const tip_entry*> ret = entry->match(
299 childPoint,
300 screenPoint);
302 if(ret.second)
303 return ret;
306 // nothing found
307 return pair<BView*, const tip_entry*>(0, 0);
310 // get frame rect (in parent view's coordinates)
311 BRect _ViewEntry::Frame() {
312 ASSERT(m_target);
313 // ASSERT(m_target->Parent());
315 // +++++ if caching or some weird proxy mechanism
316 // works out, return a cached BRect here
317 // rather than asking every view every time!
319 BRect f = m_target->Frame();
320 return f;
323 // returns pointer to sole entry in the set if it's
324 // a full-frame tip, or 0 if there's no full-frame tip
325 const tip_entry* _ViewEntry::fullFrameTip() const {
326 if(m_tips.size()) {
327 const tip_entry* front = *m_tips.begin();
328 if(!front->rect.IsValid()) {
329 return front;
332 return 0;
335 size_t _ViewEntry::countTips() const {
337 size_t tips = m_tips.size();
338 for(list<_ViewEntry*>::const_iterator it = m_childViews.begin();
339 it != m_childViews.end(); it++) {
340 tips += (*it)->countTips();
343 return tips;
347 void _ViewEntry::dump(int indent) {
348 BString s;
349 s.SetTo('\t', indent);
350 PRINT((
351 "%s_ViewEntry '%s'\n",
352 s.String(),
353 m_target->Name()));
355 for(tip_entry_set::iterator it = m_tips.begin();
356 it != m_tips.end(); ++it) {
357 (*it)->dump(indent + 1);
359 for(list<_ViewEntry*>::iterator it = m_childViews.begin();
360 it != m_childViews.end(); it++) {
361 (*it)->dump(indent + 1);
365 // -------------------------------------------------------- //
366 // _WindowEntry impl
367 // -------------------------------------------------------- //
369 _WindowEntry::~_WindowEntry() {
370 for(list<_ViewEntry*>::iterator it = m_views.begin();
371 it != m_views.end(); ++it) {
372 delete *it;
376 // add the given entry for the designated view (which must
377 // be attached to the target window)
378 // returns B_OK on success, B_ERROR if:
379 // - the given view is NOT attached to the target window, or
380 // - tips can't be added to this view due to it, or one of its
381 // parents, having a full-frame tip.
383 status_t _WindowEntry::add(
384 BView* view,
385 const tip_entry& entry) {
387 ASSERT(view);
388 if(view->Window() != target())
389 return B_ERROR;
391 // find top-level view
392 BView* parent = view;
393 while(parent && parent->Parent())
394 parent = parent->Parent();
396 // look for a _ViewEntry matching the parent & hand off
397 for(list<_ViewEntry*>::iterator it = m_views.begin();
398 it != m_views.end(); ++it)
399 if((*it)->target() == parent)
400 return (*it)->add(view, entry);
402 // create _ViewEntry for the parent & hand off
403 _ViewEntry* v = new _ViewEntry(parent, 0);
404 m_views.push_back(v);
406 return v->add(view, entry);
409 // remove tip matching the given rect's upper-left corner or
410 // all tips if rect is invalid.
411 // returns B_ERROR on failure -- if there are no entries for
412 // the given view -- or B_OK otherwise.
414 status_t _WindowEntry::remove(
415 BView* view,
416 const BRect& rect) {
418 ASSERT(view);
419 if(view->Window() != target())
420 return B_ERROR;
422 // find top-level view
423 BView* parent = view;
424 while(parent && parent->Parent())
425 parent = parent->Parent();
427 // look for a matching _ViewEntry & hand off
428 for(list<_ViewEntry*>::iterator it = m_views.begin();
429 it != m_views.end(); ++it)
430 if((*it)->target() == parent) {
432 // do it
433 status_t ret = (*it)->remove(view, rect);
435 if(!(*it)->countTips()) {
436 // remove empty entry
437 delete *it;
438 m_views.erase(it);
440 return ret;
443 // not found
444 PRINT((
445 "!!! _WindowEntry::remove(): no matching view\n"));
446 return B_ERROR;
449 // match the given point (in screen coordinates)
450 // against tips in this window's views.
452 pair<BView*, const tip_entry*> _WindowEntry::match(
453 BPoint screenPoint) {
455 for(list<_ViewEntry*>::iterator it = m_views.begin();
456 it != m_views.end(); ++it) {
458 // +++++ failing on invalid views? [e.moon 27oct99]
460 BView* target = (*it)->target();
461 if(target->Window() != m_target) {
462 PRINT((
463 "!!! _WindowEntry::match(): unexpected window for target view (%p)\n",
464 target));
466 // skip it
467 return pair<BView*,const tip_entry*>(0,0);
469 pair<BView*,const tip_entry*> ret = (*it)->match(
470 (*it)->target()->ConvertFromScreen(screenPoint),
471 screenPoint);
472 if(ret.second)
473 return ret;
476 return pair<BView*,const tip_entry*>(0,0);
479 void _WindowEntry::dump(int indent) {
480 BString s;
481 s.SetTo('\t', indent);
482 PRINT((
483 "%s_WindowEntry '%s'\n",
484 s.String(),
485 m_target->Name()));
487 for(list<_ViewEntry*>::iterator it = m_views.begin();
488 it != m_views.end(); it++) {
489 (*it)->dump(indent + 1);
494 // -------------------------------------------------------- //
495 // _TipManagerView
496 // -------------------------------------------------------- //
498 // -------------------------------------------------------- //
499 // *** ctor/dtor
500 // -------------------------------------------------------- //
502 _TipManagerView::~_TipManagerView() {
503 for(list<_WindowEntry*>::iterator it = m_windows.begin();
504 it != m_windows.end(); ++it) {
505 delete *it;
507 if(m_messageRunner)
508 delete m_messageRunner;
510 // clean up the tip-display window
511 m_tipWindow->Lock();
512 m_tipWindow->Quit();
515 _TipManagerView::_TipManagerView(
516 TipWindow* tipWindow,
517 TipManager* manager,
518 bigtime_t updatePeriod,
519 bigtime_t idlePeriod) :
520 BView(
521 BRect(0,0,0,0),
522 "_TipManagerView",
523 B_FOLLOW_NONE,
524 B_PULSE_NEEDED),
525 m_tipWindow(tipWindow),
526 m_manager(manager),
527 m_messageRunner(0),
528 m_tipWindowState(TIP_WINDOW_HIDDEN),
529 m_updatePeriod(updatePeriod),
530 m_idlePeriod(idlePeriod),
531 m_lastEventTime(0LL),
532 m_triggered(false),
533 m_armedTip(0) {
535 ASSERT(m_tipWindow);
536 ASSERT(m_manager);
538 // +++++ is this cheating?
539 m_tipWindow->Run();
541 // request to be sent all mouse & keyboard events
542 SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS);
544 // don't draw
545 SetViewColor(B_TRANSPARENT_COLOR);
549 // -------------------------------------------------------- //
550 // *** operations
551 // -------------------------------------------------------- //
553 // Prepare a 'one-off' tip: this interrupts normal mouse-watching
554 // behavior while the mouse remains in the given screen rectangle.
555 // If it idles long enough, the tip window is displayed.
557 status_t _TipManagerView::armTip(
558 const BRect& screenRect,
559 const char* text,
560 TipManager::offset_mode_t offsetMode,
561 BPoint offset,
562 uint32 flags) {
564 ASSERT(Looper()->IsLocked());
566 ASSERT(text);
567 if(!screenRect.IsValid())
568 return B_BAD_VALUE;
570 // cancel previous manual tip operation
571 if(m_armedTip) {
572 ASSERT(m_tipWindowState == TIP_WINDOW_ARMED);
573 delete m_armedTip;
574 m_armedTip = 0;
577 // is a tip with the same screen rect visible? update it:
578 if(m_tipWindowState == TIP_WINDOW_VISIBLE &&
579 m_visibleTipRect == screenRect) {
580 BAutolock _l(m_tipWindow);
581 m_tipWindow->setText(text);
582 return B_OK;
585 // create new entry; enter 'armed' state
586 m_armedTip = new tip_entry(
587 screenRect,
588 text,
589 offsetMode,
590 offset,
591 flags);
592 m_tipWindowState = TIP_WINDOW_ARMED;
594 return B_OK;
597 // Hide tip corresponding to the given screenRect, if any.
598 // [e.moon 29nov99] Cancel 'one-off' tip for the given rect if any.
600 status_t _TipManagerView::hideTip(
601 const BRect& screenRect) {
603 ASSERT(Looper()->IsLocked());
605 // check for armed tip
606 if(m_armedTip) {
607 ASSERT(m_tipWindowState == TIP_WINDOW_ARMED);
608 if(m_armedTip->rect == screenRect) {
609 // cancel it
610 delete m_armedTip;
611 m_armedTip = 0;
612 m_tipWindowState = TIP_WINDOW_HIDDEN;
613 return B_OK;
617 // check for visible tip
618 if(m_tipWindowState != TIP_WINDOW_VISIBLE)
619 return B_BAD_VALUE;
621 if(m_visibleTipRect != screenRect)
622 return B_BAD_VALUE;
624 _hideTip();
625 m_tipWindowState = TIP_WINDOW_HIDDEN;
627 return B_OK;
630 status_t _TipManagerView::setTip(
631 const BRect& rect,
632 const char* text,
633 BView* view,
634 TipManager::offset_mode_t offsetMode,
635 BPoint offset,
636 uint32 flags) {
638 ASSERT(text);
639 ASSERT(view);
640 ASSERT(Looper()->IsLocked());
642 BWindow* window = view->Window();
643 if(!window)
644 return B_ERROR; // can't add non-attached views
646 // construct & add an entry
647 tip_entry e(rect, text, offsetMode, offset, flags);
649 for(
650 list<_WindowEntry*>::iterator it = m_windows.begin();
651 it != m_windows.end(); ++it) {
652 if((*it)->target() == window)
653 return (*it)->add(view, e);
656 // create new window entry
657 _WindowEntry* windowEntry = new _WindowEntry(window);
658 m_windows.push_back(windowEntry);
660 return windowEntry->add(view, e);
663 // [e.moon 27oct99]
664 // +++++ broken for 'remove all' mode (triggered by invalid rect):
665 // doesn't remove entries.
666 status_t _TipManagerView::removeTip(
667 const BRect& rect,
668 BView* view) {
670 ASSERT(view);
671 ASSERT(Looper()->IsLocked());
673 BWindow* window = view->Window();
674 if(!window) {
675 PRINT((
676 "!!! _TipManagerView::removeTip(): not attached !!!\n"));
677 return B_ERROR; // can't add non-attached views
680 // hand off to the entry for the containing window
681 for(
682 list<_WindowEntry*>::iterator it = m_windows.begin();
683 it != m_windows.end(); ++it) {
684 if((*it)->target() == window) {
686 // PRINT((
687 // "### _TipManagerView::removeTip(%.0f,%.0f - %.0f,%.0f)\n"
688 // " * BEFORE\n\n",
689 // rect.left, rect.top, rect.right, rect.bottom));
690 // (*it)->dump(1);
692 // remove
693 status_t ret = (*it)->remove(view, rect);
695 if(!(*it)->countViews()) {
697 // emptied window entry; remove it
698 delete *it;
699 m_windows.erase(it);
700 // PRINT((
701 // " (removed window entry)\n"));
703 // else {
704 // PRINT((
705 // " * AFTER\n\n"));
706 // (*it)->dump(1);
707 // }
708 return ret;
712 PRINT((
713 "!!! _TipManagerView::removeTip(): window entry not found!\n\n"));
714 return B_ERROR;
717 status_t _TipManagerView::removeAll(
718 BWindow* window) {
720 ASSERT(window);
721 ASSERT(Looper()->IsLocked());
723 // PRINT((
724 // "### _TipManagerView::removeAll()\n"));
726 for(
727 list<_WindowEntry*>::iterator it = m_windows.begin();
728 it != m_windows.end(); ++it) {
729 if((*it)->target() == window) {
730 delete *it;
731 m_windows.erase(it);
732 return B_OK;
736 PRINT((
737 "!!! _TipManagerView::removeAll(): window entry not found!\n"));
738 return B_ERROR;
741 // -------------------------------------------------------- //
742 // *** BView
743 // -------------------------------------------------------- //
745 void _TipManagerView::AttachedToWindow() {
747 // PRINT((
748 // "### _TipManagerView::AttachedToWindow()\n"));
750 // start the updates flowing
751 m_messageRunner = new BMessageRunner(
752 BMessenger(this),
753 new BMessage(M_TIME_PASSED),
754 m_updatePeriod);
757 void _TipManagerView::KeyDown(
758 const char* bytes,
759 int32 count) {
761 // no longer attached?
762 if(!Window())
763 return;
765 // hide the tip
766 if(m_tipWindowState == TIP_WINDOW_VISIBLE) {
767 _hideTip();
768 m_tipWindowState = TIP_WINDOW_HIDDEN;
771 m_lastEventTime = system_time();
774 void _TipManagerView::MouseDown(
775 BPoint point) {
777 // no longer attached?
778 if(!Window())
779 return;
781 // hide the tip
782 if(m_tipWindowState == TIP_WINDOW_VISIBLE) {
783 _hideTip();
784 m_tipWindowState = TIP_WINDOW_HIDDEN;
787 m_lastEventTime = system_time();
788 ConvertToScreen(&point);
789 m_lastMousePoint = point;
792 void _TipManagerView::MouseMoved(
793 BPoint point,
794 uint32 orientation,
795 const BMessage* dragMessage) {
797 // PRINT((
798 // "### _TipManagerView::MouseMoved()\n"));
800 // no longer attached?
801 if(!Window())
802 return;
804 ConvertToScreen(&point);
806 bool moved = (point != m_lastMousePoint);
808 if(m_tipWindowState == TIP_WINDOW_ARMED) {
809 ASSERT(m_armedTip);
810 if(moved && !m_armedTip->rect.Contains(point)) {
811 // mouse has moved outside the tip region,
812 // disarming this manually-armed tip.
813 m_tipWindowState = TIP_WINDOW_HIDDEN;
814 delete m_armedTip;
815 m_armedTip = 0;
818 else if(m_tipWindowState == TIP_WINDOW_VISIBLE) {
819 ASSERT(m_visibleTipRect.IsValid());
821 if(moved && !m_visibleTipRect.Contains(point)) {
822 // hide the tip
823 _hideTip();
824 m_tipWindowState = TIP_WINDOW_HIDDEN;
827 // don't reset timing state until the tip is closed
828 return;
831 // if the mouse has moved, reset timing state:
832 if(moved) {
833 m_lastMousePoint = point;
834 m_lastEventTime = system_time();
835 m_triggered = false;
839 // -------------------------------------------------------- //
840 // *** BHandler
841 // -------------------------------------------------------- //
843 void _TipManagerView::MessageReceived(
844 BMessage* message) {
845 switch(message->what) {
846 case M_TIME_PASSED:
847 _timePassed();
848 break;
850 default:
851 _inherited::MessageReceived(message);
855 // -------------------------------------------------------- //
856 // implementation
857 // -------------------------------------------------------- //
859 inline void _TipManagerView::_timePassed() {
861 // PRINT((
862 // "### _TipManagerView::_timePassed()\n"));
864 // no longer attached?
865 if(!Window())
866 return;
868 // has the mouse already triggered at this point?
869 if(m_triggered)
870 // yup; nothing more to do
871 return;
873 // see if the mouse has idled for long enough to trigger
874 bigtime_t now = system_time();
875 if(now - m_lastEventTime < m_idlePeriod)
876 // nope
877 return;
879 // trigger!
880 m_triggered = true;
882 if(m_tipWindowState == TIP_WINDOW_ARMED) {
883 // a tip has been manually set
884 ASSERT(m_armedTip);
885 m_visibleTipRect = m_armedTip->rect;
886 _showTip(m_armedTip);
887 m_tipWindowState = TIP_WINDOW_VISIBLE;
889 // clean up
890 delete m_armedTip;
891 m_armedTip = 0;
892 return;
895 // look for a tip under the current mouse point
896 for(
897 list<_WindowEntry*>::iterator it = m_windows.begin();
898 it != m_windows.end(); ++it) {
900 // lock the window
901 BWindow* window = (*it)->target();
902 ASSERT(window);
904 // [e.moon 21oct99] does autolock work in this context?
905 //BAutolock _l(window);
906 window->Lock();
908 // match
909 pair<BView*, const tip_entry*> found =
910 (*it)->match(m_lastMousePoint);
912 // if no tip found, or the view's no longer attached, bail:
913 if(!found.second || found.first->Window() != window) {
914 window->Unlock();
915 continue;
918 // found a tip under the mouse; see if it's obscured
919 // by another window
920 BRegion clipRegion;
921 found.first->GetClippingRegion(&clipRegion);
922 if(!clipRegion.Contains(
923 found.first->ConvertFromScreen(m_lastMousePoint))) {
924 // view hidden; don't show tip
925 window->Unlock();
926 continue;
929 // show the tip
930 if(found.second->rect.IsValid())
931 m_visibleTipRect = found.first->ConvertToScreen(
932 found.second->rect);
933 else
934 m_visibleTipRect = found.first->ConvertToScreen(
935 found.first->Bounds());
937 _showTip(found.second);
938 m_tipWindowState = TIP_WINDOW_VISIBLE;
940 window->Unlock();
941 break;
945 inline void _TipManagerView::_showTip(
946 const tip_entry* entry) {
948 // PRINT((
949 // "### _TipManagerView::_showTip()\n"));
951 ASSERT(m_tipWindow);
952 ASSERT(m_tipWindowState != TIP_WINDOW_VISIBLE);
953 ASSERT(entry);
955 BAutolock _l(m_tipWindow);
957 // set text
958 m_tipWindow->SetWorkspaces(B_ALL_WORKSPACES);
959 m_tipWindow->setText(entry->text.String());
961 // figure position
962 BPoint offset = (entry->offset == TipManager::s_useDefaultOffset) ?
963 TipManager::s_defaultOffset :
964 entry->offset;
966 BPoint p;
967 switch(entry->offsetMode) {
968 case TipManager::LEFT_OFFSET_FROM_RECT:
969 p = m_visibleTipRect.RightTop() + offset;
970 break;
971 case TipManager::LEFT_OFFSET_FROM_POINTER:
972 p = m_lastMousePoint + offset;
973 break;
974 case TipManager::RIGHT_OFFSET_FROM_RECT:
975 p = m_visibleTipRect.LeftTop();
976 p.x -= offset.x;
977 p.y += offset.y;
978 p.x -= m_tipWindow->Frame().Width();
979 break;
980 case TipManager::RIGHT_OFFSET_FROM_POINTER:
981 p = m_lastMousePoint;
982 p.x -= offset.x;
983 p.y += offset.y;
984 p.x -= m_tipWindow->Frame().Width();
985 break;
986 default:
987 ASSERT(!"bad offset mode");
990 // constrain window to be on-screen:
991 m_tipWindow->MoveTo(p);
993 BRect screenR = BScreen(m_tipWindow).Frame();
994 BRect tipR = m_tipWindow->Frame();
996 if(tipR.left < screenR.left)
997 tipR.left = screenR.left;
998 else if(tipR.right > screenR.right)
999 tipR.left = screenR.right - tipR.Width();
1001 if(tipR.top < screenR.top)
1002 tipR.top = screenR.top;
1003 else if(tipR.bottom > screenR.bottom)
1004 tipR.top = screenR.bottom - tipR.Height();
1006 if(tipR.LeftTop() != p)
1007 m_tipWindow->MoveTo(tipR.LeftTop());
1009 if(m_tipWindow->IsHidden())
1010 m_tipWindow->Show();
1013 inline void _TipManagerView::_hideTip() {
1014 // PRINT((
1015 // "### _TipManagerView::_hideTip()\n"));
1017 ASSERT(m_tipWindow);
1018 ASSERT(m_tipWindowState == TIP_WINDOW_VISIBLE);
1019 BAutolock _l(m_tipWindow);
1021 if(m_tipWindow->IsHidden())
1022 return;
1024 m_tipWindow->Hide();
1027 // END -- TipManagerImpl.cpp --