tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / Button.cpp
blobb685d9e6fcfab2a68f4ea706f67d0b730b36e7bd
1 /*
2 * Copyright 2001-2014 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 = LowColor();
137 rgb_color base = background;
138 uint32 flags = be_control_look->Flags(this);
139 if (_Flag(FLAG_DEFAULT))
140 flags |= BControlLook::B_DEFAULT_BUTTON;
141 if (_Flag(FLAG_FLAT) && !IsTracking())
142 flags |= BControlLook::B_FLAT;
143 if (_Flag(FLAG_INSIDE))
144 flags |= BControlLook::B_HOVER;
146 be_control_look->DrawButtonFrame(this, rect, updateRect,
147 base, background, flags);
149 if (fBehavior == B_POP_UP_BEHAVIOR) {
150 be_control_look->DrawButtonWithPopUpBackground(this, rect, updateRect,
151 base, flags);
152 } else {
153 be_control_look->DrawButtonBackground(this, rect, updateRect,
154 base, flags);
157 // always leave some room around the label
158 rect.InsetBy(kLabelMargin, kLabelMargin);
160 const BBitmap* icon = IconBitmap(
161 (Value() == B_CONTROL_OFF
162 ? B_INACTIVE_ICON_BITMAP : B_ACTIVE_ICON_BITMAP)
163 | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP));
164 be_control_look->DrawLabel(this, Label(), icon, rect, updateRect,
165 base, flags, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
169 void
170 BButton::MouseDown(BPoint where)
172 if (!IsEnabled())
173 return;
175 if (fBehavior == B_POP_UP_BEHAVIOR && _PopUpRect().Contains(where)) {
176 InvokeNotify(fPopUpMessage, B_CONTROL_MODIFIED);
177 return;
180 bool toggleBehavior = fBehavior == B_TOGGLE_BEHAVIOR;
182 if (toggleBehavior) {
183 bool wasPressed = Value() == B_CONTROL_ON;
184 _SetFlag(FLAG_WAS_PRESSED, wasPressed);
185 SetValue(wasPressed ? B_CONTROL_OFF : B_CONTROL_ON);
186 Invalidate();
187 } else
188 SetValue(B_CONTROL_ON);
190 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
191 SetTracking(true);
192 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
193 } else {
194 BRect bounds = Bounds();
195 uint32 buttons;
196 bool inside = false;
198 do {
199 Window()->UpdateIfNeeded();
200 snooze(40000);
202 GetMouse(&where, &buttons, true);
203 inside = bounds.Contains(where);
205 if (toggleBehavior) {
206 bool pressed = inside ^ _Flag(FLAG_WAS_PRESSED);
207 SetValue(pressed ? B_CONTROL_ON : B_CONTROL_OFF);
208 } else {
209 if ((Value() == B_CONTROL_ON) != inside)
210 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
212 } while (buttons != 0);
214 if (inside) {
215 if (toggleBehavior) {
216 SetValue(
217 _Flag(FLAG_WAS_PRESSED) ? B_CONTROL_OFF : B_CONTROL_ON);
220 Invoke();
221 } else if (_Flag(FLAG_FLAT))
222 Invalidate();
227 void
228 BButton::AttachedToWindow()
230 BControl::AttachedToWindow();
231 // low color will now be the parents view color
233 if (IsDefault())
234 Window()->SetDefaultButton(this);
236 SetViewColor(B_TRANSPARENT_COLOR);
240 void
241 BButton::KeyDown(const char* bytes, int32 numBytes)
243 if (*bytes == B_ENTER || *bytes == B_SPACE) {
244 if (!IsEnabled())
245 return;
247 SetValue(B_CONTROL_ON);
249 // make sure the user saw that
250 Window()->UpdateIfNeeded();
251 snooze(25000);
253 Invoke();
254 } else
255 BControl::KeyDown(bytes, numBytes);
259 void
260 BButton::MakeDefault(bool flag)
262 BButton* oldDefault = NULL;
263 BWindow* window = Window();
265 if (window != NULL)
266 oldDefault = window->DefaultButton();
268 if (flag) {
269 if (_Flag(FLAG_DEFAULT) && oldDefault == this)
270 return;
272 if (_SetFlag(FLAG_DEFAULT, true)) {
273 if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
274 InvalidateLayout();
275 else {
276 ResizeBy(6.0f, 6.0f);
277 MoveBy(-3.0f, -3.0f);
281 if (window && oldDefault != this)
282 window->SetDefaultButton(this);
283 } else {
284 if (!_SetFlag(FLAG_DEFAULT, false))
285 return;
287 if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
288 InvalidateLayout();
289 else {
290 ResizeBy(-6.0f, -6.0f);
291 MoveBy(3.0f, 3.0f);
294 if (window && oldDefault == this)
295 window->SetDefaultButton(NULL);
300 void
301 BButton::SetLabel(const char* label)
303 BControl::SetLabel(label);
307 bool
308 BButton::IsDefault() const
310 return _Flag(FLAG_DEFAULT);
314 bool
315 BButton::IsFlat() const
317 return _Flag(FLAG_FLAT);
321 void
322 BButton::SetFlat(bool flat)
324 if (_SetFlag(FLAG_FLAT, flat))
325 Invalidate();
329 BButton::BBehavior
330 BButton::Behavior() const
332 return fBehavior;
336 void
337 BButton::SetBehavior(BBehavior behavior)
339 if (behavior != fBehavior) {
340 fBehavior = behavior;
341 InvalidateLayout();
342 Invalidate();
347 BMessage*
348 BButton::PopUpMessage() const
350 return fPopUpMessage;
354 void
355 BButton::SetPopUpMessage(BMessage* message)
357 delete fPopUpMessage;
358 fPopUpMessage = message;
362 void
363 BButton::MessageReceived(BMessage* message)
365 BControl::MessageReceived(message);
369 void
370 BButton::WindowActivated(bool active)
372 BControl::WindowActivated(active);
376 void
377 BButton::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
379 bool inside = (code != B_EXITED_VIEW) && Bounds().Contains(where);
380 if (_SetFlag(FLAG_INSIDE, inside))
381 Invalidate();
383 if (!IsTracking())
384 return;
386 if (fBehavior == B_TOGGLE_BEHAVIOR) {
387 bool pressed = inside ^ _Flag(FLAG_WAS_PRESSED);
388 SetValue(pressed ? B_CONTROL_ON : B_CONTROL_OFF);
389 } else {
390 if ((Value() == B_CONTROL_ON) != inside)
391 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
396 void
397 BButton::MouseUp(BPoint where)
399 if (!IsTracking())
400 return;
402 if (Bounds().Contains(where)) {
403 if (fBehavior == B_TOGGLE_BEHAVIOR)
404 SetValue(_Flag(FLAG_WAS_PRESSED) ? B_CONTROL_OFF : B_CONTROL_ON);
406 Invoke();
407 } else if (_Flag(FLAG_FLAT))
408 Invalidate();
410 SetTracking(false);
414 void
415 BButton::DetachedFromWindow()
417 BControl::DetachedFromWindow();
421 void
422 BButton::SetValue(int32 value)
424 if (value != Value())
425 BControl::SetValue(value);
429 void
430 BButton::GetPreferredSize(float* _width, float* _height)
432 _ValidatePreferredSize();
434 if (_width)
435 *_width = fPreferredSize.width;
437 if (_height)
438 *_height = fPreferredSize.height;
442 void
443 BButton::ResizeToPreferred()
445 BControl::ResizeToPreferred();
449 status_t
450 BButton::Invoke(BMessage* message)
452 Sync();
453 snooze(50000);
455 status_t err = BControl::Invoke(message);
457 if (fBehavior != B_TOGGLE_BEHAVIOR)
458 SetValue(B_CONTROL_OFF);
460 return err;
464 void
465 BButton::FrameMoved(BPoint newPosition)
467 BControl::FrameMoved(newPosition);
471 void
472 BButton::FrameResized(float newWidth, float newHeight)
474 BControl::FrameResized(newWidth, newHeight);
478 void
479 BButton::MakeFocus(bool focus)
481 BControl::MakeFocus(focus);
485 void
486 BButton::AllAttached()
488 BControl::AllAttached();
492 void
493 BButton::AllDetached()
495 BControl::AllDetached();
499 BHandler*
500 BButton::ResolveSpecifier(BMessage* message, int32 index,
501 BMessage* specifier, int32 what, const char* property)
503 return BControl::ResolveSpecifier(message, index, specifier, what,
504 property);
508 status_t
509 BButton::GetSupportedSuites(BMessage* message)
511 return BControl::GetSupportedSuites(message);
515 status_t
516 BButton::Perform(perform_code code, void* _data)
518 switch (code) {
519 case PERFORM_CODE_MIN_SIZE:
520 ((perform_data_min_size*)_data)->return_value
521 = BButton::MinSize();
522 return B_OK;
524 case PERFORM_CODE_MAX_SIZE:
525 ((perform_data_max_size*)_data)->return_value
526 = BButton::MaxSize();
527 return B_OK;
529 case PERFORM_CODE_PREFERRED_SIZE:
530 ((perform_data_preferred_size*)_data)->return_value
531 = BButton::PreferredSize();
532 return B_OK;
534 case PERFORM_CODE_LAYOUT_ALIGNMENT:
535 ((perform_data_layout_alignment*)_data)->return_value
536 = BButton::LayoutAlignment();
537 return B_OK;
539 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
540 ((perform_data_has_height_for_width*)_data)->return_value
541 = BButton::HasHeightForWidth();
542 return B_OK;
544 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
546 perform_data_get_height_for_width* data
547 = (perform_data_get_height_for_width*)_data;
548 BButton::GetHeightForWidth(data->width, &data->min, &data->max,
549 &data->preferred);
550 return B_OK;
553 case PERFORM_CODE_SET_LAYOUT:
555 perform_data_set_layout* data = (perform_data_set_layout*)_data;
556 BButton::SetLayout(data->layout);
557 return B_OK;
560 case PERFORM_CODE_LAYOUT_INVALIDATED:
562 perform_data_layout_invalidated* data
563 = (perform_data_layout_invalidated*)_data;
564 BButton::LayoutInvalidated(data->descendants);
565 return B_OK;
568 case PERFORM_CODE_DO_LAYOUT:
570 BButton::DoLayout();
571 return B_OK;
574 case PERFORM_CODE_SET_ICON:
576 perform_data_set_icon* data = (perform_data_set_icon*)_data;
577 return BButton::SetIcon(data->icon, data->flags);
581 return BControl::Perform(code, _data);
585 BSize
586 BButton::MinSize()
588 return BLayoutUtils::ComposeSize(ExplicitMinSize(),
589 _ValidatePreferredSize());
593 BSize
594 BButton::MaxSize()
596 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
597 _ValidatePreferredSize());
601 BSize
602 BButton::PreferredSize()
604 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
605 _ValidatePreferredSize());
609 status_t
610 BButton::SetIcon(const BBitmap* icon, uint32 flags)
612 return BControl::SetIcon(icon,
613 flags | B_CREATE_ACTIVE_ICON_BITMAP | B_CREATE_DISABLED_ICON_BITMAPS);
617 void
618 BButton::LayoutInvalidated(bool descendants)
620 // invalidate cached preferred size
621 fPreferredSize.Set(-1, -1);
625 void BButton::_ReservedButton1() {}
626 void BButton::_ReservedButton2() {}
627 void BButton::_ReservedButton3() {}
630 BButton &
631 BButton::operator=(const BButton &)
633 return *this;
637 BSize
638 BButton::_ValidatePreferredSize()
640 if (fPreferredSize.width < 0) {
641 BControlLook::background_type backgroundType
642 = fBehavior == B_POP_UP_BEHAVIOR
643 ? BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
644 : BControlLook::B_BUTTON_BACKGROUND;
645 float left, top, right, bottom;
646 be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME, backgroundType,
647 IsDefault() ? BControlLook::B_DEFAULT_BUTTON : 0,
648 left, top, right, bottom);
650 // width
651 float width = left + right + 2 * kLabelMargin - 1;
653 const char* label = Label();
654 if (label != NULL) {
655 width = std::max(width, 20.0f);
656 width += (float)ceil(StringWidth(label));
659 const BBitmap* icon = IconBitmap(B_INACTIVE_ICON_BITMAP);
660 if (icon != NULL)
661 width += icon->Bounds().Width() + 1;
663 if (label != NULL && icon != NULL)
664 width += be_control_look->DefaultLabelSpacing();
666 // height
667 float minHorizontalMargins = top + bottom + 2 * kLabelMargin;
668 float height = -1;
670 if (label != NULL) {
671 font_height fontHeight;
672 GetFontHeight(&fontHeight);
673 float textHeight = fontHeight.ascent + fontHeight.descent;
674 height = ceilf(textHeight * 1.8);
675 float margins = height - ceilf(textHeight);
676 if (margins < minHorizontalMargins)
677 height += minHorizontalMargins - margins;
680 if (icon != NULL) {
681 height = std::max(height,
682 icon->Bounds().Height() + minHorizontalMargins);
685 // force some minimum width/height values
686 width = std::max(width, label != NULL ? 75.0f : 5.0f);
687 height = std::max(height, 5.0f);
689 fPreferredSize.Set(width, height);
691 ResetLayoutInvalidation();
694 return fPreferredSize;
698 BRect
699 BButton::_PopUpRect() const
701 if (fBehavior != B_POP_UP_BEHAVIOR)
702 return BRect();
704 float left, top, right, bottom;
705 be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME,
706 BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND,
707 IsDefault() ? BControlLook::B_DEFAULT_BUTTON : 0,
708 left, top, right, bottom);
710 BRect rect(Bounds());
711 rect.left = rect.right - right + 1;
712 return rect;
716 inline bool
717 BButton::_Flag(uint32 flag) const
719 return (fFlags & flag) != 0;
723 inline bool
724 BButton::_SetFlag(uint32 flag, bool set)
726 if (((fFlags & flag) != 0) == set)
727 return false;
729 if (set)
730 fFlags |= flag;
731 else
732 fFlags &= ~flag;
734 return true;
738 extern "C" void
739 B_IF_GCC_2(InvalidateLayout__7BButtonb, _ZN7BButton16InvalidateLayoutEb)(
740 BView* view, bool descendants)
742 perform_data_layout_invalidated data;
743 data.descendants = descendants;
745 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);