vfs: check userland buffers before reading them.
[haiku.git] / src / kits / interface / MenuWindow.cpp
blobe8e5eeb4c62f84ca6518f30f5292736b92cce542
1 /*
2 * Copyright 2001-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stefano Ceccherini (stefano.ceccherini@gmail.com)
8 */
10 //! BMenuWindow is a custom BWindow for BMenus.
12 #include <MenuWindow.h>
14 #include <ControlLook.h>
15 #include <Debug.h>
16 #include <Menu.h>
17 #include <MenuItem.h>
19 #include <MenuPrivate.h>
20 #include <WindowPrivate.h>
23 namespace BPrivate {
25 class BMenuScroller : public BView {
26 public:
27 BMenuScroller(BRect frame);
29 bool IsEnabled() const;
30 void SetEnabled(bool enabled);
32 private:
33 bool fEnabled;
37 class BMenuFrame : public BView {
38 public:
39 BMenuFrame(BMenu* menu);
41 virtual void AttachedToWindow();
42 virtual void DetachedFromWindow();
43 virtual void Draw(BRect updateRect);
45 private:
46 friend class BMenuWindow;
48 BMenu* fMenu;
52 class UpperScroller : public BMenuScroller {
53 public:
54 UpperScroller(BRect frame);
56 virtual void Draw(BRect updateRect);
60 class LowerScroller : public BMenuScroller {
61 public:
62 LowerScroller(BRect frame);
64 virtual void Draw(BRect updateRect);
68 } // namespace BPrivate
71 using namespace BPrivate;
74 const int kScrollerHeight = 12;
77 BMenuScroller::BMenuScroller(BRect frame)
79 BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS),
80 fEnabled(false)
82 SetViewUIColor(B_MENU_BACKGROUND_COLOR);
86 bool
87 BMenuScroller::IsEnabled() const
89 return fEnabled;
93 void
94 BMenuScroller::SetEnabled(bool enabled)
96 fEnabled = enabled;
100 // #pragma mark -
103 UpperScroller::UpperScroller(BRect frame)
105 BMenuScroller(frame)
110 void
111 UpperScroller::Draw(BRect updateRect)
113 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
114 float middle = Bounds().right / 2;
116 // Draw the upper arrow.
117 if (IsEnabled())
118 SetHighColor(0, 0, 0);
119 else {
120 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
121 B_DARKEN_2_TINT));
124 FillRect(Bounds(), B_SOLID_LOW);
126 FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3),
127 BPoint(middle + 5, (kScrollerHeight / 2) + 2),
128 BPoint(middle - 5, (kScrollerHeight / 2) + 2));
132 // #pragma mark -
135 LowerScroller::LowerScroller(BRect frame)
137 BMenuScroller(frame)
142 void
143 LowerScroller::Draw(BRect updateRect)
145 SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT));
147 BRect frame = Bounds();
148 // Draw the lower arrow.
149 if (IsEnabled())
150 SetHighColor(0, 0, 0);
151 else {
152 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
153 B_DARKEN_2_TINT));
156 FillRect(frame, B_SOLID_LOW);
158 float middle = Bounds().right / 2;
160 FillTriangle(BPoint(middle, frame.bottom - (kScrollerHeight / 2) + 3),
161 BPoint(middle + 5, frame.bottom - (kScrollerHeight / 2) - 2),
162 BPoint(middle - 5, frame.bottom - (kScrollerHeight / 2) - 2));
166 // #pragma mark -
169 BMenuFrame::BMenuFrame(BMenu *menu)
171 BView(BRect(0, 0, 1, 1), "menu frame", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
172 fMenu(menu)
177 void
178 BMenuFrame::AttachedToWindow()
180 BView::AttachedToWindow();
182 if (fMenu != NULL)
183 AddChild(fMenu);
185 ResizeTo(Window()->Bounds().Width(), Window()->Bounds().Height());
186 if (fMenu != NULL) {
187 BFont font;
188 fMenu->GetFont(&font);
189 SetFont(&font);
194 void
195 BMenuFrame::DetachedFromWindow()
197 if (fMenu != NULL)
198 RemoveChild(fMenu);
202 void
203 BMenuFrame::Draw(BRect updateRect)
205 if (fMenu != NULL && fMenu->CountItems() == 0) {
206 BRect rect(Bounds());
207 be_control_look->DrawMenuBackground(this, rect, updateRect,
208 ui_color(B_MENU_BACKGROUND_COLOR));
209 SetDrawingMode(B_OP_OVER);
211 // TODO: Review this as it's a bit hacky.
212 // Since there are no items in this menu, its size is 0x0.
213 // To show an empty BMenu, we use BMenuFrame to draw an empty item.
214 // It would be nice to simply add a real "empty" item, but in that case
215 // we couldn't tell if the item was added by us or not, and applications
216 // could break (because CountItems() would return 1 for an empty BMenu).
217 // See also BMenu::UpdateWindowViewSize()
218 font_height height;
219 GetFontHeight(&height);
220 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
221 B_DISABLED_LABEL_TINT));
222 BPoint where(
223 (Bounds().Width() - fMenu->StringWidth(kEmptyMenuLabel)) / 2,
224 ceilf(height.ascent + 1));
225 DrawString(kEmptyMenuLabel, where);
231 // #pragma mark -
234 BMenuWindow::BMenuWindow(const char *name)
235 // The window will be resized by BMenu, so just pass a dummy rect
237 BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
238 B_NOT_MOVABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_AVOID_FOCUS
239 | kAcceptKeyboardFocusFlag),
240 fMenu(NULL),
241 fMenuFrame(NULL),
242 fUpperScroller(NULL),
243 fLowerScroller(NULL),
244 fScrollStep(19)
246 SetSizeLimits(2, 10000, 2, 10000);
250 BMenuWindow::~BMenuWindow()
252 DetachMenu();
256 void
257 BMenuWindow::DispatchMessage(BMessage *message, BHandler *handler)
259 BWindow::DispatchMessage(message, handler);
263 void
264 BMenuWindow::AttachMenu(BMenu *menu)
266 if (fMenuFrame)
267 debugger("BMenuWindow: a menu is already attached!");
268 if (menu != NULL) {
269 fMenuFrame = new BMenuFrame(menu);
270 AddChild(fMenuFrame);
271 menu->MakeFocus(true);
272 fMenu = menu;
277 void
278 BMenuWindow::DetachMenu()
280 DetachScrollers();
281 if (fMenuFrame) {
282 RemoveChild(fMenuFrame);
283 delete fMenuFrame;
284 fMenuFrame = NULL;
285 fMenu = NULL;
290 void
291 BMenuWindow::AttachScrollers()
293 // We want to attach a scroller only if there's a
294 // menu frame already existing.
295 if (!fMenu || !fMenuFrame)
296 return;
298 fMenu->MakeFocus(true);
300 BRect frame = Bounds();
302 if (fUpperScroller == NULL) {
303 fUpperScroller = new UpperScroller(
304 BRect(0, 0, frame.right, kScrollerHeight - 1));
305 AddChild(fUpperScroller);
308 if (fLowerScroller == NULL) {
309 fLowerScroller = new LowerScroller(
310 BRect(0, frame.bottom - kScrollerHeight + 1, frame.right,
311 frame.bottom));
312 AddChild(fLowerScroller);
315 fUpperScroller->SetEnabled(false);
316 fLowerScroller->SetEnabled(true);
318 fMenuFrame->ResizeBy(0, -2 * kScrollerHeight);
319 fMenuFrame->MoveBy(0, kScrollerHeight);
321 fValue = 0;
322 fLimit = fMenu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight);
326 void
327 BMenuWindow::DetachScrollers()
329 // BeOS doesn't remember the position where the last scrolling ended,
330 // so we just scroll back to the beginning.
331 if (fMenu)
332 fMenu->ScrollTo(0, 0);
334 if (fLowerScroller) {
335 RemoveChild(fLowerScroller);
336 delete fLowerScroller;
337 fLowerScroller = NULL;
340 if (fUpperScroller) {
341 RemoveChild(fUpperScroller);
342 delete fUpperScroller;
343 fUpperScroller = NULL;
348 void
349 BMenuWindow::SetSmallStep(float step)
351 fScrollStep = step;
355 void
356 BMenuWindow::GetSteps(float* _smallStep, float* _largeStep) const
358 if (_smallStep != NULL)
359 *_smallStep = fScrollStep;
360 if (_largeStep != NULL) {
361 if (fMenuFrame != NULL)
362 *_largeStep = fMenuFrame->Bounds().Height() - fScrollStep;
363 else
364 *_largeStep = fScrollStep * 2;
369 bool
370 BMenuWindow::HasScrollers() const
372 return fMenuFrame != NULL && fUpperScroller != NULL
373 && fLowerScroller != NULL;
377 bool
378 BMenuWindow::CheckForScrolling(const BPoint &cursor)
380 if (!fMenuFrame || !fUpperScroller || !fLowerScroller)
381 return false;
383 return _Scroll(cursor);
387 bool
388 BMenuWindow::TryScrollBy(const float& step)
390 if (!fMenuFrame || !fUpperScroller || !fLowerScroller)
391 return false;
393 _ScrollBy(step);
394 return true;
398 bool
399 BMenuWindow::TryScrollTo(const float& where)
401 if (!fMenuFrame || !fUpperScroller || !fLowerScroller)
402 return false;
404 _ScrollBy(where - fValue);
405 return true;
409 bool
410 BMenuWindow::_Scroll(const BPoint& where)
412 ASSERT((fLowerScroller != NULL));
413 ASSERT((fUpperScroller != NULL));
415 const BPoint cursor = ConvertFromScreen(where);
416 const BRect &lowerFrame = fLowerScroller->Frame();
417 const BRect &upperFrame = fUpperScroller->Frame();
419 int32 delta = 0;
420 if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor))
421 delta = 1;
422 else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor))
423 delta = -1;
425 if (delta == 0)
426 return false;
428 float smallStep;
429 GetSteps(&smallStep, NULL);
430 _ScrollBy(smallStep * delta);
432 snooze(5000);
434 return true;
438 void
439 BMenuWindow::_ScrollBy(const float& step)
441 if (step > 0) {
442 if (fValue == 0) {
443 fUpperScroller->SetEnabled(true);
444 fUpperScroller->Invalidate();
447 if (fValue + step >= fLimit) {
448 // If we reached the limit, only scroll to the end
449 fMenu->ScrollBy(0, fLimit - fValue);
450 fValue = fLimit;
451 fLowerScroller->SetEnabled(false);
452 fLowerScroller->Invalidate();
453 } else {
454 fMenu->ScrollBy(0, step);
455 fValue += step;
457 } else if (step < 0) {
458 if (fValue == fLimit) {
459 fLowerScroller->SetEnabled(true);
460 fLowerScroller->Invalidate();
463 if (fValue + step <= 0) {
464 fMenu->ScrollBy(0, -fValue);
465 fValue = 0;
466 fUpperScroller->SetEnabled(false);
467 fUpperScroller->Invalidate();
468 } else {
469 fMenu->ScrollBy(0, step);
470 fValue += step;