vfs: check userland buffers before reading them.
[haiku.git] / src / kits / interface / Button.cpp
blob9fc077c6a20cc14fd7601634226c33e445a6049c
1 /*
2 * Copyright 2001-2015 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Mike Wilber
8 * Stefano Ceccherini (burton666@libero.it)
9 * Ivan Tonizza
10 * Stephan Aßmus <superstippi@gmx.de>
11 * Ingo Weinhold, ingo_weinhold@gmx.de
15 #include <Button.h>
17 #include <algorithm>
18 #include <new>
20 #include <Bitmap.h>
21 #include <ControlLook.h>
22 #include <Font.h>
23 #include <LayoutUtils.h>
24 #include <String.h>
25 #include <Window.h>
27 #include <binary_compatibility/Interface.h>
30 enum {
31 FLAG_DEFAULT = 0x01,
32 FLAG_FLAT = 0x02,
33 FLAG_INSIDE = 0x04,
34 FLAG_WAS_PRESSED = 0x08,
38 static const float kLabelMargin = 3;
41 BButton::BButton(BRect frame, const char* name, const char* label,
42 BMessage* message, uint32 resizingMode, uint32 flags)
44 BControl(frame, name, label, message, resizingMode,
45 flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
46 fPreferredSize(-1, -1),
47 fFlags(0),
48 fBehavior(B_BUTTON_BEHAVIOR),
49 fPopUpMessage(NULL)
51 // Resize to minimum height if needed
52 font_height fh;
53 GetFontHeight(&fh);
54 float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent);
55 if (Bounds().Height() < minHeight)
56 ResizeTo(Bounds().Width(), minHeight);
60 BButton::BButton(const char* name, const char* label, BMessage* message,
61 uint32 flags)
63 BControl(name, label, message,
64 flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
65 fPreferredSize(-1, -1),
66 fFlags(0),
67 fBehavior(B_BUTTON_BEHAVIOR),
68 fPopUpMessage(NULL)
73 BButton::BButton(const char* label, BMessage* message)
75 BControl(NULL, label, message,
76 B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE),
77 fPreferredSize(-1, -1),
78 fFlags(0),
79 fBehavior(B_BUTTON_BEHAVIOR),
80 fPopUpMessage(NULL)
85 BButton::~BButton()
87 SetPopUpMessage(NULL);
91 BButton::BButton(BMessage* data)
93 BControl(data),
94 fPreferredSize(-1, -1),
95 fFlags(0),
96 fBehavior(B_BUTTON_BEHAVIOR),
97 fPopUpMessage(NULL)
99 bool isDefault = false;
100 if (data->FindBool("_default", &isDefault) == B_OK && isDefault)
101 _SetFlag(FLAG_DEFAULT, true);
102 // NOTE: Default button state will be synchronized with the window
103 // in AttachedToWindow().
107 BArchivable*
108 BButton::Instantiate(BMessage* data)
110 if (validate_instantiation(data, "BButton"))
111 return new(std::nothrow) BButton(data);
113 return NULL;
117 status_t
118 BButton::Archive(BMessage* data, bool deep) const
120 status_t err = BControl::Archive(data, deep);
122 if (err != B_OK)
123 return err;
125 if (IsDefault())
126 err = data->AddBool("_default", true);
128 return err;
132 void
133 BButton::Draw(BRect updateRect)
135 BRect rect(Bounds());
136 rgb_color background = ViewColor();
137 rgb_color base = LowColor();
138 rgb_color textColor = ui_color(B_CONTROL_TEXT_COLOR);
140 uint32 flags = be_control_look->Flags(this);
141 if (_Flag(FLAG_DEFAULT))
142 flags |= BControlLook::B_DEFAULT_BUTTON;
143 if (_Flag(FLAG_FLAT) && !IsTracking())
144 flags |= BControlLook::B_FLAT;
145 if (_Flag(FLAG_INSIDE))
146 flags |= BControlLook::B_HOVER;
148 be_control_look->DrawButtonFrame(this, rect, updateRect,
149 base, background, flags);
151 if (fBehavior == B_POP_UP_BEHAVIOR) {
152 be_control_look->DrawButtonWithPopUpBackground(this, rect, updateRect,
153 base, flags);
154 } else {
155 be_control_look->DrawButtonBackground(this, rect, updateRect,
156 base, flags);
159 // always leave some room around the label
160 rect.InsetBy(kLabelMargin, kLabelMargin);
162 const BBitmap* icon = IconBitmap(
163 (Value() == B_CONTROL_OFF
164 ? B_INACTIVE_ICON_BITMAP : B_ACTIVE_ICON_BITMAP)
165 | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP));
167 be_control_look->DrawLabel(this, Label(), icon, rect, updateRect, base,
168 flags, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE), &textColor);
172 void
173 BButton::MouseDown(BPoint where)
175 if (!IsEnabled())
176 return;
178 if (fBehavior == B_POP_UP_BEHAVIOR && _PopUpRect().Contains(where)) {
179 InvokeNotify(fPopUpMessage, B_CONTROL_MODIFIED);
180 return;
183 bool toggleBehavior = fBehavior == B_TOGGLE_BEHAVIOR;
185 if (toggleBehavior) {
186 bool wasPressed = Value() == B_CONTROL_ON;
187 _SetFlag(FLAG_WAS_PRESSED, wasPressed);
188 SetValue(wasPressed ? B_CONTROL_OFF : B_CONTROL_ON);
189 Invalidate();
190 } else
191 SetValue(B_CONTROL_ON);
193 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
194 SetTracking(true);
195 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
196 } else {
197 BRect bounds = Bounds();
198 uint32 buttons;
199 bool inside = false;
201 do {
202 Window()->UpdateIfNeeded();
203 snooze(40000);
205 GetMouse(&where, &buttons, true);
206 inside = bounds.Contains(where);
208 if (toggleBehavior) {
209 bool pressed = inside ^ _Flag(FLAG_WAS_PRESSED);
210 SetValue(pressed ? B_CONTROL_ON : B_CONTROL_OFF);
211 } else {
212 if ((Value() == B_CONTROL_ON) != inside)
213 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
215 } while (buttons != 0);
217 if (inside) {
218 if (toggleBehavior) {
219 SetValue(
220 _Flag(FLAG_WAS_PRESSED) ? B_CONTROL_OFF : B_CONTROL_ON);
223 Invoke();
224 } else if (_Flag(FLAG_FLAT))
225 Invalidate();
229 void
230 BButton::AttachedToWindow()
232 BControl::AttachedToWindow();
234 // Tint default control background color to match default panel background.
235 SetLowUIColor(B_CONTROL_BACKGROUND_COLOR, 1.115);
236 SetHighUIColor(B_CONTROL_TEXT_COLOR);
238 if (IsDefault())
239 Window()->SetDefaultButton(this);
243 void
244 BButton::KeyDown(const char* bytes, int32 numBytes)
246 if (*bytes == B_ENTER || *bytes == B_SPACE) {
247 if (!IsEnabled())
248 return;
250 SetValue(B_CONTROL_ON);
252 // make sure the user saw that
253 Window()->UpdateIfNeeded();
254 snooze(25000);
256 Invoke();
257 } else
258 BControl::KeyDown(bytes, numBytes);
262 void
263 BButton::MakeDefault(bool flag)
265 BButton* oldDefault = NULL;
266 BWindow* window = Window();
268 if (window != NULL)
269 oldDefault = window->DefaultButton();
271 if (flag) {
272 if (_Flag(FLAG_DEFAULT) && oldDefault == this)
273 return;
275 if (_SetFlag(FLAG_DEFAULT, true)) {
276 if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
277 InvalidateLayout();
278 else {
279 ResizeBy(6.0f, 6.0f);
280 MoveBy(-3.0f, -3.0f);
284 if (window && oldDefault != this)
285 window->SetDefaultButton(this);
286 } else {
287 if (!_SetFlag(FLAG_DEFAULT, false))
288 return;
290 if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
291 InvalidateLayout();
292 else {
293 ResizeBy(-6.0f, -6.0f);
294 MoveBy(3.0f, 3.0f);
297 if (window && oldDefault == this)
298 window->SetDefaultButton(NULL);
303 void
304 BButton::SetLabel(const char* label)
306 BControl::SetLabel(label);
310 bool
311 BButton::IsDefault() const
313 return _Flag(FLAG_DEFAULT);
317 bool
318 BButton::IsFlat() const
320 return _Flag(FLAG_FLAT);
324 void
325 BButton::SetFlat(bool flat)
327 if (_SetFlag(FLAG_FLAT, flat))
328 Invalidate();
332 BButton::BBehavior
333 BButton::Behavior() const
335 return fBehavior;
339 void
340 BButton::SetBehavior(BBehavior behavior)
342 if (behavior != fBehavior) {
343 fBehavior = behavior;
344 InvalidateLayout();
345 Invalidate();
350 BMessage*
351 BButton::PopUpMessage() const
353 return fPopUpMessage;
357 void
358 BButton::SetPopUpMessage(BMessage* message)
360 delete fPopUpMessage;
361 fPopUpMessage = message;
365 void
366 BButton::MessageReceived(BMessage* message)
368 BControl::MessageReceived(message);
372 void
373 BButton::WindowActivated(bool active)
375 BControl::WindowActivated(active);
379 void
380 BButton::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
382 bool inside = (code != B_EXITED_VIEW) && Bounds().Contains(where);
383 if (_SetFlag(FLAG_INSIDE, inside))
384 Invalidate();
386 if (!IsTracking())
387 return;
389 if (fBehavior == B_TOGGLE_BEHAVIOR) {
390 bool pressed = inside ^ _Flag(FLAG_WAS_PRESSED);
391 SetValue(pressed ? B_CONTROL_ON : B_CONTROL_OFF);
392 } else {
393 if ((Value() == B_CONTROL_ON) != inside)
394 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
399 void
400 BButton::MouseUp(BPoint where)
402 if (!IsTracking())
403 return;
405 if (Bounds().Contains(where)) {
406 if (fBehavior == B_TOGGLE_BEHAVIOR)
407 SetValue(_Flag(FLAG_WAS_PRESSED) ? B_CONTROL_OFF : B_CONTROL_ON);
409 Invoke();
410 } else if (_Flag(FLAG_FLAT))
411 Invalidate();
413 SetTracking(false);
417 void
418 BButton::DetachedFromWindow()
420 BControl::DetachedFromWindow();
424 void
425 BButton::SetValue(int32 value)
427 if (value != Value())
428 BControl::SetValue(value);
432 void
433 BButton::GetPreferredSize(float* _width, float* _height)
435 _ValidatePreferredSize();
437 if (_width)
438 *_width = fPreferredSize.width;
440 if (_height)
441 *_height = fPreferredSize.height;
445 void
446 BButton::ResizeToPreferred()
448 BControl::ResizeToPreferred();
452 status_t
453 BButton::Invoke(BMessage* message)
455 Sync();
456 snooze(50000);
458 status_t err = BControl::Invoke(message);
460 if (fBehavior != B_TOGGLE_BEHAVIOR)
461 SetValue(B_CONTROL_OFF);
463 return err;
467 void
468 BButton::FrameMoved(BPoint newPosition)
470 BControl::FrameMoved(newPosition);
474 void
475 BButton::FrameResized(float newWidth, float newHeight)
477 BControl::FrameResized(newWidth, newHeight);
481 void
482 BButton::MakeFocus(bool focus)
484 BControl::MakeFocus(focus);
488 void
489 BButton::AllAttached()
491 BControl::AllAttached();
495 void
496 BButton::AllDetached()
498 BControl::AllDetached();
502 BHandler*
503 BButton::ResolveSpecifier(BMessage* message, int32 index,
504 BMessage* specifier, int32 what, const char* property)
506 return BControl::ResolveSpecifier(message, index, specifier, what,
507 property);
511 status_t
512 BButton::GetSupportedSuites(BMessage* message)
514 return BControl::GetSupportedSuites(message);
518 status_t
519 BButton::Perform(perform_code code, void* _data)
521 switch (code) {
522 case PERFORM_CODE_MIN_SIZE:
523 ((perform_data_min_size*)_data)->return_value
524 = BButton::MinSize();
525 return B_OK;
527 case PERFORM_CODE_MAX_SIZE:
528 ((perform_data_max_size*)_data)->return_value
529 = BButton::MaxSize();
530 return B_OK;
532 case PERFORM_CODE_PREFERRED_SIZE:
533 ((perform_data_preferred_size*)_data)->return_value
534 = BButton::PreferredSize();
535 return B_OK;
537 case PERFORM_CODE_LAYOUT_ALIGNMENT:
538 ((perform_data_layout_alignment*)_data)->return_value
539 = BButton::LayoutAlignment();
540 return B_OK;
542 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
543 ((perform_data_has_height_for_width*)_data)->return_value
544 = BButton::HasHeightForWidth();
545 return B_OK;
547 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
549 perform_data_get_height_for_width* data
550 = (perform_data_get_height_for_width*)_data;
551 BButton::GetHeightForWidth(data->width, &data->min, &data->max,
552 &data->preferred);
553 return B_OK;
556 case PERFORM_CODE_SET_LAYOUT:
558 perform_data_set_layout* data = (perform_data_set_layout*)_data;
559 BButton::SetLayout(data->layout);
560 return B_OK;
563 case PERFORM_CODE_LAYOUT_INVALIDATED:
565 perform_data_layout_invalidated* data
566 = (perform_data_layout_invalidated*)_data;
567 BButton::LayoutInvalidated(data->descendants);
568 return B_OK;
571 case PERFORM_CODE_DO_LAYOUT:
573 BButton::DoLayout();
574 return B_OK;
577 case PERFORM_CODE_SET_ICON:
579 perform_data_set_icon* data = (perform_data_set_icon*)_data;
580 return BButton::SetIcon(data->icon, data->flags);
584 return BControl::Perform(code, _data);
588 BSize
589 BButton::MinSize()
591 return BLayoutUtils::ComposeSize(ExplicitMinSize(),
592 _ValidatePreferredSize());
596 BSize
597 BButton::MaxSize()
599 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
600 _ValidatePreferredSize());
604 BSize
605 BButton::PreferredSize()
607 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
608 _ValidatePreferredSize());
612 status_t
613 BButton::SetIcon(const BBitmap* icon, uint32 flags)
615 return BControl::SetIcon(icon,
616 flags | B_CREATE_ACTIVE_ICON_BITMAP | B_CREATE_DISABLED_ICON_BITMAPS);
620 void
621 BButton::LayoutInvalidated(bool descendants)
623 // invalidate cached preferred size
624 fPreferredSize.Set(-1, -1);
628 void BButton::_ReservedButton1() {}
629 void BButton::_ReservedButton2() {}
630 void BButton::_ReservedButton3() {}
633 BButton &
634 BButton::operator=(const BButton &)
636 return *this;
640 BSize
641 BButton::_ValidatePreferredSize()
643 if (fPreferredSize.width < 0) {
644 BControlLook::background_type backgroundType
645 = fBehavior == B_POP_UP_BEHAVIOR
646 ? BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
647 : BControlLook::B_BUTTON_BACKGROUND;
648 float left, top, right, bottom;
649 be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME, backgroundType,
650 IsDefault() ? BControlLook::B_DEFAULT_BUTTON : 0,
651 left, top, right, bottom);
653 // width
654 float width = left + right + 2 * kLabelMargin - 1;
656 const char* label = Label();
657 if (label != NULL) {
658 width = std::max(width, 20.0f);
659 width += (float)ceil(StringWidth(label));
662 const BBitmap* icon = IconBitmap(B_INACTIVE_ICON_BITMAP);
663 if (icon != NULL)
664 width += icon->Bounds().Width() + 1;
666 if (label != NULL && icon != NULL)
667 width += be_control_look->DefaultLabelSpacing();
669 // height
670 float minHorizontalMargins = top + bottom + 2 * kLabelMargin;
671 float height = -1;
673 if (label != NULL) {
674 font_height fontHeight;
675 GetFontHeight(&fontHeight);
676 float textHeight = fontHeight.ascent + fontHeight.descent;
677 height = ceilf(textHeight * 1.8);
678 float margins = height - ceilf(textHeight);
679 if (margins < minHorizontalMargins)
680 height += minHorizontalMargins - margins;
683 if (icon != NULL) {
684 height = std::max(height,
685 icon->Bounds().Height() + minHorizontalMargins);
688 // force some minimum width/height values
689 width = std::max(width, label != NULL ? 75.0f : 5.0f);
690 height = std::max(height, 5.0f);
692 fPreferredSize.Set(width, height);
694 ResetLayoutInvalidation();
697 return fPreferredSize;
701 BRect
702 BButton::_PopUpRect() const
704 if (fBehavior != B_POP_UP_BEHAVIOR)
705 return BRect();
707 float left, top, right, bottom;
708 be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME,
709 BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND,
710 IsDefault() ? BControlLook::B_DEFAULT_BUTTON : 0,
711 left, top, right, bottom);
713 BRect rect(Bounds());
714 rect.left = rect.right - right + 1;
715 return rect;
719 inline bool
720 BButton::_Flag(uint32 flag) const
722 return (fFlags & flag) != 0;
726 inline bool
727 BButton::_SetFlag(uint32 flag, bool set)
729 if (((fFlags & flag) != 0) == set)
730 return false;
732 if (set)
733 fFlags |= flag;
734 else
735 fFlags &= ~flag;
737 return true;
741 extern "C" void
742 B_IF_GCC_2(InvalidateLayout__7BButtonb, _ZN7BButton16InvalidateLayoutEb)(
743 BView* view, bool descendants)
745 perform_data_layout_invalidated data;
746 data.descendants = descendants;
748 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);