1 // TipManager.cpp 2 // e.moon 12may99 3 4 #include "TipManager.h" 5 #include "TipManagerImpl.h" 6 #include "TipWindow.h" 7 8 #include <Autolock.h> 9 #include <Message.h> 10 #include <MessageFilter.h> 11 #include <Region.h> 12 #include <float.h> 13 14 #include "debug_tools.h" 15 16 __USE_CORTEX_NAMESPACE 17 18 // -------------------------------------------------------- // 19 // constants 20 // -------------------------------------------------------- // 21 22 // static instance (created on first call to TipManager::Instance().) 23 TipManager* TipManager::s_instance = 0; 24 BLocker TipManager::s_instanceLock("TipManager::s_instanceLock"); 25 26 // special point value set to highly improbable position 27 const BPoint TipManager::s_useDefaultOffset(FLT_MIN, FLT_MIN); 28 29 // default tip position 30 const BPoint TipManager::s_defaultOffset(8.0, 8.0); 31 32 const bigtime_t TipManager::s_defIdleTime = 750000LL; 33 const bigtime_t TipManager::s_sleepPeriod = 250000LL; 34 35 // -------------------------------------------------------- // 36 // *** message filter 37 // -------------------------------------------------------- // 38 39 filter_result ignore_quit_key( 40 BMessage* message, 41 BHandler** target, 42 BMessageFilter* filter) 43 { 44 switch(message->what) 45 { 46 // filter command-Q 47 case B_KEY_DOWN: 48 { 49 if((modifiers() & B_COMMAND_KEY)) 50 { 51 int8 key; 52 message->FindInt8("byte", &key); 53 if(key == 'q') 54 return B_SKIP_MESSAGE; 55 } 56 break; 57 } 58 } 59 return B_DISPATCH_MESSAGE; 60 } 61 62 // -------------------------------------------------------- // 63 // *** dtor 64 // -------------------------------------------------------- // 65 66 TipManager::~TipManager() {} 67 68 // -------------------------------------------------------- // 69 // *** singleton access 70 // -------------------------------------------------------- // 71 72 /*static*/ 73 TipManager* TipManager::Instance() { 74 BAutolock _l(s_instanceLock); 75 if(!s_instance) 76 s_instance = new TipManager(); 77 78 return s_instance; 79 } 80 81 // kill current instance if any 82 /*static*/ 83 void TipManager::QuitInstance() { 84 BAutolock _l(s_instanceLock); 85 if(s_instance) { 86 s_instance->Lock(); 87 s_instance->Quit(); 88 s_instance = 0; 89 } 90 } 91 92 // -------------------------------------------------------- // 93 // hidden constructor (use Instance() to access 94 // a single instance) 95 // -------------------------------------------------------- // 96 97 TipManager::TipManager() : 98 99 BWindow( 100 BRect(-100,-100,-100,-100), 101 "TipManager", 102 B_NO_BORDER_WINDOW_LOOK, 103 B_FLOATING_ALL_WINDOW_FEEL, 104 B_ASYNCHRONOUS_CONTROLS | B_AVOID_FOCUS), 105 m_view(0) { 106 107 AddCommonFilter( 108 new BMessageFilter( 109 B_PROGRAMMED_DELIVERY, 110 B_ANY_SOURCE, 111 &ignore_quit_key)); 112 113 m_view = new _TipManagerView( 114 new TipWindow(), 115 this, 116 s_sleepPeriod, 117 s_defIdleTime); 118 AddChild(m_view); 119 120 // start the window thread 121 Show(); 122 } 123 124 125 // -------------------------------------------------------- // 126 // add and remove tips 127 // -------------------------------------------------------- // 128 129 // add or modify a tip: 130 131 status_t TipManager::setTip( 132 const BRect& rect, 133 const char* text, 134 BView* view, 135 offset_mode_t offsetMode /*=LEFT_OFFSET_FROM_RECT*/, 136 BPoint offset /*=s_useDefaultOffset*/, 137 uint32 flags /*=NONE*/) { 138 139 ASSERT(text); 140 ASSERT(m_view); 141 142 BAutolock _l(this); 143 return m_view->setTip( 144 rect, text, view, offsetMode, offset, flags); 145 } 146 147 148 status_t TipManager::setTip( 149 const char* text, 150 BView* view, 151 offset_mode_t offsetMode /*=LEFT_OFFSET_FROM_RECT*/, 152 BPoint offset /*=s_useDefaultOffset*/, 153 uint32 flags /*=NONE*/) { 154 155 return setTip( 156 BRect(), text, view, offsetMode, offset, flags); 157 } 158 159 // Remove all tips matching the given rectangle and/or child 160 // view. Returns the number of tips removed. 161 162 status_t TipManager::removeTip( 163 const BRect& rect, 164 BView* view) { 165 166 ASSERT(view); 167 ASSERT(m_view); 168 169 BAutolock _l(this); 170 return m_view->removeTip(rect, view); 171 } 172 173 // If more than one tip is mapped to pChild, all are removed: 174 175 status_t TipManager::removeAll( 176 BView* view) { 177 178 return removeTip(BRect(), view); 179 } 180 181 status_t TipManager::removeAll( 182 BWindow* window) { 183 184 // PRINT(( 185 // "### TipManager::removeAll(): %p, %p\n", this, m_view->Looper())); 186 187 ASSERT(window); 188 ASSERT(m_view); 189 ASSERT(m_view->Looper() == this); // +++++ 190 191 BAutolock _l(this); 192 return m_view->removeAll(window); 193 } 194 195 // -------------------------------------------------------- // 196 // *** manual tip arming 197 // -------------------------------------------------------- // 198 199 // [e.moon 19oct99] 200 // Call when the mouse has entered a particular region of 201 // the screen for which you want a tip to be displayed. 202 // The tip will be displayed if the mouse stops moving 203 // for idleTime microseconds within the rectangle screenRect. 204 205 status_t TipManager::showTip( 206 const char* text, 207 BRect screenRect, 208 offset_mode_t offsetMode /*=LEFT_OFFSET_FROM_RECT*/, 209 BPoint offset /*=s_useDefaultOffset*/, 210 uint32 flags /*=NONE*/) { 211 212 ASSERT(text); 213 ASSERT(m_view); 214 215 BAutolock _l(this); 216 return m_view->armTip( 217 screenRect, text, offsetMode, offset, flags); 218 } 219 220 // [e.moon 22oct99] 221 // Call to immediately hide a visible tip. You need to know 222 // the screen rectangle for which the tip was shown (which is easy 223 // if was displayed due to a showTip() call -- pass the same 224 // screenRect argument.) 225 // If the tip was found & hidden, returns B_OK; if there's 226 // no visible tip or it was triggered by a different rectangle, 227 // returns B_BAD_VALUE. 228 229 status_t TipManager::hideTip( 230 BRect screenRect) { 231 232 ASSERT(m_view); 233 234 BAutolock _l(this); 235 return m_view->hideTip(screenRect); 236 } 237 238 239 // -------------------------------------------------------- // 240 // *** BWindow 241 // -------------------------------------------------------- // 242 243 // -------------------------------------------------------- // 244 // *** BLooper 245 // -------------------------------------------------------- // 246 247 bool TipManager::QuitRequested() { 248 // ignored, since I receive key events bound for other apps 249 return false; 250 } 251 252 // -------------------------------------------------------- // 253 // *** BHandler 254 // -------------------------------------------------------- // 255 256 void TipManager::MessageReceived( 257 BMessage* message) { 258 259 switch(message->what) { 260 default: 261 _inherited::MessageReceived(message); 262 } 263 } 264 265 //// -------------------------------------------------------- // 266 //// BasicThread impl. 267 //// -------------------------------------------------------- // 268 // 269 //// +++++ 270 //// 12aug99: a locking bug seems to cause occasional 271 //// crashes on shutdown after the looper's been deleted. 272 //// +++++ 273 //// 23sep99: probably fixed; the TipManager needs to be manually 274 //// killed before the window is deleted. 275 // 276 //void TipManager::run() { 277 // 278 // BPoint point, lastPoint, screenPoint; 279 // bigtime_t curTime, lastTime; 280 // uint32 buttons; 281 // 282 // bool bTipVisible = false; 283 // BRect tipScreenRect; 284 // 285 // lastTime = 0; 286 // curTime = 0; 287 // 288 // // [e.moon 27sep99] 289 // // store whether the tip has fired at the current point 290 // bool fired = false; 291 // 292 // ASSERT(m_tree); 293 // BView* pOwningView = m_tree->target(); 294 // 295 // while(!stopping()) { 296 // snooze(s_sleepPeriod); 297 // if(stopping()) 298 // break; 299 // 300 // // wait for the view to show up 301 // if(!pOwningView->Parent() || !pOwningView->Window()) 302 // continue; 303 // 304 // // get current mouse position 305 // pOwningView->LockLooper(); 306 // 307 // pOwningView->GetMouse(&point, &buttons, false); 308 // screenPoint = pOwningView->ConvertToScreen(point); 309 // 310 // pOwningView->UnlockLooper(); 311 // 312 // // has it been sitting in one place long enough? 313 // bool bMoved = (point != lastPoint); 314 // 315 // if(bMoved) { 316 // lastTime = curTime; 317 // fired = false; 318 // } 319 // else if(fired) { 320 // // [27sep99 e.moon] the tip has already fired, and 321 // // the mouse hasn't moved; bail out now 322 // continue; 323 // } 324 // 325 // curTime = system_time(); 326 // bool bIdle = !bMoved && lastTime && (curTime - lastTime) > m_idleTime; 327 // lastPoint = point; 328 // 329 // if(bTipVisible) { 330 // // hide tip once mouse moves outside its rectangle 331 // if(!tipScreenRect.Contains(screenPoint)) { 332 // m_tipWindow->Lock(); 333 // if(!m_tipWindow->IsHidden()) // tip may hide itself [7sep99] 334 // m_tipWindow->Hide(); 335 // bTipVisible = false; 336 // m_tipWindow->Unlock(); 337 // } 338 // } else if(bIdle) { 339 // 340 // // mouse has idled at a given point long enough; 341 // // look for a tip at that position and display one if found: 342 // 343 // fired = true; 344 // 345 // pOwningView->LockLooper(); 346 // 347 // // make sure this part of the view is actually visible 348 // if(!pOwningView->Window()->Frame().Contains(screenPoint)) { 349 // pOwningView->UnlockLooper(); 350 // continue; 351 // } 352 // 353 // // look for a tip under the mouse 354 // m_tipWindow->Lock(); 355 // pair<BView*, const tip_entry*> found = 356 // m_tree->match(point, screenPoint); 357 // 358 // if(!found.second) { 359 // // none found; move on 360 // pOwningView->UnlockLooper(); 361 // m_tipWindow->Unlock(); 362 // continue; 363 // } 364 // 365 // BView* pTipTarget = found.first; 366 // const tip_entry& entry = *found.second; 367 // 368 // // test the screen point against the view's clipping region; 369 // // if no match, the given point is likely covered by another 370 // // window (so stop recursing) 371 // 372 // BRegion clipRegion; 373 // pTipTarget->GetClippingRegion(&clipRegion); 374 // if(!clipRegion.Contains( 375 // pTipTarget->ConvertFromScreen(screenPoint))) { 376 // // move on 377 // pOwningView->UnlockLooper(); 378 // m_tipWindow->Unlock(); 379 // continue; 380 // } 381 // 382 // // found one; set up the tip window: 383 // BRect entryFrame = pTipTarget->ConvertToScreen(entry.rect); 384 // 385 // // set text (this has the side effect of resizing the 386 // // window) 387 // 388 // ASSERT(m_tipWindow); 389 // m_tipWindow->setText(entry.text.String()); 390 // 391 // // figure out where to display it: 392 // 393 // BPoint offset = (entry.offset == s_useDefaultOffset) ? 394 // s_defaultOffset : 395 // entry.offset; 396 // 397 // BPoint p; 398 // switch(entry.offsetMode) { 399 // case LEFT_OFFSET_FROM_RECT: 400 // p = entryFrame.RightTop() + offset; 401 // break; 402 // case LEFT_OFFSET_FROM_POINTER: 403 // p = screenPoint + offset; 404 // break; 405 // case RIGHT_OFFSET_FROM_RECT: 406 // p = entryFrame.LeftTop(); 407 // p.x -= offset.x; 408 // p.y += offset.y; 409 // p.x -= m_tipWindow->Frame().Width(); 410 // break; 411 // case RIGHT_OFFSET_FROM_POINTER: 412 // p = screenPoint; 413 // p.x -= offset.x; 414 // p.y += offset.y; 415 // p.x -= m_tipWindow->Frame().Width(); 416 // break; 417 // default: 418 // ASSERT(!"bad offset mode"); 419 // } 420 // 421 // // do it: 422 // 423 // m_tipWindow->MoveTo(p); 424 // m_tipWindow->Show(); 425 // 426 // bTipVisible = true; 427 // tipScreenRect = entryFrame; 428 // 429 // m_tipWindow->Unlock(); 430 // pOwningView->UnlockLooper(); 431 // 432 // } // if(bIdle ... 433 // } // while(!stopping ... 434 //} 435 436 // END -- TipManager.cpp -- 437 438