1 /*
2 * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Stephan Aßmus <superstippi@gmx.de>
8 */
9
10
11 // BCheckBox displays an on/off control.
12
13
14 #include <CheckBox.h>
15
16 #include <algorithm>
17 #include <new>
18
19 #include <Bitmap.h>
20 #include <ControlLook.h>
21 #include <LayoutUtils.h>
22 #include <Window.h>
23
24 #include <binary_compatibility/Interface.h>
25
26
BCheckBox(BRect frame,const char * name,const char * label,BMessage * message,uint32 resizingMode,uint32 flags)27 BCheckBox::BCheckBox(BRect frame, const char* name, const char* label,
28 BMessage* message, uint32 resizingMode, uint32 flags)
29 :
30 BControl(frame, name, label, message, resizingMode, flags),
31 fPreferredSize(),
32 fOutlined(false),
33 fPartialToOff(false)
34 {
35 // Resize to minimum height if needed
36 font_height fontHeight;
37 GetFontHeight(&fontHeight);
38 float minHeight = (float)ceil(6.0f + fontHeight.ascent
39 + fontHeight.descent);
40 if (Bounds().Height() < minHeight)
41 ResizeTo(Bounds().Width(), minHeight);
42 }
43
44
BCheckBox(const char * name,const char * label,BMessage * message,uint32 flags)45 BCheckBox::BCheckBox(const char* name, const char* label, BMessage* message,
46 uint32 flags)
47 :
48 BControl(name, label, message, flags | B_WILL_DRAW | B_NAVIGABLE),
49 fPreferredSize(),
50 fOutlined(false),
51 fPartialToOff(false)
52 {
53 }
54
55
BCheckBox(const char * label,BMessage * message)56 BCheckBox::BCheckBox(const char* label, BMessage* message)
57 :
58 BControl(NULL, label, message, B_WILL_DRAW | B_NAVIGABLE),
59 fPreferredSize(),
60 fOutlined(false),
61 fPartialToOff(false)
62 {
63 }
64
65
BCheckBox(BMessage * data)66 BCheckBox::BCheckBox(BMessage* data)
67 :
68 BControl(data),
69 fOutlined(false),
70 fPartialToOff(false)
71 {
72 }
73
74
~BCheckBox()75 BCheckBox::~BCheckBox()
76 {
77 }
78
79
80 // #pragma mark - Archiving methods
81
82
83 BArchivable*
Instantiate(BMessage * data)84 BCheckBox::Instantiate(BMessage* data)
85 {
86 if (validate_instantiation(data, "BCheckBox"))
87 return new(std::nothrow) BCheckBox(data);
88
89 return NULL;
90 }
91
92
93 status_t
Archive(BMessage * data,bool deep) const94 BCheckBox::Archive(BMessage* data, bool deep) const
95 {
96 return BControl::Archive(data, deep);
97 }
98
99
100 // #pragma mark - Hook methods
101
102
103 void
Draw(BRect updateRect)104 BCheckBox::Draw(BRect updateRect)
105 {
106 rgb_color base = ViewColor();
107
108 uint32 flags = be_control_look->Flags(this);
109 if (fOutlined)
110 flags |= BControlLook::B_CLICKED;
111
112 BRect checkBoxRect(_CheckBoxFrame());
113 BRect rect(checkBoxRect);
114 be_control_look->DrawCheckBox(this, rect, updateRect, base, flags);
115
116 BRect labelRect(Bounds());
117 labelRect.left = checkBoxRect.right + 1
118 + be_control_look->DefaultLabelSpacing();
119
120 const BBitmap* icon = IconBitmap(
121 B_INACTIVE_ICON_BITMAP | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP));
122 const BAlignment alignment = BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER);
123
124 be_control_look->DrawLabel(this, Label(), icon, labelRect, updateRect, base, flags, alignment);
125 }
126
127
128 void
AttachedToWindow()129 BCheckBox::AttachedToWindow()
130 {
131 BControl::AttachedToWindow();
132 }
133
134
135 void
DetachedFromWindow()136 BCheckBox::DetachedFromWindow()
137 {
138 BControl::DetachedFromWindow();
139 }
140
141
142 void
AllAttached()143 BCheckBox::AllAttached()
144 {
145 BControl::AllAttached();
146 }
147
148
149 void
AllDetached()150 BCheckBox::AllDetached()
151 {
152 BControl::AllDetached();
153 }
154
155
156 void
FrameMoved(BPoint newPosition)157 BCheckBox::FrameMoved(BPoint newPosition)
158 {
159 BControl::FrameMoved(newPosition);
160 }
161
162
163 void
FrameResized(float newWidth,float newHeight)164 BCheckBox::FrameResized(float newWidth, float newHeight)
165 {
166 BControl::FrameResized(newWidth, newHeight);
167 }
168
169
170 void
WindowActivated(bool active)171 BCheckBox::WindowActivated(bool active)
172 {
173 BControl::WindowActivated(active);
174 }
175
176
177 void
MessageReceived(BMessage * message)178 BCheckBox::MessageReceived(BMessage* message)
179 {
180 BControl::MessageReceived(message);
181 }
182
183
184 void
KeyDown(const char * bytes,int32 numBytes)185 BCheckBox::KeyDown(const char* bytes, int32 numBytes)
186 {
187 if (*bytes == B_ENTER || *bytes == B_SPACE) {
188 if (!IsEnabled())
189 return;
190
191 SetValue(_NextState());
192 Invoke();
193 } else {
194 // skip the BControl implementation
195 BView::KeyDown(bytes, numBytes);
196 }
197 }
198
199
200 void
MouseDown(BPoint where)201 BCheckBox::MouseDown(BPoint where)
202 {
203 if (!IsEnabled())
204 return;
205
206 fOutlined = true;
207
208 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
209 Invalidate();
210 SetTracking(true);
211 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
212 } else {
213 BRect bounds = Bounds();
214 uint32 buttons;
215
216 Invalidate();
217 Window()->UpdateIfNeeded();
218
219 do {
220 snooze(40000);
221
222 GetMouse(&where, &buttons, true);
223
224 bool inside = bounds.Contains(where);
225 if (fOutlined != inside) {
226 fOutlined = inside;
227 Invalidate();
228 Window()->UpdateIfNeeded();
229 }
230 } while (buttons != 0);
231
232 if (fOutlined) {
233 fOutlined = false;
234 SetValue(_NextState());
235 Invoke();
236 } else {
237 Invalidate();
238 Window()->UpdateIfNeeded();
239 }
240 }
241 }
242
243
244 void
MouseUp(BPoint where)245 BCheckBox::MouseUp(BPoint where)
246 {
247 if (!IsTracking())
248 return;
249
250 bool inside = Bounds().Contains(where);
251
252 if (fOutlined != inside) {
253 fOutlined = inside;
254 Invalidate();
255 }
256
257 if (fOutlined) {
258 fOutlined = false;
259 SetValue(_NextState());
260 Invoke();
261 } else {
262 Invalidate();
263 }
264
265 SetTracking(false);
266 }
267
268
269 void
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)270 BCheckBox::MouseMoved(BPoint where, uint32 code,
271 const BMessage* dragMessage)
272 {
273 if (!IsTracking())
274 return;
275
276 bool inside = Bounds().Contains(where);
277
278 if (fOutlined != inside) {
279 fOutlined = inside;
280 Invalidate();
281 }
282 }
283
284
285 // #pragma mark -
286
287
288 void
GetPreferredSize(float * _width,float * _height)289 BCheckBox::GetPreferredSize(float* _width, float* _height)
290 {
291 _ValidatePreferredSize();
292
293 if (_width)
294 *_width = fPreferredSize.width;
295
296 if (_height)
297 *_height = fPreferredSize.height;
298 }
299
300
301 void
ResizeToPreferred()302 BCheckBox::ResizeToPreferred()
303 {
304 BControl::ResizeToPreferred();
305 }
306
307
308 BSize
MinSize()309 BCheckBox::MinSize()
310 {
311 return BLayoutUtils::ComposeSize(ExplicitMinSize(),
312 _ValidatePreferredSize());
313 }
314
315
316 BSize
MaxSize()317 BCheckBox::MaxSize()
318 {
319 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
320 _ValidatePreferredSize());
321 }
322
323
324 BSize
PreferredSize()325 BCheckBox::PreferredSize()
326 {
327 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
328 _ValidatePreferredSize());
329 }
330
331
332 BAlignment
LayoutAlignment()333 BCheckBox::LayoutAlignment()
334 {
335 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
336 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
337 }
338
339
340 // #pragma mark -
341
342
343 void
MakeFocus(bool focused)344 BCheckBox::MakeFocus(bool focused)
345 {
346 BControl::MakeFocus(focused);
347 }
348
349
350 void
SetValue(int32 value)351 BCheckBox::SetValue(int32 value)
352 {
353 // We only accept three possible values.
354 switch (value) {
355 case B_CONTROL_OFF:
356 case B_CONTROL_ON:
357 case B_CONTROL_PARTIALLY_ON:
358 break;
359 default:
360 value = B_CONTROL_ON;
361 break;
362 }
363
364 if (value != Value()) {
365 BControl::SetValueNoUpdate(value);
366 Invalidate(_CheckBoxFrame());
367 }
368 }
369
370
371 status_t
Invoke(BMessage * message)372 BCheckBox::Invoke(BMessage* message)
373 {
374 return BControl::Invoke(message);
375 }
376
377
378 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 what,const char * property)379 BCheckBox::ResolveSpecifier(BMessage* message, int32 index,
380 BMessage* specifier, int32 what, const char* property)
381 {
382 return BControl::ResolveSpecifier(message, index, specifier, what,
383 property);
384 }
385
386
387 status_t
GetSupportedSuites(BMessage * message)388 BCheckBox::GetSupportedSuites(BMessage* message)
389 {
390 return BControl::GetSupportedSuites(message);
391 }
392
393
394 status_t
Perform(perform_code code,void * _data)395 BCheckBox::Perform(perform_code code, void* _data)
396 {
397 switch (code) {
398 case PERFORM_CODE_MIN_SIZE:
399 ((perform_data_min_size*)_data)->return_value
400 = BCheckBox::MinSize();
401 return B_OK;
402 case PERFORM_CODE_MAX_SIZE:
403 ((perform_data_max_size*)_data)->return_value
404 = BCheckBox::MaxSize();
405 return B_OK;
406 case PERFORM_CODE_PREFERRED_SIZE:
407 ((perform_data_preferred_size*)_data)->return_value
408 = BCheckBox::PreferredSize();
409 return B_OK;
410 case PERFORM_CODE_LAYOUT_ALIGNMENT:
411 ((perform_data_layout_alignment*)_data)->return_value
412 = BCheckBox::LayoutAlignment();
413 return B_OK;
414 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
415 ((perform_data_has_height_for_width*)_data)->return_value
416 = BCheckBox::HasHeightForWidth();
417 return B_OK;
418 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
419 {
420 perform_data_get_height_for_width* data
421 = (perform_data_get_height_for_width*)_data;
422 BCheckBox::GetHeightForWidth(data->width, &data->min, &data->max,
423 &data->preferred);
424 return B_OK;
425 }
426 case PERFORM_CODE_SET_LAYOUT:
427 {
428 perform_data_set_layout* data = (perform_data_set_layout*)_data;
429 BCheckBox::SetLayout(data->layout);
430 return B_OK;
431 }
432 case PERFORM_CODE_LAYOUT_INVALIDATED:
433 {
434 perform_data_layout_invalidated* data
435 = (perform_data_layout_invalidated*)_data;
436 BCheckBox::LayoutInvalidated(data->descendants);
437 return B_OK;
438 }
439 case PERFORM_CODE_DO_LAYOUT:
440 {
441 BCheckBox::DoLayout();
442 return B_OK;
443 }
444 case PERFORM_CODE_SET_ICON:
445 {
446 perform_data_set_icon* data = (perform_data_set_icon*)_data;
447 return BCheckBox::SetIcon(data->icon, data->flags);
448 }
449 }
450
451 return BControl::Perform(code, _data);
452 }
453
454
455 status_t
SetIcon(const BBitmap * icon,uint32 flags)456 BCheckBox::SetIcon(const BBitmap* icon, uint32 flags)
457 {
458 return BControl::SetIcon(icon, flags | B_CREATE_DISABLED_ICON_BITMAPS);
459 }
460
461
462 void
LayoutInvalidated(bool descendants)463 BCheckBox::LayoutInvalidated(bool descendants)
464 {
465 // invalidate cached preferred size
466 fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET);
467 }
468
469
470 bool
IsPartialStateToOff() const471 BCheckBox::IsPartialStateToOff() const
472 {
473 return fPartialToOff;
474 }
475
476
477 void
SetPartialStateToOff(bool partialToOff)478 BCheckBox::SetPartialStateToOff(bool partialToOff)
479 {
480 fPartialToOff = partialToOff;
481 }
482
483
484 // #pragma mark - FBC padding
485
486
_ReservedCheckBox1()487 void BCheckBox::_ReservedCheckBox1() {}
_ReservedCheckBox2()488 void BCheckBox::_ReservedCheckBox2() {}
_ReservedCheckBox3()489 void BCheckBox::_ReservedCheckBox3() {}
490
491
492 BRect
_CheckBoxFrame(const font_height & fontHeight) const493 BCheckBox::_CheckBoxFrame(const font_height& fontHeight) const
494 {
495 return BRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent),
496 ceilf(5.0f + fontHeight.ascent));
497 }
498
499
500 BRect
_CheckBoxFrame() const501 BCheckBox::_CheckBoxFrame() const
502 {
503 font_height fontHeight;
504 GetFontHeight(&fontHeight);
505 return _CheckBoxFrame(fontHeight);
506 }
507
508
509 BSize
_ValidatePreferredSize()510 BCheckBox::_ValidatePreferredSize()
511 {
512 if (!fPreferredSize.IsWidthSet()) {
513 font_height fontHeight;
514 GetFontHeight(&fontHeight);
515
516 BRect rect(_CheckBoxFrame(fontHeight));
517 float width = rect.right + rect.left;
518 float height = rect.bottom + rect.top;
519
520 const BBitmap* icon = IconBitmap(B_INACTIVE_ICON_BITMAP);
521 if (icon != NULL) {
522 width += be_control_look->DefaultLabelSpacing()
523 + icon->Bounds().Width() + 1;
524 height = std::max(height, icon->Bounds().Height());
525 }
526
527 if (const char* label = Label()) {
528 width += be_control_look->DefaultLabelSpacing()
529 + ceilf(StringWidth(label));
530 height = std::max(height,
531 ceilf(6.0f + fontHeight.ascent + fontHeight.descent));
532 }
533
534 fPreferredSize.Set(width, height);
535
536 ResetLayoutInvalidation();
537 }
538
539 return fPreferredSize;
540 }
541
542
543 int32
_NextState() const544 BCheckBox::_NextState() const
545 {
546 switch (Value()) {
547 case B_CONTROL_OFF:
548 return B_CONTROL_ON;
549 case B_CONTROL_PARTIALLY_ON:
550 return fPartialToOff ? B_CONTROL_OFF : B_CONTROL_ON;
551 case B_CONTROL_ON:
552 default:
553 return B_CONTROL_OFF;
554 }
555 }
556
557
558 BCheckBox &
operator =(const BCheckBox &)559 BCheckBox::operator=(const BCheckBox &)
560 {
561 return *this;
562 }
563
564
565 extern "C" void
B_IF_GCC_2(InvalidateLayout__9BCheckBoxb,_ZN9BCheckBox16InvalidateLayoutEb)566 B_IF_GCC_2(InvalidateLayout__9BCheckBoxb, _ZN9BCheckBox16InvalidateLayoutEb)(
567 BCheckBox* box, bool descendants)
568 {
569 perform_data_layout_invalidated data;
570 data.descendants = descendants;
571
572 box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
573 }
574
575