tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / ToolTipManager.cpp
blob5084adcebf74f99bcca5580521221eeaefcf8f91
1 /*
2 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>.
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
8 #include <ToolTipManager.h>
9 #include <ToolTipWindow.h>
11 #include <pthread.h>
13 #include <Autolock.h>
14 #include <LayoutBuilder.h>
15 #include <MessageRunner.h>
16 #include <Screen.h>
18 #include <WindowPrivate.h>
19 #include <ToolTip.h>
22 static pthread_once_t sManagerInitOnce = PTHREAD_ONCE_INIT;
23 BToolTipManager* BToolTipManager::sDefaultInstance;
25 static const uint32 kMsgHideToolTip = 'hide';
26 static const uint32 kMsgShowToolTip = 'show';
27 static const uint32 kMsgCurrentToolTip = 'curr';
28 static const uint32 kMsgCloseToolTip = 'clos';
31 namespace BPrivate {
34 class ToolTipView : public BView {
35 public:
36 ToolTipView(BToolTip* tip);
37 virtual ~ToolTipView();
39 virtual void AttachedToWindow();
40 virtual void DetachedFromWindow();
42 virtual void FrameResized(float width, float height);
43 virtual void MouseMoved(BPoint where, uint32 transit,
44 const BMessage* dragMessage);
45 virtual void KeyDown(const char* bytes, int32 numBytes);
47 void HideTip();
48 void ShowTip();
50 void ResetWindowFrame();
51 void ResetWindowFrame(BPoint where);
53 BToolTip* Tip() const { return fToolTip; }
54 bool IsTipHidden() const { return fHidden; }
56 private:
57 BToolTip* fToolTip;
58 bool fHidden;
62 ToolTipView::ToolTipView(BToolTip* tip)
64 BView("tool tip", B_WILL_DRAW | B_FRAME_EVENTS),
65 fToolTip(tip),
66 fHidden(false)
68 fToolTip->AcquireReference();
69 SetViewColor(ui_color(B_TOOL_TIP_BACKGROUND_COLOR));
71 BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
72 layout->SetInsets(5, 5, 5, 5);
73 SetLayout(layout);
75 AddChild(fToolTip->View());
79 ToolTipView::~ToolTipView()
81 fToolTip->ReleaseReference();
85 void
86 ToolTipView::AttachedToWindow()
88 SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, 0);
89 fToolTip->AttachedToWindow();
93 void
94 ToolTipView::DetachedFromWindow()
96 BToolTipManager* manager = BToolTipManager::Manager();
97 manager->Lock();
99 RemoveChild(fToolTip->View());
100 // don't delete this one!
101 fToolTip->DetachedFromWindow();
103 manager->Unlock();
107 void
108 ToolTipView::FrameResized(float width, float height)
110 ResetWindowFrame();
114 void
115 ToolTipView::MouseMoved(BPoint where, uint32 transit,
116 const BMessage* dragMessage)
118 if (fToolTip->IsSticky()) {
119 ResetWindowFrame(ConvertToScreen(where));
120 } else if (transit == B_ENTERED_VIEW) {
121 // close instantly if the user managed to enter
122 Window()->Quit();
123 } else {
124 // close with the preferred delay in case the mouse just moved
125 HideTip();
130 void
131 ToolTipView::KeyDown(const char* bytes, int32 numBytes)
133 if (!fToolTip->IsSticky())
134 HideTip();
138 void
139 ToolTipView::HideTip()
141 if (fHidden)
142 return;
144 BMessage quit(kMsgCloseToolTip);
145 BMessageRunner::StartSending(Window(), &quit,
146 BToolTipManager::Manager()->HideDelay(), 1);
147 fHidden = true;
151 void
152 ToolTipView::ShowTip()
154 fHidden = false;
158 void
159 ToolTipView::ResetWindowFrame()
161 BPoint where;
162 GetMouse(&where, NULL, false);
164 ResetWindowFrame(ConvertToScreen(where));
168 /*! Tries to find the right frame to show the tool tip in, trying to use the
169 alignment that the tool tip specifies.
170 Makes sure the tool tip can be shown on screen in its entirety, ie. it will
171 resize the window if necessary.
173 void
174 ToolTipView::ResetWindowFrame(BPoint where)
176 if (Window() == NULL)
177 return;
179 BSize size = PreferredSize();
181 BScreen screen(Window());
182 BRect screenFrame = screen.Frame().InsetBySelf(2, 2);
183 BPoint offset = fToolTip->MouseRelativeLocation();
185 // Ensure that the tip can be placed on screen completely
187 if (size.width > screenFrame.Width())
188 size.width = screenFrame.Width();
190 if (size.width > where.x - screenFrame.left
191 && size.width > screenFrame.right - where.x) {
192 // There is no space to put the tip to the left or the right of the
193 // cursor, it can either be below or above it
194 if (size.height > where.y - screenFrame.top
195 && where.y - screenFrame.top > screenFrame.Height() / 2) {
196 size.height = where.y - offset.y - screenFrame.top;
197 } else if (size.height > screenFrame.bottom - where.y
198 && screenFrame.bottom - where.y > screenFrame.Height() / 2) {
199 size.height = screenFrame.bottom - where.y - offset.y;
203 // Find best alignment, starting with the requested one
205 BAlignment alignment = fToolTip->Alignment();
206 BPoint location = where;
207 bool doesNotFit = false;
209 switch (alignment.horizontal) {
210 case B_ALIGN_LEFT:
211 location.x -= size.width + offset.x;
212 if (location.x < screenFrame.left) {
213 location.x = screenFrame.left;
214 doesNotFit = true;
216 break;
217 case B_ALIGN_CENTER:
218 location.x -= size.width / 2 - offset.x;
219 if (location.x < screenFrame.left) {
220 location.x = screenFrame.left;
221 doesNotFit = true;
222 } else if (location.x + size.width > screenFrame.right) {
223 location.x = screenFrame.right - size.width;
224 doesNotFit = true;
226 break;
228 default:
229 location.x += offset.x;
230 if (location.x + size.width > screenFrame.right) {
231 location.x = screenFrame.right - size.width;
232 doesNotFit = true;
234 break;
237 if ((doesNotFit && alignment.vertical == B_ALIGN_MIDDLE)
238 || (alignment.vertical == B_ALIGN_MIDDLE
239 && alignment.horizontal == B_ALIGN_CENTER))
240 alignment.vertical = B_ALIGN_BOTTOM;
242 // Adjust the tooltip position in cases where it would be partly out of the
243 // screen frame. Try to fit the tooltip on the requested side of the
244 // cursor, if that fails, try the opposite side, and if that fails again,
245 // give up and leave the tooltip under the mouse cursor.
246 bool firstTry = true;
247 while (true) {
248 switch (alignment.vertical) {
249 case B_ALIGN_TOP:
250 location.y = where.y - size.height - offset.y;
251 if (location.y < screenFrame.top) {
252 alignment.vertical = firstTry ? B_ALIGN_BOTTOM
253 : B_ALIGN_MIDDLE;
254 firstTry = false;
255 continue;
257 break;
259 case B_ALIGN_MIDDLE:
260 location.y -= size.height / 2 - offset.y;
261 if (location.y < screenFrame.top)
262 location.y = screenFrame.top;
263 else if (location.y + size.height > screenFrame.bottom)
264 location.y = screenFrame.bottom - size.height;
265 break;
267 default:
268 location.y = where.y + offset.y;
269 if (location.y + size.height > screenFrame.bottom) {
270 alignment.vertical = firstTry ? B_ALIGN_TOP
271 : B_ALIGN_MIDDLE;
272 firstTry = false;
273 continue;
275 break;
277 break;
280 where = location;
282 // Cut off any out-of-screen areas
284 if (screenFrame.left > where.x) {
285 size.width -= where.x - screenFrame.left;
286 where.x = screenFrame.left;
287 } else if (screenFrame.right < where.x + size.width)
288 size.width = screenFrame.right - where.x;
290 if (screenFrame.top > where.y) {
291 size.height -= where.y - screenFrame.top;
292 where.y = screenFrame.top;
293 } else if (screenFrame.bottom < where.y + size.height)
294 size.height -= screenFrame.bottom - where.y;
296 // Change window frame
298 Window()->ResizeTo(size.width, size.height);
299 Window()->MoveTo(where);
303 // #pragma mark -
306 ToolTipWindow::ToolTipWindow(BToolTip* tip, BPoint where, void* owner)
308 BWindow(BRect(0, 0, 250, 10).OffsetBySelf(where), "tool tip",
309 B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
310 B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_AUTO_UPDATE_SIZE_LIMITS
311 | B_AVOID_FRONT | B_AVOID_FOCUS),
312 fOwner(owner)
314 SetLayout(new BGroupLayout(B_VERTICAL));
316 BToolTipManager* manager = BToolTipManager::Manager();
317 ToolTipView* view = new ToolTipView(tip);
319 manager->Lock();
320 AddChild(view);
321 manager->Unlock();
323 // figure out size and location
325 view->ResetWindowFrame(where);
329 void
330 ToolTipWindow::MessageReceived(BMessage* message)
332 ToolTipView* view = static_cast<ToolTipView*>(ChildAt(0));
334 switch (message->what) {
335 case kMsgHideToolTip:
336 view->HideTip();
337 break;
339 case kMsgCurrentToolTip:
341 BToolTip* tip = view->Tip();
343 BMessage reply(B_REPLY);
344 reply.AddPointer("current", tip);
345 reply.AddPointer("owner", fOwner);
347 if (message->SendReply(&reply) == B_OK)
348 tip->AcquireReference();
349 break;
352 case kMsgShowToolTip:
353 view->ShowTip();
354 break;
356 case kMsgCloseToolTip:
357 if (view->IsTipHidden())
358 Quit();
359 break;
361 default:
362 BWindow::MessageReceived(message);
367 } // namespace BPrivate
370 // #pragma mark -
373 /*static*/ BToolTipManager*
374 BToolTipManager::Manager()
376 // Note: The check is not necessary; it's just faster than always calling
377 // pthread_once(). It requires reading/writing of pointers to be atomic
378 // on the architecture.
379 if (sDefaultInstance == NULL)
380 pthread_once(&sManagerInitOnce, &_InitSingleton);
382 return sDefaultInstance;
386 void
387 BToolTipManager::ShowTip(BToolTip* tip, BPoint where, void* owner)
389 BToolTip* current = NULL;
390 void* currentOwner = NULL;
391 BMessage reply;
392 if (fWindow.SendMessage(kMsgCurrentToolTip, &reply) == B_OK) {
393 reply.FindPointer("current", (void**)&current);
394 reply.FindPointer("owner", &currentOwner);
397 // Release reference from the message
398 if (current != NULL)
399 current->ReleaseReference();
401 if (current == tip || currentOwner == owner) {
402 fWindow.SendMessage(kMsgShowToolTip);
403 return;
406 fWindow.SendMessage(kMsgHideToolTip);
408 if (tip != NULL) {
409 BWindow* window = new BPrivate::ToolTipWindow(tip, where, owner);
410 window->Show();
412 fWindow = BMessenger(window);
417 void
418 BToolTipManager::HideTip()
420 fWindow.SendMessage(kMsgHideToolTip);
424 void
425 BToolTipManager::SetShowDelay(bigtime_t time)
427 // between 10ms and 3s
428 if (time < 10000)
429 time = 10000;
430 else if (time > 3000000)
431 time = 3000000;
433 fShowDelay = time;
437 bigtime_t
438 BToolTipManager::ShowDelay() const
440 return fShowDelay;
444 void
445 BToolTipManager::SetHideDelay(bigtime_t time)
447 // between 0 and 0.5s
448 if (time < 0)
449 time = 0;
450 else if (time > 500000)
451 time = 500000;
453 fHideDelay = time;
457 bigtime_t
458 BToolTipManager::HideDelay() const
460 return fHideDelay;
464 BToolTipManager::BToolTipManager()
466 fLock("tool tip manager"),
467 fShowDelay(750000),
468 fHideDelay(50000)
473 BToolTipManager::~BToolTipManager()
478 /*static*/ void
479 BToolTipManager::_InitSingleton()
481 sDefaultInstance = new BToolTipManager();