/* * Copyright (c) 1999-2000, Eric Moon. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions, and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // TipManager.cpp // e.moon 12may99 #include "TipManager.h" #include "TipManagerImpl.h" #include "TipWindow.h" #include #include #include #include #include #include "debug_tools.h" __USE_CORTEX_NAMESPACE // -------------------------------------------------------- // // constants // -------------------------------------------------------- // // static instance (created on first call to TipManager::Instance().) TipManager* TipManager::s_instance = 0; BLocker TipManager::s_instanceLock("TipManager::s_instanceLock"); // special point value set to highly improbable position const BPoint TipManager::s_useDefaultOffset(FLT_MIN, FLT_MIN); // default tip position const BPoint TipManager::s_defaultOffset(8.0, 8.0); const bigtime_t TipManager::s_defIdleTime = 750000LL; const bigtime_t TipManager::s_sleepPeriod = 250000LL; // -------------------------------------------------------- // // *** message filter // -------------------------------------------------------- // filter_result ignore_quit_key( BMessage* message, BHandler** target, BMessageFilter* filter) { switch(message->what) { // filter command-Q case B_KEY_DOWN: { if((modifiers() & B_COMMAND_KEY)) { int8 key; message->FindInt8("byte", &key); if(key == 'q') return B_SKIP_MESSAGE; } break; } } return B_DISPATCH_MESSAGE; } // -------------------------------------------------------- // // *** dtor // -------------------------------------------------------- // TipManager::~TipManager() {} // -------------------------------------------------------- // // *** singleton access // -------------------------------------------------------- // /*static*/ TipManager* TipManager::Instance() { BAutolock _l(s_instanceLock); if(!s_instance) s_instance = new TipManager(); return s_instance; } // kill current instance if any /*static*/ void TipManager::QuitInstance() { BAutolock _l(s_instanceLock); if(s_instance) { s_instance->Lock(); s_instance->Quit(); s_instance = 0; } } // -------------------------------------------------------- // // hidden constructor (use Instance() to access // a single instance) // -------------------------------------------------------- // TipManager::TipManager() : BWindow( BRect(-100,-100,-100,-100), "TipManager", B_NO_BORDER_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS | B_AVOID_FOCUS), m_view(0) { AddCommonFilter( new BMessageFilter( B_PROGRAMMED_DELIVERY, B_ANY_SOURCE, &ignore_quit_key)); m_view = new _TipManagerView( new TipWindow(), this, s_sleepPeriod, s_defIdleTime); AddChild(m_view); // start the window thread Show(); } // -------------------------------------------------------- // // add and remove tips // -------------------------------------------------------- // // add or modify a tip: status_t TipManager::setTip( const BRect& rect, const char* text, BView* view, offset_mode_t offsetMode /*=LEFT_OFFSET_FROM_RECT*/, BPoint offset /*=s_useDefaultOffset*/, uint32 flags /*=NONE*/) { ASSERT(text); ASSERT(m_view); BAutolock _l(this); return m_view->setTip( rect, text, view, offsetMode, offset, flags); } status_t TipManager::setTip( const char* text, BView* view, offset_mode_t offsetMode /*=LEFT_OFFSET_FROM_RECT*/, BPoint offset /*=s_useDefaultOffset*/, uint32 flags /*=NONE*/) { return setTip( BRect(), text, view, offsetMode, offset, flags); } // Remove all tips matching the given rectangle and/or child // view. Returns the number of tips removed. status_t TipManager::removeTip( const BRect& rect, BView* view) { ASSERT(view); ASSERT(m_view); BAutolock _l(this); return m_view->removeTip(rect, view); } // If more than one tip is mapped to pChild, all are removed: status_t TipManager::removeAll( BView* view) { return removeTip(BRect(), view); } status_t TipManager::removeAll( BWindow* window) { // PRINT(( // "### TipManager::removeAll(): %p, %p\n", this, m_view->Looper())); ASSERT(window); ASSERT(m_view); ASSERT(m_view->Looper() == this); // +++++ BAutolock _l(this); return m_view->removeAll(window); } // -------------------------------------------------------- // // *** manual tip arming // -------------------------------------------------------- // // [e.moon 19oct99] // Call when the mouse has entered a particular region of // the screen for which you want a tip to be displayed. // The tip will be displayed if the mouse stops moving // for idleTime microseconds within the rectangle screenRect. status_t TipManager::showTip( const char* text, BRect screenRect, offset_mode_t offsetMode /*=LEFT_OFFSET_FROM_RECT*/, BPoint offset /*=s_useDefaultOffset*/, uint32 flags /*=NONE*/) { ASSERT(text); ASSERT(m_view); BAutolock _l(this); return m_view->armTip( screenRect, text, offsetMode, offset, flags); } // [e.moon 22oct99] // Call to immediately hide a visible tip. You need to know // the screen rectangle for which the tip was shown (which is easy // if was displayed due to a showTip() call -- pass the same // screenRect argument.) // If the tip was found & hidden, returns B_OK; if there's // no visible tip or it was triggered by a different rectangle, // returns B_BAD_VALUE. status_t TipManager::hideTip( BRect screenRect) { ASSERT(m_view); BAutolock _l(this); return m_view->hideTip(screenRect); } // -------------------------------------------------------- // // *** BWindow // -------------------------------------------------------- // // -------------------------------------------------------- // // *** BLooper // -------------------------------------------------------- // bool TipManager::QuitRequested() { // ignored, since I receive key events bound for other apps return false; } // -------------------------------------------------------- // // *** BHandler // -------------------------------------------------------- // void TipManager::MessageReceived( BMessage* message) { switch(message->what) { default: _inherited::MessageReceived(message); } } //// -------------------------------------------------------- // //// BasicThread impl. //// -------------------------------------------------------- // // //// +++++ //// 12aug99: a locking bug seems to cause occasional //// crashes on shutdown after the looper's been deleted. //// +++++ //// 23sep99: probably fixed; the TipManager needs to be manually //// killed before the window is deleted. // //void TipManager::run() { // // BPoint point, lastPoint, screenPoint; // bigtime_t curTime, lastTime; // uint32 buttons; // // bool bTipVisible = false; // BRect tipScreenRect; // // lastTime = 0; // curTime = 0; // // // [e.moon 27sep99] // // store whether the tip has fired at the current point // bool fired = false; // // ASSERT(m_tree); // BView* pOwningView = m_tree->target(); // // while(!stopping()) { // snooze(s_sleepPeriod); // if(stopping()) // break; // // // wait for the view to show up // if(!pOwningView->Parent() || !pOwningView->Window()) // continue; // // // get current mouse position // pOwningView->LockLooper(); // // pOwningView->GetMouse(&point, &buttons, false); // screenPoint = pOwningView->ConvertToScreen(point); // // pOwningView->UnlockLooper(); // // // has it been sitting in one place long enough? // bool bMoved = (point != lastPoint); // // if(bMoved) { // lastTime = curTime; // fired = false; // } // else if(fired) { // // [27sep99 e.moon] the tip has already fired, and // // the mouse hasn't moved; bail out now // continue; // } // // curTime = system_time(); // bool bIdle = !bMoved && lastTime && (curTime - lastTime) > m_idleTime; // lastPoint = point; // // if(bTipVisible) { // // hide tip once mouse moves outside its rectangle // if(!tipScreenRect.Contains(screenPoint)) { // m_tipWindow->Lock(); // if(!m_tipWindow->IsHidden()) // tip may hide itself [7sep99] // m_tipWindow->Hide(); // bTipVisible = false; // m_tipWindow->Unlock(); // } // } else if(bIdle) { // // // mouse has idled at a given point long enough; // // look for a tip at that position and display one if found: // // fired = true; // // pOwningView->LockLooper(); // // // make sure this part of the view is actually visible // if(!pOwningView->Window()->Frame().Contains(screenPoint)) { // pOwningView->UnlockLooper(); // continue; // } // // // look for a tip under the mouse // m_tipWindow->Lock(); // pair found = // m_tree->match(point, screenPoint); // // if(!found.second) { // // none found; move on // pOwningView->UnlockLooper(); // m_tipWindow->Unlock(); // continue; // } // // BView* pTipTarget = found.first; // const tip_entry& entry = *found.second; // // // test the screen point against the view's clipping region; // // if no match, the given point is likely covered by another // // window (so stop recursing) // // BRegion clipRegion; // pTipTarget->GetClippingRegion(&clipRegion); // if(!clipRegion.Contains( // pTipTarget->ConvertFromScreen(screenPoint))) { // // move on // pOwningView->UnlockLooper(); // m_tipWindow->Unlock(); // continue; // } // // // found one; set up the tip window: // BRect entryFrame = pTipTarget->ConvertToScreen(entry.rect); // // // set text (this has the side effect of resizing the // // window) // // ASSERT(m_tipWindow); // m_tipWindow->setText(entry.text.String()); // // // figure out where to display it: // // BPoint offset = (entry.offset == s_useDefaultOffset) ? // s_defaultOffset : // entry.offset; // // BPoint p; // switch(entry.offsetMode) { // case LEFT_OFFSET_FROM_RECT: // p = entryFrame.RightTop() + offset; // break; // case LEFT_OFFSET_FROM_POINTER: // p = screenPoint + offset; // break; // case RIGHT_OFFSET_FROM_RECT: // p = entryFrame.LeftTop(); // p.x -= offset.x; // p.y += offset.y; // p.x -= m_tipWindow->Frame().Width(); // break; // case RIGHT_OFFSET_FROM_POINTER: // p = screenPoint; // p.x -= offset.x; // p.y += offset.y; // p.x -= m_tipWindow->Frame().Width(); // break; // default: // ASSERT(!"bad offset mode"); // } // // // do it: // // m_tipWindow->MoveTo(p); // m_tipWindow->Show(); // // bTipVisible = true; // tipScreenRect = entryFrame; // // m_tipWindow->Unlock(); // pOwningView->UnlockLooper(); // // } // if(bIdle ... // } // while(!stopping ... //} // END -- TipManager.cpp --