/** 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 <Autolock.h>#include <Message.h>#include <MessageFilter.h>#include <Region.h>#include <float.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 positionconst BPoint TipManager::s_useDefaultOffset(FLT_MIN, FLT_MIN);// default tip positionconst 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-Qcase 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 threadShow();}// -------------------------------------------------------- //// 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 appsreturn 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<BView*, const tip_entry*> 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 --