1 // TipManagerImpl.cpp 2 // e.moon 13may99 3 4 #include <algorithm> 5 6 #include "TipManager.h" 7 #include "TipManagerImpl.h" 8 #include "TipWindow.h" 9 10 #include <Autolock.h> 11 #include <Debug.h> 12 #include <MessageRunner.h> 13 #include <Region.h> 14 #include <Screen.h> 15 16 //#include "debug_tools.h" 17 18 using namespace std; 19 20 __USE_CORTEX_NAMESPACE 21 22 // -------------------------------------------------------- // 23 24 // [e.moon 13oct99] now matches entry by pointer 25 class entry_target_matches_view { public: 26 const BView* pView; 27 entry_target_matches_view(const BView* p) : pView(p) {} 28 bool operator()(const _ViewEntry* view) const { 29 return view->target() == pView; 30 } 31 }; 32 33 // -------------------------------------------------------- // 34 // _ViewEntry impl. 35 // -------------------------------------------------------- // 36 37 // [e.moon 13oct99] delete tips & children 38 _ViewEntry::~_ViewEntry() { 39 for(list<_ViewEntry*>::iterator it = m_childViews.begin(); 40 it != m_childViews.end(); ++it) { 41 delete *it; 42 } 43 for(tip_entry_set::iterator it = m_tips.begin(); 44 it != m_tips.end(); ++it) { 45 delete *it; 46 } 47 } 48 49 // add the given entry for the designated view 50 // (which may be the target view or a child.) 51 // returns B_OK on success, B_ERROR if the given view is 52 // NOT a child . 53 54 status_t _ViewEntry::add(BView* pView, const tip_entry& tipEntry) { 55 56 // walk up the view's parent tree, building a child- 57 // hierarchy list and looking for my target view. 58 // The list should be in descending order: the last 59 // entry is pView. 60 // +++++ move to separate method 61 list<BView*> parentOrder; 62 BView* pCurView = pView; 63 while(pCurView && pCurView != m_target) { 64 parentOrder.push_front(pCurView); 65 pCurView = pCurView->Parent(); 66 } 67 if(pCurView != m_target) 68 return B_ERROR; // +++++ ever so descriptive 69 70 // walk down the child hierarchy, making ViewEntries as 71 // needed 72 _ViewEntry* viewEntry = this; 73 74 // [e.moon 13oct99] clone tipEntry 75 tip_entry* newTipEntry = new tip_entry(tipEntry); 76 77 for(list<BView*>::iterator itView = parentOrder.begin(); 78 itView != parentOrder.end(); itView++) { 79 80 // look for this view in children of the current entry 81 list<_ViewEntry*>::iterator itEntry = 82 find_if( 83 viewEntry->m_childViews.begin(), 84 viewEntry->m_childViews.end(), 85 entry_target_matches_view(*itView)); 86 87 // add new _ViewEntry if necessary 88 if(itEntry == viewEntry->m_childViews.end()) { 89 viewEntry->m_childViews.push_back(new _ViewEntry(*itView, viewEntry)); 90 viewEntry = viewEntry->m_childViews.back(); 91 } else 92 viewEntry = *itEntry; 93 } 94 95 // found a home; can it hold the tip? 96 if(viewEntry->m_tips.size() && 97 !(*viewEntry->m_tips.begin())->rect.IsValid()) { 98 // [e.moon 13oct99] clean up 99 delete newTipEntry; 100 return B_ERROR; // +++++ error: full-view tip leaves no room 101 } 102 103 // remove matching tip if any, then add the new one: 104 // [e.moon 13oct99] ref'd by pointer 105 tip_entry_set::iterator itFound = viewEntry->m_tips.find(newTipEntry); 106 if(itFound != viewEntry->m_tips.end()) { 107 viewEntry->m_tips.erase(itFound); 108 delete *itFound; 109 } 110 111 pair<tip_entry_set::iterator, bool> ret; 112 ret = viewEntry->m_tips.insert(newTipEntry); 113 114 // something's terribly wrong if insert() failed 115 ASSERT(ret.second); 116 117 return B_OK; 118 } 119 120 // remove tip matching the given rect's upper-left corner or 121 // all tips if rect is invalid. 122 // returns B_OK on success, B_ERROR on failure 123 status_t _ViewEntry::remove( 124 BView* pView, const BRect& rect) { 125 126 // walk up the view's parent tree, building a child- 127 // hierarchy list and looking for my target view. 128 // The list should be in descending order: the last 129 // entry is pView. 130 // +++++ move to separate method 131 list<BView*> parentOrder; 132 BView* pCurView = pView; 133 while(pCurView && pCurView != m_target) { 134 parentOrder.push_front(pCurView); 135 pCurView = pCurView->Parent(); 136 } 137 if(pCurView != m_target) 138 return B_ERROR; // +++++ ever so descriptive 139 140 // walk down the child tree to the entry for the 141 // target view 142 _ViewEntry* viewEntry = this; 143 for(list<BView*>::iterator itView = parentOrder.begin(); 144 itView != parentOrder.end(); itView++) { 145 146 // look for this view in children of the current entry 147 list<_ViewEntry*>::iterator itEntry = 148 find_if( 149 viewEntry->m_childViews.begin(), 150 viewEntry->m_childViews.end(), 151 entry_target_matches_view(*itView)); 152 153 // it'd better be there! 154 if(itEntry == viewEntry->m_childViews.end()) 155 return B_ERROR; 156 157 viewEntry = *itEntry; 158 } 159 160 // remove matching entries: 161 // [13oct99 e.moon] now ref'd by pointer; find and erase all matching tips 162 if(rect.IsValid()) { 163 tip_entry matchEntry(rect); 164 tip_entry_set::iterator it = viewEntry->m_tips.lower_bound(&matchEntry); 165 tip_entry_set::iterator itEnd = viewEntry->m_tips.upper_bound(&matchEntry); 166 for(; it != itEnd; ++it) { 167 // found one; erase it 168 delete *it; 169 viewEntry->m_tips.erase(it); 170 } 171 } 172 else { 173 // invalid rect == wildcard 174 175 // PRINT(( 176 // "### _ViewEntry::remove(): WILDCARD MODE\n")); 177 178 // [13oct99 e.moon] free all tip entries 179 for( 180 tip_entry_set::iterator it = viewEntry->m_tips.begin(); 181 it != viewEntry->m_tips.end(); ++it) { 182 delete *it; 183 } 184 viewEntry->m_tips.clear(); 185 186 // PRINT(( 187 // "### - freed all tips\n")); 188 189 // [27oct99 e.moon] remove all child views 190 for( 191 list<_ViewEntry*>::iterator itChild = viewEntry->m_childViews.begin(); 192 itChild != viewEntry->m_childViews.end(); ++itChild) { 193 194 delete *itChild; 195 } 196 viewEntry->m_childViews.clear(); 197 198 // PRINT(( 199 // "### - freed all child views\n")); 200 201 // remove the view entry if possible 202 if(viewEntry->m_parent) { 203 PRINT(( 204 "### - removing view entry from %p\n", 205 viewEntry->m_parent)); 206 207 list<_ViewEntry*>::iterator it = 208 find_if( 209 viewEntry->m_parent->m_childViews.begin(), 210 viewEntry->m_parent->m_childViews.end(), 211 entry_target_matches_view(pView)); 212 ASSERT(it != viewEntry->m_parent->m_childViews.end()); 213 214 _ViewEntry* parent = viewEntry->m_parent; 215 delete viewEntry; 216 parent->m_childViews.erase(it); 217 } 218 } 219 220 return B_OK; 221 } 222 223 // match the given point (in target's view coordinates) 224 // against tips in this view and child views. recurse. 225 226 pair<BView*, const tip_entry*> _ViewEntry::match( 227 BPoint point, BPoint screenPoint) { 228 229 // fetch this view's current frame rect 230 BRect f = Frame(); 231 232 // check for a full-frame tip: 233 234 const tip_entry* pFront = fullFrameTip(); 235 if(pFront) { 236 // match, and stop recursing here; children can't have tips. 237 m_target->ConvertFromParent(&f); 238 return make_pair(m_target, f.Contains(point) ? pFront : 0); 239 } 240 241 // match against tips for my target view 242 if(m_tips.size()) { 243 244 tip_entry matchEntry(BRect(point, point)); 245 tip_entry_set::iterator itCur = m_tips.lower_bound(&matchEntry); 246 // tip_entry_set::iterator itCur = m_tips.begin(); 247 tip_entry_set::iterator itEnd = m_tips.end(); 248 249 while(itCur != itEnd) { 250 // match: 251 const tip_entry* entry = *itCur; 252 if(entry->rect.Contains(point)) 253 return pair<BView*, const tip_entry*>(m_target, entry); 254 255 ++itCur; 256 } 257 } 258 259 // recurse through children 260 for(list<_ViewEntry*>::iterator it = m_childViews.begin(); 261 it != m_childViews.end(); it++) { 262 263 _ViewEntry* entry = *it; 264 BPoint childPoint = 265 entry->target()->ConvertFromParent(point); 266 267 pair<BView*, const tip_entry*> ret = entry->match( 268 childPoint, 269 screenPoint); 270 271 if(ret.second) 272 return ret; 273 } 274 275 // nothing found 276 return pair<BView*, const tip_entry*>(0, 0); 277 } 278 279 // get frame rect (in parent view's coordinates) 280 BRect _ViewEntry::Frame() { 281 ASSERT(m_target); 282 // ASSERT(m_target->Parent()); 283 284 // +++++ if caching or some weird proxy mechanism 285 // works out, return a cached BRect here 286 // rather than asking every view every time! 287 288 BRect f = m_target->Frame(); 289 return f; 290 } 291 292 // returns pointer to sole entry in the set if it's 293 // a full-frame tip, or 0 if there's no full-frame tip 294 const tip_entry* _ViewEntry::fullFrameTip() const { 295 if(m_tips.size()) { 296 const tip_entry* front = *m_tips.begin(); 297 if(!front->rect.IsValid()) { 298 return front; 299 } 300 } 301 return 0; 302 } 303 304 size_t _ViewEntry::countTips() const { 305 306 size_t tips = m_tips.size(); 307 for(list<_ViewEntry*>::const_iterator it = m_childViews.begin(); 308 it != m_childViews.end(); it++) { 309 tips += (*it)->countTips(); 310 } 311 312 return tips; 313 } 314 315 316 void _ViewEntry::dump(int indent) { 317 BString s; 318 s.SetTo('\t', indent); 319 PRINT(( 320 "%s_ViewEntry '%s'\n", 321 s.String(), 322 m_target->Name())); 323 324 for(tip_entry_set::iterator it = m_tips.begin(); 325 it != m_tips.end(); ++it) { 326 (*it)->dump(indent + 1); 327 } 328 for(list<_ViewEntry*>::iterator it = m_childViews.begin(); 329 it != m_childViews.end(); it++) { 330 (*it)->dump(indent + 1); 331 } 332 } 333 334 // -------------------------------------------------------- // 335 // _WindowEntry impl 336 // -------------------------------------------------------- // 337 338 _WindowEntry::~_WindowEntry() { 339 for(list<_ViewEntry*>::iterator it = m_views.begin(); 340 it != m_views.end(); ++it) { 341 delete *it; 342 } 343 } 344 345 // add the given entry for the designated view (which must 346 // be attached to the target window) 347 // returns B_OK on success, B_ERROR if: 348 // - the given view is NOT attached to the target window, or 349 // - tips can't be added to this view due to it, or one of its 350 // parents, having a full-frame tip. 351 352 status_t _WindowEntry::add( 353 BView* view, 354 const tip_entry& entry) { 355 356 ASSERT(view); 357 if(view->Window() != target()) 358 return B_ERROR; 359 360 // find top-level view 361 BView* parent = view; 362 while(parent && parent->Parent()) 363 parent = parent->Parent(); 364 365 // look for a _ViewEntry matching the parent & hand off 366 for(list<_ViewEntry*>::iterator it = m_views.begin(); 367 it != m_views.end(); ++it) 368 if((*it)->target() == parent) 369 return (*it)->add(view, entry); 370 371 // create _ViewEntry for the parent & hand off 372 _ViewEntry* v = new _ViewEntry(parent, 0); 373 m_views.push_back(v); 374 375 return v->add(view, entry); 376 } 377 378 // remove tip matching the given rect's upper-left corner or 379 // all tips if rect is invalid. 380 // returns B_ERROR on failure -- if there are no entries for 381 // the given view -- or B_OK otherwise. 382 383 status_t _WindowEntry::remove( 384 BView* view, 385 const BRect& rect) { 386 387 ASSERT(view); 388 if(view->Window() != target()) 389 return B_ERROR; 390 391 // find top-level view 392 BView* parent = view; 393 while(parent && parent->Parent()) 394 parent = parent->Parent(); 395 396 // look for a matching _ViewEntry & hand off 397 for(list<_ViewEntry*>::iterator it = m_views.begin(); 398 it != m_views.end(); ++it) 399 if((*it)->target() == parent) { 400 401 // do it 402 status_t ret = (*it)->remove(view, rect); 403 404 if(!(*it)->countTips()) { 405 // remove empty entry 406 delete *it; 407 m_views.erase(it); 408 } 409 return ret; 410 } 411 412 // not found 413 PRINT(( 414 "!!! _WindowEntry::remove(): no matching view\n")); 415 return B_ERROR; 416 } 417 418 // match the given point (in screen coordinates) 419 // against tips in this window's views. 420 421 pair<BView*, const tip_entry*> _WindowEntry::match( 422 BPoint screenPoint) { 423 424 for(list<_ViewEntry*>::iterator it = m_views.begin(); 425 it != m_views.end(); ++it) { 426 427 // +++++ failing on invalid views? [e.moon 27oct99] 428 429 BView* target = (*it)->target(); 430 if(target->Window() != m_target) { 431 PRINT(( 432 "!!! _WindowEntry::match(): unexpected window for target view (%p)\n", 433 target)); 434 435 // skip it 436 return pair<BView*,const tip_entry*>(0,0); 437 } 438 pair<BView*,const tip_entry*> ret = (*it)->match( 439 (*it)->target()->ConvertFromScreen(screenPoint), 440 screenPoint); 441 if(ret.second) 442 return ret; 443 } 444 445 return pair<BView*,const tip_entry*>(0,0); 446 } 447 448 void _WindowEntry::dump(int indent) { 449 BString s; 450 s.SetTo('\t', indent); 451 PRINT(( 452 "%s_WindowEntry '%s'\n", 453 s.String(), 454 m_target->Name())); 455 456 for(list<_ViewEntry*>::iterator it = m_views.begin(); 457 it != m_views.end(); it++) { 458 (*it)->dump(indent + 1); 459 } 460 } 461 462 463 // -------------------------------------------------------- // 464 // _TipManagerView 465 // -------------------------------------------------------- // 466 467 // -------------------------------------------------------- // 468 // *** ctor/dtor 469 // -------------------------------------------------------- // 470 471 _TipManagerView::~_TipManagerView() { 472 for(list<_WindowEntry*>::iterator it = m_windows.begin(); 473 it != m_windows.end(); ++it) { 474 delete *it; 475 } 476 if(m_messageRunner) 477 delete m_messageRunner; 478 479 // clean up the tip-display window 480 m_tipWindow->Lock(); 481 m_tipWindow->Quit(); 482 } 483 484 _TipManagerView::_TipManagerView( 485 TipWindow* tipWindow, 486 TipManager* manager, 487 bigtime_t updatePeriod, 488 bigtime_t idlePeriod) : 489 BView( 490 BRect(0,0,0,0), 491 "_TipManagerView", 492 B_FOLLOW_NONE, 493 B_PULSE_NEEDED), 494 m_tipWindow(tipWindow), 495 m_manager(manager), 496 m_messageRunner(0), 497 m_tipWindowState(TIP_WINDOW_HIDDEN), 498 m_updatePeriod(updatePeriod), 499 m_idlePeriod(idlePeriod), 500 m_lastEventTime(0LL), 501 m_triggered(false), 502 m_armedTip(0) { 503 504 ASSERT(m_tipWindow); 505 ASSERT(m_manager); 506 507 // +++++ is this cheating? 508 m_tipWindow->Run(); 509 510 // request to be sent all mouse & keyboard events 511 SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS); 512 513 // don't draw 514 SetViewColor(B_TRANSPARENT_COLOR); 515 } 516 517 518 // -------------------------------------------------------- // 519 // *** operations 520 // -------------------------------------------------------- // 521 522 // Prepare a 'one-off' tip: this interrupts normal mouse-watching 523 // behavior while the mouse remains in the given screen rectangle. 524 // If it idles long enough, the tip window is displayed. 525 526 status_t _TipManagerView::armTip( 527 const BRect& screenRect, 528 const char* text, 529 TipManager::offset_mode_t offsetMode, 530 BPoint offset, 531 uint32 flags) { 532 533 ASSERT(Looper()->IsLocked()); 534 535 ASSERT(text); 536 if(!screenRect.IsValid()) 537 return B_BAD_VALUE; 538 539 // cancel previous manual tip operation 540 if(m_armedTip) { 541 ASSERT(m_tipWindowState == TIP_WINDOW_ARMED); 542 delete m_armedTip; 543 m_armedTip = 0; 544 } 545 546 // is a tip with the same screen rect visible? update it: 547 if(m_tipWindowState == TIP_WINDOW_VISIBLE && 548 m_visibleTipRect == screenRect) { 549 BAutolock _l(m_tipWindow); 550 m_tipWindow->setText(text); 551 return B_OK; 552 } 553 554 // create new entry; enter 'armed' state 555 m_armedTip = new tip_entry( 556 screenRect, 557 text, 558 offsetMode, 559 offset, 560 flags); 561 m_tipWindowState = TIP_WINDOW_ARMED; 562 563 return B_OK; 564 } 565 566 // Hide tip corresponding to the given screenRect, if any. 567 // [e.moon 29nov99] Cancel 'one-off' tip for the given rect if any. 568 569 status_t _TipManagerView::hideTip( 570 const BRect& screenRect) { 571 572 ASSERT(Looper()->IsLocked()); 573 574 // check for armed tip 575 if(m_armedTip) { 576 ASSERT(m_tipWindowState == TIP_WINDOW_ARMED); 577 if(m_armedTip->rect == screenRect) { 578 // cancel it 579 delete m_armedTip; 580 m_armedTip = 0; 581 m_tipWindowState = TIP_WINDOW_HIDDEN; 582 return B_OK; 583 } 584 } 585 586 // check for visible tip 587 if(m_tipWindowState != TIP_WINDOW_VISIBLE) 588 return B_BAD_VALUE; 589 590 if(m_visibleTipRect != screenRect) 591 return B_BAD_VALUE; 592 593 _hideTip(); 594 m_tipWindowState = TIP_WINDOW_HIDDEN; 595 596 return B_OK; 597 } 598 599 status_t _TipManagerView::setTip( 600 const BRect& rect, 601 const char* text, 602 BView* view, 603 TipManager::offset_mode_t offsetMode, 604 BPoint offset, 605 uint32 flags) { 606 607 ASSERT(text); 608 ASSERT(view); 609 ASSERT(Looper()->IsLocked()); 610 611 BWindow* window = view->Window(); 612 if(!window) 613 return B_ERROR; // can't add non-attached views 614 615 // construct & add an entry 616 tip_entry e(rect, text, offsetMode, offset, flags); 617 618 for( 619 list<_WindowEntry*>::iterator it = m_windows.begin(); 620 it != m_windows.end(); ++it) { 621 if((*it)->target() == window) 622 return (*it)->add(view, e); 623 } 624 625 // create new window entry 626 _WindowEntry* windowEntry = new _WindowEntry(window); 627 m_windows.push_back(windowEntry); 628 629 return windowEntry->add(view, e); 630 } 631 632 // [e.moon 27oct99] 633 // +++++ broken for 'remove all' mode (triggered by invalid rect): 634 // doesn't remove entries. 635 status_t _TipManagerView::removeTip( 636 const BRect& rect, 637 BView* view) { 638 639 ASSERT(view); 640 ASSERT(Looper()->IsLocked()); 641 642 BWindow* window = view->Window(); 643 if(!window) { 644 PRINT(( 645 "!!! _TipManagerView::removeTip(): not attached !!!\n")); 646 return B_ERROR; // can't add non-attached views 647 } 648 649 // hand off to the entry for the containing window 650 for( 651 list<_WindowEntry*>::iterator it = m_windows.begin(); 652 it != m_windows.end(); ++it) { 653 if((*it)->target() == window) { 654 655 // PRINT(( 656 // "### _TipManagerView::removeTip(%.0f,%.0f - %.0f,%.0f)\n" 657 // " * BEFORE\n\n", 658 // rect.left, rect.top, rect.right, rect.bottom)); 659 // (*it)->dump(1); 660 661 // remove 662 status_t ret = (*it)->remove(view, rect); 663 664 if(!(*it)->countViews()) { 665 666 // emptied window entry; remove it 667 delete *it; 668 m_windows.erase(it); 669 // PRINT(( 670 // " (removed window entry)\n")); 671 } 672 // else { 673 // PRINT(( 674 // " * AFTER\n\n")); 675 // (*it)->dump(1); 676 // } 677 return ret; 678 } 679 } 680 681 PRINT(( 682 "!!! _TipManagerView::removeTip(): window entry not found!\n\n")); 683 return B_ERROR; 684 } 685 686 status_t _TipManagerView::removeAll( 687 BWindow* window) { 688 689 ASSERT(window); 690 ASSERT(Looper()->IsLocked()); 691 692 // PRINT(( 693 // "### _TipManagerView::removeAll()\n")); 694 695 for( 696 list<_WindowEntry*>::iterator it = m_windows.begin(); 697 it != m_windows.end(); ++it) { 698 if((*it)->target() == window) { 699 delete *it; 700 m_windows.erase(it); 701 return B_OK; 702 } 703 } 704 705 PRINT(( 706 "!!! _TipManagerView::removeAll(): window entry not found!\n")); 707 return B_ERROR; 708 } 709 710 // -------------------------------------------------------- // 711 // *** BView 712 // -------------------------------------------------------- // 713 714 void _TipManagerView::AttachedToWindow() { 715 716 // PRINT(( 717 // "### _TipManagerView::AttachedToWindow()\n")); 718 719 // start the updates flowing 720 m_messageRunner = new BMessageRunner( 721 BMessenger(this), 722 new BMessage(M_TIME_PASSED), 723 m_updatePeriod); 724 } 725 726 void _TipManagerView::KeyDown( 727 const char* bytes, 728 int32 count) { 729 730 // no longer attached? 731 if(!Window()) 732 return; 733 734 // hide the tip 735 if(m_tipWindowState == TIP_WINDOW_VISIBLE) { 736 _hideTip(); 737 m_tipWindowState = TIP_WINDOW_HIDDEN; 738 } 739 740 m_lastEventTime = system_time(); 741 } 742 743 void _TipManagerView::MouseDown( 744 BPoint point) { 745 746 // no longer attached? 747 if(!Window()) 748 return; 749 750 // hide the tip 751 if(m_tipWindowState == TIP_WINDOW_VISIBLE) { 752 _hideTip(); 753 m_tipWindowState = TIP_WINDOW_HIDDEN; 754 } 755 756 m_lastEventTime = system_time(); 757 ConvertToScreen(&point); 758 m_lastMousePoint = point; 759 } 760 761 void _TipManagerView::MouseMoved( 762 BPoint point, 763 uint32 orientation, 764 const BMessage* dragMessage) { 765 766 // PRINT(( 767 // "### _TipManagerView::MouseMoved()\n")); 768 769 // no longer attached? 770 if(!Window()) 771 return; 772 773 ConvertToScreen(&point); 774 775 bool moved = (point != m_lastMousePoint); 776 777 if(m_tipWindowState == TIP_WINDOW_ARMED) { 778 ASSERT(m_armedTip); 779 if(moved && !m_armedTip->rect.Contains(point)) { 780 // mouse has moved outside the tip region, 781 // disarming this manually-armed tip. 782 m_tipWindowState = TIP_WINDOW_HIDDEN; 783 delete m_armedTip; 784 m_armedTip = 0; 785 } 786 } 787 else if(m_tipWindowState == TIP_WINDOW_VISIBLE) { 788 ASSERT(m_visibleTipRect.IsValid()); 789 790 if(moved && !m_visibleTipRect.Contains(point)) { 791 // hide the tip 792 _hideTip(); 793 m_tipWindowState = TIP_WINDOW_HIDDEN; 794 } 795 796 // don't reset timing state until the tip is closed 797 return; 798 } 799 800 // if the mouse has moved, reset timing state: 801 if(moved) { 802 m_lastMousePoint = point; 803 m_lastEventTime = system_time(); 804 m_triggered = false; 805 } 806 } 807 808 // -------------------------------------------------------- // 809 // *** BHandler 810 // -------------------------------------------------------- // 811 812 void _TipManagerView::MessageReceived( 813 BMessage* message) { 814 switch(message->what) { 815 case M_TIME_PASSED: 816 _timePassed(); 817 break; 818 819 default: 820 _inherited::MessageReceived(message); 821 } 822 } 823 824 // -------------------------------------------------------- // 825 // implementation 826 // -------------------------------------------------------- // 827 828 inline void _TipManagerView::_timePassed() { 829 830 // PRINT(( 831 // "### _TipManagerView::_timePassed()\n")); 832 833 // no longer attached? 834 if(!Window()) 835 return; 836 837 // has the mouse already triggered at this point? 838 if(m_triggered) 839 // yup; nothing more to do 840 return; 841 842 // see if the mouse has idled for long enough to trigger 843 bigtime_t now = system_time(); 844 if(now - m_lastEventTime < m_idlePeriod) 845 // nope 846 return; 847 848 // trigger! 849 m_triggered = true; 850 851 if(m_tipWindowState == TIP_WINDOW_ARMED) { 852 // a tip has been manually set 853 ASSERT(m_armedTip); 854 m_visibleTipRect = m_armedTip->rect; 855 _showTip(m_armedTip); 856 m_tipWindowState = TIP_WINDOW_VISIBLE; 857 858 // clean up 859 delete m_armedTip; 860 m_armedTip = 0; 861 return; 862 } 863 864 // look for a tip under the current mouse point 865 for( 866 list<_WindowEntry*>::iterator it = m_windows.begin(); 867 it != m_windows.end(); ++it) { 868 869 // lock the window 870 BWindow* window = (*it)->target(); 871 ASSERT(window); 872 873 // [e.moon 21oct99] does autolock work in this context? 874 //BAutolock _l(window); 875 window->Lock(); 876 877 // match 878 pair<BView*, const tip_entry*> found = 879 (*it)->match(m_lastMousePoint); 880 881 // if no tip found, or the view's no longer attached, bail: 882 if(!found.second || found.first->Window() != window) { 883 window->Unlock(); 884 continue; 885 } 886 887 // found a tip under the mouse; see if it's obscured 888 // by another window 889 BRegion clipRegion; 890 found.first->GetClippingRegion(&clipRegion); 891 if(!clipRegion.Contains( 892 found.first->ConvertFromScreen(m_lastMousePoint))) { 893 // view hidden; don't show tip 894 window->Unlock(); 895 continue; 896 } 897 898 // show the tip 899 if(found.second->rect.IsValid()) 900 m_visibleTipRect = found.first->ConvertToScreen( 901 found.second->rect); 902 else 903 m_visibleTipRect = found.first->ConvertToScreen( 904 found.first->Bounds()); 905 906 _showTip(found.second); 907 m_tipWindowState = TIP_WINDOW_VISIBLE; 908 909 window->Unlock(); 910 break; 911 } 912 } 913 914 inline void _TipManagerView::_showTip( 915 const tip_entry* entry) { 916 917 // PRINT(( 918 // "### _TipManagerView::_showTip()\n")); 919 920 ASSERT(m_tipWindow); 921 ASSERT(m_tipWindowState != TIP_WINDOW_VISIBLE); 922 ASSERT(entry); 923 924 BAutolock _l(m_tipWindow); 925 926 // set text 927 m_tipWindow->SetWorkspaces(B_ALL_WORKSPACES); 928 m_tipWindow->setText(entry->text.String()); 929 930 // figure position 931 BPoint offset = (entry->offset == TipManager::s_useDefaultOffset) ? 932 TipManager::s_defaultOffset : 933 entry->offset; 934 935 BPoint p; 936 switch(entry->offsetMode) { 937 case TipManager::LEFT_OFFSET_FROM_RECT: 938 p = m_visibleTipRect.RightTop() + offset; 939 break; 940 case TipManager::LEFT_OFFSET_FROM_POINTER: 941 p = m_lastMousePoint + offset; 942 break; 943 case TipManager::RIGHT_OFFSET_FROM_RECT: 944 p = m_visibleTipRect.LeftTop(); 945 p.x -= offset.x; 946 p.y += offset.y; 947 p.x -= m_tipWindow->Frame().Width(); 948 break; 949 case TipManager::RIGHT_OFFSET_FROM_POINTER: 950 p = m_lastMousePoint; 951 p.x -= offset.x; 952 p.y += offset.y; 953 p.x -= m_tipWindow->Frame().Width(); 954 break; 955 default: 956 ASSERT(!"bad offset mode"); 957 } 958 959 // constrain window to be on-screen: 960 m_tipWindow->MoveTo(p); 961 962 BRect screenR = BScreen(m_tipWindow).Frame(); 963 BRect tipR = m_tipWindow->Frame(); 964 965 if(tipR.left < screenR.left) 966 tipR.left = screenR.left; 967 else if(tipR.right > screenR.right) 968 tipR.left = screenR.right - tipR.Width(); 969 970 if(tipR.top < screenR.top) 971 tipR.top = screenR.top; 972 else if(tipR.bottom > screenR.bottom) 973 tipR.top = screenR.bottom - tipR.Height(); 974 975 if(tipR.LeftTop() != p) 976 m_tipWindow->MoveTo(tipR.LeftTop()); 977 978 if(m_tipWindow->IsHidden()) 979 m_tipWindow->Show(); 980 } 981 982 inline void _TipManagerView::_hideTip() { 983 // PRINT(( 984 // "### _TipManagerView::_hideTip()\n")); 985 986 ASSERT(m_tipWindow); 987 ASSERT(m_tipWindowState == TIP_WINDOW_VISIBLE); 988 BAutolock _l(m_tipWindow); 989 990 if(m_tipWindow->IsHidden()) 991 return; 992 993 m_tipWindow->Hide(); 994 } 995 996 // END -- TipManagerImpl.cpp -- 997