xref: /haiku/src/kits/interface/CheckBox.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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 = 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
129 BCheckBox::AttachedToWindow()
130 {
131 	BControl::AttachedToWindow();
132 }
133 
134 
135 void
136 BCheckBox::DetachedFromWindow()
137 {
138 	BControl::DetachedFromWindow();
139 }
140 
141 
142 void
143 BCheckBox::AllAttached()
144 {
145 	BControl::AllAttached();
146 }
147 
148 
149 void
150 BCheckBox::AllDetached()
151 {
152 	BControl::AllDetached();
153 }
154 
155 
156 void
157 BCheckBox::FrameMoved(BPoint newPosition)
158 {
159 	BControl::FrameMoved(newPosition);
160 }
161 
162 
163 void
164 BCheckBox::FrameResized(float newWidth, float newHeight)
165 {
166 	BControl::FrameResized(newWidth, newHeight);
167 }
168 
169 
170 void
171 BCheckBox::WindowActivated(bool active)
172 {
173 	BControl::WindowActivated(active);
174 }
175 
176 
177 void
178 BCheckBox::MessageReceived(BMessage* message)
179 {
180 	BControl::MessageReceived(message);
181 }
182 
183 
184 void
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
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
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
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
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
302 BCheckBox::ResizeToPreferred()
303 {
304 	BControl::ResizeToPreferred();
305 }
306 
307 
308 BSize
309 BCheckBox::MinSize()
310 {
311 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
312 		_ValidatePreferredSize());
313 }
314 
315 
316 BSize
317 BCheckBox::MaxSize()
318 {
319 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
320 		_ValidatePreferredSize());
321 }
322 
323 
324 BSize
325 BCheckBox::PreferredSize()
326 {
327 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
328 		_ValidatePreferredSize());
329 }
330 
331 
332 BAlignment
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
344 BCheckBox::MakeFocus(bool focused)
345 {
346 	BControl::MakeFocus(focused);
347 }
348 
349 
350 void
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
372 BCheckBox::Invoke(BMessage* message)
373 {
374 	return BControl::Invoke(message);
375 }
376 
377 
378 BHandler*
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
388 BCheckBox::GetSupportedSuites(BMessage* message)
389 {
390 	return BControl::GetSupportedSuites(message);
391 }
392 
393 
394 status_t
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
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
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
471 BCheckBox::IsPartialStateToOff() const
472 {
473 	return fPartialToOff;
474 }
475 
476 
477 void
478 BCheckBox::SetPartialStateToOff(bool partialToOff)
479 {
480 	fPartialToOff = partialToOff;
481 }
482 
483 
484 // #pragma mark - FBC padding
485 
486 
487 void BCheckBox::_ReservedCheckBox1() {}
488 void BCheckBox::_ReservedCheckBox2() {}
489 void BCheckBox::_ReservedCheckBox3() {}
490 
491 
492 BRect
493 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
501 BCheckBox::_CheckBoxFrame() const
502 {
503 	font_height fontHeight;
504 	GetFontHeight(&fontHeight);
505 	return _CheckBoxFrame(fontHeight);
506 }
507 
508 
509 BSize
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
544 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 &
559 BCheckBox::operator=(const BCheckBox &)
560 {
561 	return *this;
562 }
563 
564 
565 extern "C" void
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