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