xref: /haiku/src/apps/cortex/TipManager/TipManager.cpp (revision 8195a5a835117ab2da405e0d477153570b75d921)
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