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