2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
37 #include "TipManager.h"
38 #include "TipManagerImpl.h"
39 #include "TipWindow.h"
43 #include <MessageRunner.h>
47 //#include "debug_tools.h"
51 __USE_CORTEX_NAMESPACE
53 // -------------------------------------------------------- //
55 // [e.moon 13oct99] now matches entry by pointer
56 class entry_target_matches_view
{ public:
58 entry_target_matches_view(const BView
* p
) : pView(p
) {}
59 bool operator()(const _ViewEntry
* view
) const {
60 return view
->target() == pView
;
64 // -------------------------------------------------------- //
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
) {
74 for(tip_entry_set::iterator it
= m_tips
.begin();
75 it
!= m_tips
.end(); ++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
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
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
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
=
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();
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
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()) {
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
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
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
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
=
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())
188 viewEntry
= *itEntry
;
191 // remove matching entries:
192 // [13oct99 e.moon] now ref'd by pointer; find and erase all matching tips
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
);
198 // found one; erase it
200 viewEntry
->m_tips
.erase(it
++);
204 // invalid rect == wildcard
207 // "### _ViewEntry::remove(): WILDCARD MODE\n"));
209 // [13oct99 e.moon] free all tip entries
211 tip_entry_set::iterator it
= viewEntry
->m_tips
.begin();
212 it
!= viewEntry
->m_tips
.end(); ++it
) {
215 viewEntry
->m_tips
.clear();
218 // "### - freed all tips\n"));
220 // [27oct99 e.moon] remove all child views
222 list
<_ViewEntry
*>::iterator itChild
= viewEntry
->m_childViews
.begin();
223 itChild
!= viewEntry
->m_childViews
.end(); ++itChild
) {
227 viewEntry
->m_childViews
.clear();
230 // "### - freed all child views\n"));
232 // remove the view entry if possible
233 if(viewEntry
->m_parent
) {
235 "### - removing view entry from %p\n",
236 viewEntry
->m_parent
));
238 list
<_ViewEntry
*>::iterator it
=
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
;
247 parent
->m_childViews
.erase(it
);
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
263 // check for a full-frame tip:
265 const tip_entry
* pFront
= fullFrameTip();
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
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
) {
282 const tip_entry
* entry
= *itCur
;
283 if(entry
->rect
.Contains(point
))
284 return pair
<BView
*, const tip_entry
*>(m_target
, entry
);
290 // recurse through children
291 for(list
<_ViewEntry
*>::iterator it
= m_childViews
.begin();
292 it
!= m_childViews
.end(); it
++) {
294 _ViewEntry
* entry
= *it
;
296 entry
->target()->ConvertFromParent(point
);
298 pair
<BView
*, const tip_entry
*> ret
= entry
->match(
307 return pair
<BView
*, const tip_entry
*>(0, 0);
310 // get frame rect (in parent view's coordinates)
311 BRect
_ViewEntry::Frame() {
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();
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 {
327 const tip_entry
* front
= *m_tips
.begin();
328 if(!front
->rect
.IsValid()) {
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();
347 void _ViewEntry::dump(int indent
) {
349 s
.SetTo('\t', indent
);
351 "%s_ViewEntry '%s'\n",
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 // -------------------------------------------------------- //
367 // -------------------------------------------------------- //
369 _WindowEntry::~_WindowEntry() {
370 for(list
<_ViewEntry
*>::iterator it
= m_views
.begin();
371 it
!= m_views
.end(); ++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(
385 const tip_entry
& entry
) {
388 if(view
->Window() != target())
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(
419 if(view
->Window() != target())
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
) {
433 status_t ret
= (*it
)->remove(view
, rect
);
435 if(!(*it
)->countTips()) {
436 // remove empty entry
445 "!!! _WindowEntry::remove(): no matching view\n"));
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
) {
463 "!!! _WindowEntry::match(): unexpected window for target view (%p)\n",
467 return pair
<BView
*,const tip_entry
*>(0,0);
469 pair
<BView
*,const tip_entry
*> ret
= (*it
)->match(
470 (*it
)->target()->ConvertFromScreen(screenPoint
),
476 return pair
<BView
*,const tip_entry
*>(0,0);
479 void _WindowEntry::dump(int indent
) {
481 s
.SetTo('\t', indent
);
483 "%s_WindowEntry '%s'\n",
487 for(list
<_ViewEntry
*>::iterator it
= m_views
.begin();
488 it
!= m_views
.end(); it
++) {
489 (*it
)->dump(indent
+ 1);
494 // -------------------------------------------------------- //
496 // -------------------------------------------------------- //
498 // -------------------------------------------------------- //
500 // -------------------------------------------------------- //
502 _TipManagerView::~_TipManagerView() {
503 for(list
<_WindowEntry
*>::iterator it
= m_windows
.begin();
504 it
!= m_windows
.end(); ++it
) {
508 delete m_messageRunner
;
510 // clean up the tip-display window
515 _TipManagerView::_TipManagerView(
516 TipWindow
* tipWindow
,
518 bigtime_t updatePeriod
,
519 bigtime_t idlePeriod
) :
525 m_tipWindow(tipWindow
),
528 m_tipWindowState(TIP_WINDOW_HIDDEN
),
529 m_updatePeriod(updatePeriod
),
530 m_idlePeriod(idlePeriod
),
531 m_lastEventTime(0LL),
538 // +++++ is this cheating?
541 // request to be sent all mouse & keyboard events
542 SetEventMask(B_POINTER_EVENTS
| B_KEYBOARD_EVENTS
);
545 SetViewColor(B_TRANSPARENT_COLOR
);
549 // -------------------------------------------------------- //
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
,
560 TipManager::offset_mode_t offsetMode
,
564 ASSERT(Looper()->IsLocked());
567 if(!screenRect
.IsValid())
570 // cancel previous manual tip operation
572 ASSERT(m_tipWindowState
== TIP_WINDOW_ARMED
);
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
);
585 // create new entry; enter 'armed' state
586 m_armedTip
= new tip_entry(
592 m_tipWindowState
= TIP_WINDOW_ARMED
;
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
607 ASSERT(m_tipWindowState
== TIP_WINDOW_ARMED
);
608 if(m_armedTip
->rect
== screenRect
) {
612 m_tipWindowState
= TIP_WINDOW_HIDDEN
;
617 // check for visible tip
618 if(m_tipWindowState
!= TIP_WINDOW_VISIBLE
)
621 if(m_visibleTipRect
!= screenRect
)
625 m_tipWindowState
= TIP_WINDOW_HIDDEN
;
630 status_t
_TipManagerView::setTip(
634 TipManager::offset_mode_t offsetMode
,
640 ASSERT(Looper()->IsLocked());
642 BWindow
* window
= view
->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
);
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
);
664 // +++++ broken for 'remove all' mode (triggered by invalid rect):
665 // doesn't remove entries.
666 status_t
_TipManagerView::removeTip(
671 ASSERT(Looper()->IsLocked());
673 BWindow
* window
= view
->Window();
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
682 list
<_WindowEntry
*>::iterator it
= m_windows
.begin();
683 it
!= m_windows
.end(); ++it
) {
684 if((*it
)->target() == window
) {
687 // "### _TipManagerView::removeTip(%.0f,%.0f - %.0f,%.0f)\n"
689 // rect.left, rect.top, rect.right, rect.bottom));
693 status_t ret
= (*it
)->remove(view
, rect
);
695 if(!(*it
)->countViews()) {
697 // emptied window entry; remove it
701 // " (removed window entry)\n"));
713 "!!! _TipManagerView::removeTip(): window entry not found!\n\n"));
717 status_t
_TipManagerView::removeAll(
721 ASSERT(Looper()->IsLocked());
724 // "### _TipManagerView::removeAll()\n"));
727 list
<_WindowEntry
*>::iterator it
= m_windows
.begin();
728 it
!= m_windows
.end(); ++it
) {
729 if((*it
)->target() == window
) {
737 "!!! _TipManagerView::removeAll(): window entry not found!\n"));
741 // -------------------------------------------------------- //
743 // -------------------------------------------------------- //
745 void _TipManagerView::AttachedToWindow() {
748 // "### _TipManagerView::AttachedToWindow()\n"));
750 // start the updates flowing
751 m_messageRunner
= new BMessageRunner(
753 new BMessage(M_TIME_PASSED
),
757 void _TipManagerView::KeyDown(
761 // no longer attached?
766 if(m_tipWindowState
== TIP_WINDOW_VISIBLE
) {
768 m_tipWindowState
= TIP_WINDOW_HIDDEN
;
771 m_lastEventTime
= system_time();
774 void _TipManagerView::MouseDown(
777 // no longer attached?
782 if(m_tipWindowState
== TIP_WINDOW_VISIBLE
) {
784 m_tipWindowState
= TIP_WINDOW_HIDDEN
;
787 m_lastEventTime
= system_time();
788 ConvertToScreen(&point
);
789 m_lastMousePoint
= point
;
792 void _TipManagerView::MouseMoved(
795 const BMessage
* dragMessage
) {
798 // "### _TipManagerView::MouseMoved()\n"));
800 // no longer attached?
804 ConvertToScreen(&point
);
806 bool moved
= (point
!= m_lastMousePoint
);
808 if(m_tipWindowState
== TIP_WINDOW_ARMED
) {
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
;
818 else if(m_tipWindowState
== TIP_WINDOW_VISIBLE
) {
819 ASSERT(m_visibleTipRect
.IsValid());
821 if(moved
&& !m_visibleTipRect
.Contains(point
)) {
824 m_tipWindowState
= TIP_WINDOW_HIDDEN
;
827 // don't reset timing state until the tip is closed
831 // if the mouse has moved, reset timing state:
833 m_lastMousePoint
= point
;
834 m_lastEventTime
= system_time();
839 // -------------------------------------------------------- //
841 // -------------------------------------------------------- //
843 void _TipManagerView::MessageReceived(
845 switch(message
->what
) {
851 _inherited::MessageReceived(message
);
855 // -------------------------------------------------------- //
857 // -------------------------------------------------------- //
859 inline void _TipManagerView::_timePassed() {
862 // "### _TipManagerView::_timePassed()\n"));
864 // no longer attached?
868 // has the mouse already triggered at this point?
870 // yup; nothing more to do
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
)
882 if(m_tipWindowState
== TIP_WINDOW_ARMED
) {
883 // a tip has been manually set
885 m_visibleTipRect
= m_armedTip
->rect
;
886 _showTip(m_armedTip
);
887 m_tipWindowState
= TIP_WINDOW_VISIBLE
;
895 // look for a tip under the current mouse point
897 list
<_WindowEntry
*>::iterator it
= m_windows
.begin();
898 it
!= m_windows
.end(); ++it
) {
901 BWindow
* window
= (*it
)->target();
904 // [e.moon 21oct99] does autolock work in this context?
905 //BAutolock _l(window);
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
) {
918 // found a tip under the mouse; see if it's obscured
921 found
.first
->GetClippingRegion(&clipRegion
);
922 if(!clipRegion
.Contains(
923 found
.first
->ConvertFromScreen(m_lastMousePoint
))) {
924 // view hidden; don't show tip
930 if(found
.second
->rect
.IsValid())
931 m_visibleTipRect
= found
.first
->ConvertToScreen(
934 m_visibleTipRect
= found
.first
->ConvertToScreen(
935 found
.first
->Bounds());
937 _showTip(found
.second
);
938 m_tipWindowState
= TIP_WINDOW_VISIBLE
;
945 inline void _TipManagerView::_showTip(
946 const tip_entry
* entry
) {
949 // "### _TipManagerView::_showTip()\n"));
952 ASSERT(m_tipWindowState
!= TIP_WINDOW_VISIBLE
);
955 BAutolock
_l(m_tipWindow
);
958 m_tipWindow
->SetWorkspaces(B_ALL_WORKSPACES
);
959 m_tipWindow
->setText(entry
->text
.String());
962 BPoint offset
= (entry
->offset
== TipManager::s_useDefaultOffset
) ?
963 TipManager::s_defaultOffset
:
967 switch(entry
->offsetMode
) {
968 case TipManager::LEFT_OFFSET_FROM_RECT
:
969 p
= m_visibleTipRect
.RightTop() + offset
;
971 case TipManager::LEFT_OFFSET_FROM_POINTER
:
972 p
= m_lastMousePoint
+ offset
;
974 case TipManager::RIGHT_OFFSET_FROM_RECT
:
975 p
= m_visibleTipRect
.LeftTop();
978 p
.x
-= m_tipWindow
->Frame().Width();
980 case TipManager::RIGHT_OFFSET_FROM_POINTER
:
981 p
= m_lastMousePoint
;
984 p
.x
-= m_tipWindow
->Frame().Width();
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() {
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())
1024 m_tipWindow
->Hide();
1027 // END -- TipManagerImpl.cpp --