vfs: check userland buffers before reading them.
[haiku.git] / src / kits / interface / ToolTipManager.cpp
blobad0f8024f8c69e1620cb799fecaa9ec073b5aa12
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 SetViewUIColor(B_TOOL_TIP_BACKGROUND_COLOR);
70 SetHighUIColor(B_TOOL_TIP_TEXT_COLOR);
72 BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
73 layout->SetInsets(5, 5, 5, 5);
74 SetLayout(layout);
76 AddChild(fToolTip->View());
80 ToolTipView::~ToolTipView()
82 fToolTip->ReleaseReference();
86 void
87 ToolTipView::AttachedToWindow()
89 SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, 0);
90 fToolTip->AttachedToWindow();
94 void
95 ToolTipView::DetachedFromWindow()
97 BToolTipManager* manager = BToolTipManager::Manager();
98 manager->Lock();
100 RemoveChild(fToolTip->View());
101 // don't delete this one!
102 fToolTip->DetachedFromWindow();
104 manager->Unlock();
108 void
109 ToolTipView::FrameResized(float width, float height)
111 ResetWindowFrame();
115 void
116 ToolTipView::MouseMoved(BPoint where, uint32 transit,
117 const BMessage* dragMessage)
119 if (fToolTip->IsSticky()) {
120 ResetWindowFrame(ConvertToScreen(where));
121 } else if (transit == B_ENTERED_VIEW) {
122 // close instantly if the user managed to enter
123 Window()->Quit();
124 } else {
125 // close with the preferred delay in case the mouse just moved
126 HideTip();
131 void
132 ToolTipView::KeyDown(const char* bytes, int32 numBytes)
134 if (!fToolTip->IsSticky())
135 HideTip();
139 void
140 ToolTipView::HideTip()
142 if (fHidden)
143 return;
145 BMessage quit(kMsgCloseToolTip);
146 BMessageRunner::StartSending(Window(), &quit,
147 BToolTipManager::Manager()->HideDelay(), 1);
148 fHidden = true;
152 void
153 ToolTipView::ShowTip()
155 fHidden = false;
159 void
160 ToolTipView::ResetWindowFrame()
162 BPoint where;
163 GetMouse(&where, NULL, false);
165 ResetWindowFrame(ConvertToScreen(where));
169 /*! Tries to find the right frame to show the tool tip in, trying to use the
170 alignment that the tool tip specifies.
171 Makes sure the tool tip can be shown on screen in its entirety, ie. it will
172 resize the window if necessary.
174 void
175 ToolTipView::ResetWindowFrame(BPoint where)
177 if (Window() == NULL)
178 return;
180 BSize size = PreferredSize();
182 BScreen screen(Window());
183 BRect screenFrame = screen.Frame().InsetBySelf(2, 2);
184 BPoint offset = fToolTip->MouseRelativeLocation();
186 // Ensure that the tip can be placed on screen completely
188 if (size.width > screenFrame.Width())
189 size.width = screenFrame.Width();
191 if (size.width > where.x - screenFrame.left
192 && size.width > screenFrame.right - where.x) {
193 // There is no space to put the tip to the left or the right of the
194 // cursor, it can either be below or above it
195 if (size.height > where.y - screenFrame.top
196 && where.y - screenFrame.top > screenFrame.Height() / 2) {
197 size.height = where.y - offset.y - screenFrame.top;
198 } else if (size.height > screenFrame.bottom - where.y
199 && screenFrame.bottom - where.y > screenFrame.Height() / 2) {
200 size.height = screenFrame.bottom - where.y - offset.y;
204 // Find best alignment, starting with the requested one
206 BAlignment alignment = fToolTip->Alignment();
207 BPoint location = where;
208 bool doesNotFit = false;
210 switch (alignment.horizontal) {
211 case B_ALIGN_LEFT:
212 location.x -= size.width + offset.x;
213 if (location.x < screenFrame.left) {
214 location.x = screenFrame.left;
215 doesNotFit = true;
217 break;
218 case B_ALIGN_CENTER:
219 location.x -= size.width / 2 - offset.x;
220 if (location.x < screenFrame.left) {
221 location.x = screenFrame.left;
222 doesNotFit = true;
223 } else if (location.x + size.width > screenFrame.right) {
224 location.x = screenFrame.right - size.width;
225 doesNotFit = true;
227 break;
229 default:
230 location.x += offset.x;
231 if (location.x + size.width > screenFrame.right) {
232 location.x = screenFrame.right - size.width;
233 doesNotFit = true;
235 break;
238 if ((doesNotFit && alignment.vertical == B_ALIGN_MIDDLE)
239 || (alignment.vertical == B_ALIGN_MIDDLE
240 && alignment.horizontal == B_ALIGN_CENTER))
241 alignment.vertical = B_ALIGN_BOTTOM;
243 // Adjust the tooltip position in cases where it would be partly out of the
244 // screen frame. Try to fit the tooltip on the requested side of the
245 // cursor, if that fails, try the opposite side, and if that fails again,
246 // give up and leave the tooltip under the mouse cursor.
247 bool firstTry = true;
248 while (true) {
249 switch (alignment.vertical) {
250 case B_ALIGN_TOP:
251 location.y = where.y - size.height - offset.y;
252 if (location.y < screenFrame.top) {
253 alignment.vertical = firstTry ? B_ALIGN_BOTTOM
254 : B_ALIGN_MIDDLE;
255 firstTry = false;
256 continue;
258 break;
260 case B_ALIGN_MIDDLE:
261 location.y -= size.height / 2 - offset.y;
262 if (location.y < screenFrame.top)
263 location.y = screenFrame.top;
264 else if (location.y + size.height > screenFrame.bottom)
265 location.y = screenFrame.bottom - size.height;
266 break;
268 default:
269 location.y = where.y + offset.y;
270 if (location.y + size.height > screenFrame.bottom) {
271 alignment.vertical = firstTry ? B_ALIGN_TOP
272 : B_ALIGN_MIDDLE;
273 firstTry = false;
274 continue;
276 break;
278 break;
281 where = location;
283 // Cut off any out-of-screen areas
285 if (screenFrame.left > where.x) {
286 size.width -= where.x - screenFrame.left;
287 where.x = screenFrame.left;
288 } else if (screenFrame.right < where.x + size.width)
289 size.width = screenFrame.right - where.x;
291 if (screenFrame.top > where.y) {
292 size.height -= where.y - screenFrame.top;
293 where.y = screenFrame.top;
294 } else if (screenFrame.bottom < where.y + size.height)
295 size.height -= screenFrame.bottom - where.y;
297 // Change window frame
299 Window()->ResizeTo(size.width, size.height);
300 Window()->MoveTo(where);
304 // #pragma mark -
307 ToolTipWindow::ToolTipWindow(BToolTip* tip, BPoint where, void* owner)
309 BWindow(BRect(0, 0, 250, 10).OffsetBySelf(where), "tool tip",
310 B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
311 B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_AUTO_UPDATE_SIZE_LIMITS
312 | B_AVOID_FRONT | B_AVOID_FOCUS),
313 fOwner(owner)
315 SetLayout(new BGroupLayout(B_VERTICAL));
317 BToolTipManager* manager = BToolTipManager::Manager();
318 ToolTipView* view = new ToolTipView(tip);
320 manager->Lock();
321 AddChild(view);
322 manager->Unlock();
324 // figure out size and location
326 view->ResetWindowFrame(where);
330 void
331 ToolTipWindow::MessageReceived(BMessage* message)
333 ToolTipView* view = static_cast<ToolTipView*>(ChildAt(0));
335 switch (message->what) {
336 case kMsgHideToolTip:
337 view->HideTip();
338 break;
340 case kMsgCurrentToolTip:
342 BToolTip* tip = view->Tip();
344 BMessage reply(B_REPLY);
345 reply.AddPointer("current", tip);
346 reply.AddPointer("owner", fOwner);
348 if (message->SendReply(&reply) == B_OK)
349 tip->AcquireReference();
350 break;
353 case kMsgShowToolTip:
354 view->ShowTip();
355 break;
357 case kMsgCloseToolTip:
358 if (view->IsTipHidden())
359 Quit();
360 break;
362 default:
363 BWindow::MessageReceived(message);
368 } // namespace BPrivate
371 // #pragma mark -
374 /*static*/ BToolTipManager*
375 BToolTipManager::Manager()
377 // Note: The check is not necessary; it's just faster than always calling
378 // pthread_once(). It requires reading/writing of pointers to be atomic
379 // on the architecture.
380 if (sDefaultInstance == NULL)
381 pthread_once(&sManagerInitOnce, &_InitSingleton);
383 return sDefaultInstance;
387 void
388 BToolTipManager::ShowTip(BToolTip* tip, BPoint where, void* owner)
390 BToolTip* current = NULL;
391 void* currentOwner = NULL;
392 BMessage reply;
393 if (fWindow.SendMessage(kMsgCurrentToolTip, &reply) == B_OK) {
394 reply.FindPointer("current", (void**)&current);
395 reply.FindPointer("owner", &currentOwner);
398 // Release reference from the message
399 if (current != NULL)
400 current->ReleaseReference();
402 if (current == tip || currentOwner == owner) {
403 fWindow.SendMessage(kMsgShowToolTip);
404 return;
407 fWindow.SendMessage(kMsgHideToolTip);
409 if (tip != NULL) {
410 BWindow* window = new BPrivate::ToolTipWindow(tip, where, owner);
411 window->Show();
413 fWindow = BMessenger(window);
418 void
419 BToolTipManager::HideTip()
421 fWindow.SendMessage(kMsgHideToolTip);
425 void
426 BToolTipManager::SetShowDelay(bigtime_t time)
428 // between 10ms and 3s
429 if (time < 10000)
430 time = 10000;
431 else if (time > 3000000)
432 time = 3000000;
434 fShowDelay = time;
438 bigtime_t
439 BToolTipManager::ShowDelay() const
441 return fShowDelay;
445 void
446 BToolTipManager::SetHideDelay(bigtime_t time)
448 // between 0 and 0.5s
449 if (time < 0)
450 time = 0;
451 else if (time > 500000)
452 time = 500000;
454 fHideDelay = time;
458 bigtime_t
459 BToolTipManager::HideDelay() const
461 return fHideDelay;
465 BToolTipManager::BToolTipManager()
467 fLock("tool tip manager"),
468 fShowDelay(750000),
469 fHideDelay(50000)
474 BToolTipManager::~BToolTipManager()
479 /*static*/ void
480 BToolTipManager::_InitSingleton()
482 sDefaultInstance = new BToolTipManager();