xref: /haiku/src/kits/interface/CheckBox.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 
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 
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 
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 
66 BCheckBox::BCheckBox(BMessage* data)
67 	:
68 	BControl(data),
69 	fOutlined(false),
70 	fPartialToOff(false)
71 {
72 }
73 
74 
75 BCheckBox::~BCheckBox()
76 {
77 }
78 
79 
80 // #pragma mark - Archiving methods
81 
82 
83 BArchivable*
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
94 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
104 BCheckBox::Draw(BRect updateRect)
105 {
106 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
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 	// erase the is control flag before drawing the label so that the label
117 	// will get drawn using B_PANEL_TEXT_COLOR
118 	flags &= ~BControlLook::B_IS_CONTROL;
119 
120 	BRect labelRect(Bounds());
121 	labelRect.left = checkBoxRect.right + 1
122 		+ be_control_look->DefaultLabelSpacing();
123 
124 	const BBitmap* icon = IconBitmap(
125 		B_INACTIVE_ICON_BITMAP | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP));
126 
127 	be_control_look->DrawLabel(this, Label(), icon, labelRect, updateRect,
128 		base, flags);
129 }
130 
131 
132 void
133 BCheckBox::AttachedToWindow()
134 {
135 	BControl::AttachedToWindow();
136 }
137 
138 
139 void
140 BCheckBox::DetachedFromWindow()
141 {
142 	BControl::DetachedFromWindow();
143 }
144 
145 
146 void
147 BCheckBox::AllAttached()
148 {
149 	BControl::AllAttached();
150 }
151 
152 
153 void
154 BCheckBox::AllDetached()
155 {
156 	BControl::AllDetached();
157 }
158 
159 
160 void
161 BCheckBox::FrameMoved(BPoint newPosition)
162 {
163 	BControl::FrameMoved(newPosition);
164 }
165 
166 
167 void
168 BCheckBox::FrameResized(float newWidth, float newHeight)
169 {
170 	BControl::FrameResized(newWidth, newHeight);
171 }
172 
173 
174 void
175 BCheckBox::WindowActivated(bool active)
176 {
177 	BControl::WindowActivated(active);
178 }
179 
180 
181 void
182 BCheckBox::MessageReceived(BMessage* message)
183 {
184 	BControl::MessageReceived(message);
185 }
186 
187 
188 void
189 BCheckBox::KeyDown(const char* bytes, int32 numBytes)
190 {
191 	if (*bytes == B_ENTER || *bytes == B_SPACE) {
192 		if (!IsEnabled())
193 			return;
194 
195 		SetValue(_NextState());
196 		Invoke();
197 	} else {
198 		// skip the BControl implementation
199 		BView::KeyDown(bytes, numBytes);
200 	}
201 }
202 
203 
204 void
205 BCheckBox::MouseDown(BPoint where)
206 {
207 	if (!IsEnabled())
208 		return;
209 
210 	fOutlined = true;
211 
212 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
213 		Invalidate();
214 		SetTracking(true);
215 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
216 	} else {
217 		BRect bounds = Bounds();
218 		uint32 buttons;
219 
220 		Invalidate();
221 		Window()->UpdateIfNeeded();
222 
223 		do {
224 			snooze(40000);
225 
226 			GetMouse(&where, &buttons, true);
227 
228 			bool inside = bounds.Contains(where);
229 			if (fOutlined != inside) {
230 				fOutlined = inside;
231 				Invalidate();
232 				Window()->UpdateIfNeeded();
233 			}
234 		} while (buttons != 0);
235 
236 		if (fOutlined) {
237 			fOutlined = false;
238 			SetValue(_NextState());
239 			Invoke();
240 		} else {
241 			Invalidate();
242 			Window()->UpdateIfNeeded();
243 		}
244 	}
245 }
246 
247 
248 void
249 BCheckBox::MouseUp(BPoint where)
250 {
251 	if (!IsTracking())
252 		return;
253 
254 	bool inside = Bounds().Contains(where);
255 
256 	if (fOutlined != inside) {
257 		fOutlined = inside;
258 		Invalidate();
259 	}
260 
261 	if (fOutlined) {
262 		fOutlined = false;
263 		SetValue(_NextState());
264 		Invoke();
265 	} else {
266 		Invalidate();
267 	}
268 
269 	SetTracking(false);
270 }
271 
272 
273 void
274 BCheckBox::MouseMoved(BPoint where, uint32 code,
275 	const BMessage* dragMessage)
276 {
277 	if (!IsTracking())
278 		return;
279 
280 	bool inside = Bounds().Contains(where);
281 
282 	if (fOutlined != inside) {
283 		fOutlined = inside;
284 		Invalidate();
285 	}
286 }
287 
288 
289 // #pragma mark -
290 
291 
292 void
293 BCheckBox::GetPreferredSize(float* _width, float* _height)
294 {
295 	_ValidatePreferredSize();
296 
297 	if (_width)
298 		*_width = fPreferredSize.width;
299 
300 	if (_height)
301 		*_height = fPreferredSize.height;
302 }
303 
304 
305 void
306 BCheckBox::ResizeToPreferred()
307 {
308 	BControl::ResizeToPreferred();
309 }
310 
311 
312 BSize
313 BCheckBox::MinSize()
314 {
315 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
316 		_ValidatePreferredSize());
317 }
318 
319 
320 BSize
321 BCheckBox::MaxSize()
322 {
323 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
324 		_ValidatePreferredSize());
325 }
326 
327 
328 BSize
329 BCheckBox::PreferredSize()
330 {
331 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
332 		_ValidatePreferredSize());
333 }
334 
335 
336 BAlignment
337 BCheckBox::LayoutAlignment()
338 {
339 	return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
340 		BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
341 }
342 
343 
344 // #pragma mark -
345 
346 
347 void
348 BCheckBox::MakeFocus(bool focused)
349 {
350 	BControl::MakeFocus(focused);
351 }
352 
353 
354 void
355 BCheckBox::SetValue(int32 value)
356 {
357 	// We only accept three possible values.
358 	switch (value) {
359 		case B_CONTROL_OFF:
360 		case B_CONTROL_ON:
361 		case B_CONTROL_PARTIALLY_ON:
362 			break;
363 		default:
364 			value = B_CONTROL_ON;
365 			break;
366 	}
367 
368 	if (value != Value()) {
369 		BControl::SetValueNoUpdate(value);
370 		Invalidate(_CheckBoxFrame());
371 	}
372 }
373 
374 
375 status_t
376 BCheckBox::Invoke(BMessage* message)
377 {
378 	return BControl::Invoke(message);
379 }
380 
381 
382 BHandler*
383 BCheckBox::ResolveSpecifier(BMessage* message, int32 index,
384 	BMessage* specifier, int32 what, const char* property)
385 {
386 	return BControl::ResolveSpecifier(message, index, specifier, what,
387 		property);
388 }
389 
390 
391 status_t
392 BCheckBox::GetSupportedSuites(BMessage* message)
393 {
394 	return BControl::GetSupportedSuites(message);
395 }
396 
397 
398 status_t
399 BCheckBox::Perform(perform_code code, void* _data)
400 {
401 	switch (code) {
402 		case PERFORM_CODE_MIN_SIZE:
403 			((perform_data_min_size*)_data)->return_value
404 				= BCheckBox::MinSize();
405 			return B_OK;
406 		case PERFORM_CODE_MAX_SIZE:
407 			((perform_data_max_size*)_data)->return_value
408 				= BCheckBox::MaxSize();
409 			return B_OK;
410 		case PERFORM_CODE_PREFERRED_SIZE:
411 			((perform_data_preferred_size*)_data)->return_value
412 				= BCheckBox::PreferredSize();
413 			return B_OK;
414 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
415 			((perform_data_layout_alignment*)_data)->return_value
416 				= BCheckBox::LayoutAlignment();
417 			return B_OK;
418 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
419 			((perform_data_has_height_for_width*)_data)->return_value
420 				= BCheckBox::HasHeightForWidth();
421 			return B_OK;
422 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
423 		{
424 			perform_data_get_height_for_width* data
425 				= (perform_data_get_height_for_width*)_data;
426 			BCheckBox::GetHeightForWidth(data->width, &data->min, &data->max,
427 				&data->preferred);
428 			return B_OK;
429 		}
430 		case PERFORM_CODE_SET_LAYOUT:
431 		{
432 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
433 			BCheckBox::SetLayout(data->layout);
434 			return B_OK;
435 		}
436 		case PERFORM_CODE_LAYOUT_INVALIDATED:
437 		{
438 			perform_data_layout_invalidated* data
439 				= (perform_data_layout_invalidated*)_data;
440 			BCheckBox::LayoutInvalidated(data->descendants);
441 			return B_OK;
442 		}
443 		case PERFORM_CODE_DO_LAYOUT:
444 		{
445 			BCheckBox::DoLayout();
446 			return B_OK;
447 		}
448 		case PERFORM_CODE_SET_ICON:
449 		{
450 			perform_data_set_icon* data = (perform_data_set_icon*)_data;
451 			return BCheckBox::SetIcon(data->icon, data->flags);
452 		}
453 	}
454 
455 	return BControl::Perform(code, _data);
456 }
457 
458 
459 status_t
460 BCheckBox::SetIcon(const BBitmap* icon, uint32 flags)
461 {
462 	return BControl::SetIcon(icon, flags | B_CREATE_DISABLED_ICON_BITMAPS);
463 }
464 
465 
466 void
467 BCheckBox::LayoutInvalidated(bool descendants)
468 {
469 	// invalidate cached preferred size
470 	fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET);
471 }
472 
473 
474 bool
475 BCheckBox::IsPartialStateToOff() const
476 {
477 	return fPartialToOff;
478 }
479 
480 
481 void
482 BCheckBox::SetPartialStateToOff(bool partialToOff)
483 {
484 	fPartialToOff = partialToOff;
485 }
486 
487 
488 // #pragma mark - FBC padding
489 
490 
491 void BCheckBox::_ReservedCheckBox1() {}
492 void BCheckBox::_ReservedCheckBox2() {}
493 void BCheckBox::_ReservedCheckBox3() {}
494 
495 
496 BRect
497 BCheckBox::_CheckBoxFrame(const font_height& fontHeight) const
498 {
499 	return BRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent),
500 		ceilf(5.0f + fontHeight.ascent));
501 }
502 
503 
504 BRect
505 BCheckBox::_CheckBoxFrame() const
506 {
507 	font_height fontHeight;
508 	GetFontHeight(&fontHeight);
509 	return _CheckBoxFrame(fontHeight);
510 }
511 
512 
513 BSize
514 BCheckBox::_ValidatePreferredSize()
515 {
516 	if (!fPreferredSize.IsWidthSet()) {
517 		font_height fontHeight;
518 		GetFontHeight(&fontHeight);
519 
520 		BRect rect(_CheckBoxFrame(fontHeight));
521 		float width = rect.right + rect.left;
522 		float height = rect.bottom + rect.top;
523 
524 		const BBitmap* icon = IconBitmap(B_INACTIVE_ICON_BITMAP);
525 		if (icon != NULL) {
526 			width += be_control_look->DefaultLabelSpacing()
527 				+ icon->Bounds().Width() + 1;
528 			height = std::max(height, icon->Bounds().Height());
529 		}
530 
531 		if (const char* label = Label()) {
532 			width += be_control_look->DefaultLabelSpacing()
533 				+ ceilf(StringWidth(label));
534 			height = std::max(height,
535 				ceilf(6.0f + fontHeight.ascent + fontHeight.descent));
536 		}
537 
538 		fPreferredSize.Set(width, height);
539 
540 		ResetLayoutInvalidation();
541 	}
542 
543 	return fPreferredSize;
544 }
545 
546 
547 int32
548 BCheckBox::_NextState() const
549 {
550 	switch (Value()) {
551 		case B_CONTROL_OFF:
552 			return B_CONTROL_ON;
553 		case B_CONTROL_PARTIALLY_ON:
554 			return fPartialToOff ? B_CONTROL_OFF : B_CONTROL_ON;
555 		case B_CONTROL_ON:
556 		default:
557 			return B_CONTROL_OFF;
558 	}
559 }
560 
561 
562 BCheckBox &
563 BCheckBox::operator=(const BCheckBox &)
564 {
565 	return *this;
566 }
567 
568 
569 extern "C" void
570 B_IF_GCC_2(InvalidateLayout__9BCheckBoxb, _ZN9BCheckBox16InvalidateLayoutEb)(
571 	BCheckBox* box, bool descendants)
572 {
573 	perform_data_layout_invalidated data;
574 	data.descendants = descendants;
575 
576 	box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
577 }
578 
579