xref: /haiku/src/kits/interface/CheckBox.cpp (revision f73f5d4c42a01ece688cbb57b5d332cc0f68b2c6)
1 /*
2  * Copyright 2001-2009, Haiku Inc.
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 /*!	BCheckBox displays an on/off control. */
11 
12 
13 #include <CheckBox.h>
14 
15 #include <new>
16 
17 #include <ControlLook.h>
18 #include <LayoutUtils.h>
19 #include <Window.h>
20 
21 #include <binary_compatibility/Interface.h>
22 
23 
24 BCheckBox::BCheckBox(BRect frame, const char *name, const char *label,
25 		BMessage *message, uint32 resizingMode, uint32 flags)
26 	:
27 	BControl(frame, name, label, message, resizingMode, flags),
28 	fPreferredSize(),
29 	fOutlined(false)
30 {
31 	// Resize to minimum height if needed
32 	font_height fontHeight;
33 	GetFontHeight(&fontHeight);
34 	float minHeight = (float)ceil(6.0f + fontHeight.ascent
35 		+ fontHeight.descent);
36 	if (Bounds().Height() < minHeight)
37 		ResizeTo(Bounds().Width(), minHeight);
38 }
39 
40 
41 BCheckBox::BCheckBox(const char *name, const char *label, BMessage *message,
42 	uint32 flags)
43 	:
44 	BControl(name, label, message, flags | B_WILL_DRAW | B_NAVIGABLE),
45 	fPreferredSize(),
46 	fOutlined(false)
47 {
48 }
49 
50 
51 BCheckBox::BCheckBox(const char *label, BMessage *message)
52 	:
53 	BControl(NULL, label, message, B_WILL_DRAW | B_NAVIGABLE),
54 	fPreferredSize(),
55 	fOutlined(false)
56 {
57 }
58 
59 
60 BCheckBox::BCheckBox(BMessage *archive)
61 	:
62 	BControl(archive),
63 	fOutlined(false)
64 {
65 }
66 
67 
68 BCheckBox::~BCheckBox()
69 {
70 }
71 
72 
73 // #pragma mark -
74 
75 
76 BArchivable *
77 BCheckBox::Instantiate(BMessage *archive)
78 {
79 	if (validate_instantiation(archive, "BCheckBox"))
80 		return new(std::nothrow) BCheckBox(archive);
81 
82 	return NULL;
83 }
84 
85 
86 status_t
87 BCheckBox::Archive(BMessage *archive, bool deep) const
88 {
89 	return BControl::Archive(archive, deep);
90 }
91 
92 
93 // #pragma mark -
94 
95 
96 void
97 BCheckBox::Draw(BRect updateRect)
98 {
99 	if (be_control_look != NULL) {
100 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
101 
102 		uint32 flags = be_control_look->Flags(this);
103 		if (fOutlined)
104 			flags |= BControlLook::B_CLICKED;
105 
106 		BRect checkBoxRect(_CheckBoxFrame());
107 		BRect rect(checkBoxRect);
108 		be_control_look->DrawCheckBox(this, rect, updateRect,base, flags);
109 
110 		BRect labelRect(Bounds());
111 		labelRect.left = checkBoxRect.right
112 			+ be_control_look->DefaultLabelSpacing();
113 
114 		be_control_look->DrawLabel(this, Label(), labelRect, updateRect,
115 			base, flags);
116 		return;
117 	}
118 
119 	font_height fontHeight;
120 	GetFontHeight(&fontHeight);
121 
122 	// If the focus is changing, just redraw the focus indicator
123 	if (IsFocusChanging()) {
124 		float x = (float)ceil(10.0f + fontHeight.ascent);
125 		float y = 5.0f + (float)ceil(fontHeight.ascent);
126 
127 		if (IsFocus())
128 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
129 		else
130 			SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
131 
132 		StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
133 		return;
134 	}
135 
136 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
137 	rgb_color lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT);
138 	rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
139 	rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
140 	rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
141 	rgb_color darken3 = tint_color(noTint, B_DARKEN_3_TINT);
142 	rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
143 	rgb_color darkenmax = tint_color(noTint, B_DARKEN_MAX_TINT);
144 
145 	BRect rect = _CheckBoxFrame();
146 
147 	if (IsEnabled()) {
148 		// Filling
149 		SetHighColor(lightenMax);
150 		FillRect(rect);
151 
152 		// Box
153 		if (fOutlined) {
154 			SetHighColor(darken3);
155 			StrokeRect(rect);
156 
157 			rect.InsetBy(1, 1);
158 
159 			BeginLineArray(6);
160 
161 			AddLine(BPoint(rect.left, rect.bottom),
162 					BPoint(rect.left, rect.top), darken2);
163 			AddLine(BPoint(rect.left, rect.top),
164 					BPoint(rect.right, rect.top), darken2);
165 			AddLine(BPoint(rect.left, rect.bottom),
166 					BPoint(rect.right, rect.bottom), darken4);
167 			AddLine(BPoint(rect.right, rect.bottom),
168 					BPoint(rect.right, rect.top), darken4);
169 
170 			EndLineArray();
171 		} else {
172 			BeginLineArray(6);
173 
174 			AddLine(BPoint(rect.left, rect.bottom),
175 					BPoint(rect.left, rect.top), darken1);
176 			AddLine(BPoint(rect.left, rect.top),
177 					BPoint(rect.right, rect.top), darken1);
178 			rect.InsetBy(1, 1);
179 			AddLine(BPoint(rect.left, rect.bottom),
180 					BPoint(rect.left, rect.top), darken4);
181 			AddLine(BPoint(rect.left, rect.top),
182 					BPoint(rect.right, rect.top), darken4);
183 			AddLine(BPoint(rect.left + 1.0f, rect.bottom),
184 					BPoint(rect.right, rect.bottom), noTint);
185 			AddLine(BPoint(rect.right, rect.bottom),
186 					BPoint(rect.right, rect.top + 1.0f), noTint);
187 
188 			EndLineArray();
189 		}
190 
191 		// Checkmark
192 		if (Value() == B_CONTROL_ON) {
193 			rect.InsetBy(3, 3);
194 
195 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
196 			SetPenSize(2);
197 			StrokeLine(BPoint(rect.left, rect.top),
198 					   BPoint(rect.right, rect.bottom));
199 			StrokeLine(BPoint(rect.left, rect.bottom),
200 					   BPoint(rect.right, rect.top));
201 			SetPenSize(1);
202 		}
203 
204 		// Label
205 		SetHighColor(darkenmax);
206 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
207 			3.0f + (float)ceil(fontHeight.ascent)));
208 
209 		// Focus
210 		if (IsFocus()) {
211 			float x = (float)ceil(10.0f + fontHeight.ascent);
212 			float y = 5.0f + (float)ceil(fontHeight.ascent);
213 
214 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
215 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
216 		}
217 	} else {
218 		// Filling
219 		SetHighColor(lighten1);
220 		FillRect(rect);
221 
222 		// Box
223 		BeginLineArray(6);
224 
225 		AddLine(BPoint(rect.left, rect.bottom),
226 				BPoint(rect.left, rect.top), noTint);
227 		AddLine(BPoint(rect.left, rect.top),
228 				BPoint(rect.right, rect.top), noTint);
229 		rect.InsetBy(1, 1);
230 		AddLine(BPoint(rect.left, rect.bottom),
231 				BPoint(rect.left, rect.top), darken2);
232 		AddLine(BPoint(rect.left, rect.top),
233 				BPoint(rect.right, rect.top), darken2);
234 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
235 				BPoint(rect.right, rect.bottom), darken1);
236 		AddLine(BPoint(rect.right, rect.bottom),
237 				BPoint(rect.right, rect.top + 1.0f), darken1);
238 
239 		EndLineArray();
240 
241 		// Checkmark
242 		if (Value() == B_CONTROL_ON) {
243 			rect.InsetBy(3, 3);
244 
245 			SetHighColor(tint_color(ui_color(B_KEYBOARD_NAVIGATION_COLOR),
246 				B_DISABLED_MARK_TINT));
247 			SetPenSize(2);
248 			StrokeLine(BPoint(rect.left, rect.top),
249 					   BPoint(rect.right, rect.bottom));
250 			StrokeLine(BPoint(rect.left, rect.bottom),
251 					   BPoint(rect.right, rect.top));
252 			SetPenSize(1);
253 		}
254 
255 		// Label
256 		SetHighColor(tint_color(noTint, B_DISABLED_LABEL_TINT));
257 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
258 			3.0f + (float)ceil(fontHeight.ascent)));
259 	}
260 }
261 
262 
263 void
264 BCheckBox::AttachedToWindow()
265 {
266 	BControl::AttachedToWindow();
267 }
268 
269 
270 void
271 BCheckBox::DetachedFromWindow()
272 {
273 	BControl::DetachedFromWindow();
274 }
275 
276 
277 void
278 BCheckBox::AllAttached()
279 {
280 	BControl::AllAttached();
281 }
282 
283 
284 void
285 BCheckBox::AllDetached()
286 {
287 	BControl::AllDetached();
288 }
289 
290 
291 void
292 BCheckBox::FrameMoved(BPoint newLocation)
293 {
294 	BControl::FrameMoved(newLocation);
295 }
296 
297 
298 void
299 BCheckBox::FrameResized(float width, float height)
300 {
301 	BControl::FrameResized(width, height);
302 }
303 
304 
305 void
306 BCheckBox::WindowActivated(bool active)
307 {
308 	BControl::WindowActivated(active);
309 }
310 
311 
312 // #pragma mark -
313 
314 
315 void
316 BCheckBox::MessageReceived(BMessage *message)
317 {
318 	BControl::MessageReceived(message);
319 }
320 
321 
322 void
323 BCheckBox::KeyDown(const char *bytes, int32 numBytes)
324 {
325 	BControl::KeyDown(bytes, numBytes);
326 }
327 
328 
329 void
330 BCheckBox::MouseDown(BPoint point)
331 {
332 	if (!IsEnabled())
333 		return;
334 
335 	fOutlined = true;
336 
337 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
338 		Invalidate();
339 		SetTracking(true);
340 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
341 	} else {
342 		BRect bounds = Bounds();
343 		uint32 buttons;
344 
345 		Invalidate();
346 		Window()->UpdateIfNeeded();
347 
348 		do {
349 			snooze(40000);
350 
351 			GetMouse(&point, &buttons, true);
352 
353 			bool inside = bounds.Contains(point);
354 			if (fOutlined != inside) {
355 				fOutlined = inside;
356 				Invalidate();
357 				Window()->UpdateIfNeeded();
358 			}
359 		} while (buttons != 0);
360 
361 		if (fOutlined) {
362 			fOutlined = false;
363 			SetValue(!Value());
364 			Invoke();
365 		} else {
366 			Invalidate();
367 			Window()->UpdateIfNeeded();
368 		}
369 	}
370 }
371 
372 
373 void
374 BCheckBox::MouseUp(BPoint point)
375 {
376 	if (!IsTracking())
377 		return;
378 
379 	bool inside = Bounds().Contains(point);
380 
381 	if (fOutlined != inside) {
382 		fOutlined = inside;
383 		Invalidate();
384 	}
385 
386 	if (fOutlined) {
387 		fOutlined = false;
388 		SetValue(!Value());
389 		Invoke();
390 	} else {
391 		Invalidate();
392 	}
393 
394 	SetTracking(false);
395 }
396 
397 
398 void
399 BCheckBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
400 {
401 	if (!IsTracking())
402 		return;
403 
404 	bool inside = Bounds().Contains(point);
405 
406 	if (fOutlined != inside) {
407 		fOutlined = inside;
408 		Invalidate();
409 	}
410 }
411 
412 
413 // #pragma mark -
414 
415 
416 void
417 BCheckBox::GetPreferredSize(float* _width, float* _height)
418 {
419 	font_height fontHeight;
420 	GetFontHeight(&fontHeight);
421 
422 	if (_width) {
423 		float width = 12.0f + fontHeight.ascent;
424 
425 		if (Label())
426 			width += StringWidth(Label());
427 
428 		*_width = (float)ceil(width);
429 	}
430 
431 	if (_height)
432 		*_height = (float)ceil(6.0f + fontHeight.ascent + fontHeight.descent);
433 }
434 
435 
436 void
437 BCheckBox::ResizeToPreferred()
438 {
439 	BControl::ResizeToPreferred();
440 }
441 
442 
443 BSize
444 BCheckBox::MinSize()
445 {
446 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
447 		_ValidatePreferredSize());
448 }
449 
450 
451 BSize
452 BCheckBox::MaxSize()
453 {
454 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
455 		_ValidatePreferredSize());
456 }
457 
458 
459 BSize
460 BCheckBox::PreferredSize()
461 {
462 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
463 		_ValidatePreferredSize());
464 }
465 
466 
467 BAlignment
468 BCheckBox::LayoutAlignment()
469 {
470 	return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
471 		BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
472 }
473 
474 
475 // #pragma mark -
476 
477 
478 void
479 BCheckBox::MakeFocus(bool focused)
480 {
481 	BControl::MakeFocus(focused);
482 }
483 
484 
485 void
486 BCheckBox::SetValue(int32 value)
487 {
488 	value = value ? B_CONTROL_ON : B_CONTROL_OFF;
489 		// we only accept boolean values
490 
491 	if (value != Value()) {
492 		BControl::SetValueNoUpdate(value);
493 		Invalidate(_CheckBoxFrame());
494 	}
495 }
496 
497 
498 status_t
499 BCheckBox::Invoke(BMessage *message)
500 {
501 	return BControl::Invoke(message);
502 }
503 
504 
505 BHandler *
506 BCheckBox::ResolveSpecifier(BMessage *message, int32 index,
507 	BMessage *specifier, int32 what, const char *property)
508 {
509 	return BControl::ResolveSpecifier(message, index, specifier, what,
510 		property);
511 }
512 
513 
514 status_t
515 BCheckBox::GetSupportedSuites(BMessage *message)
516 {
517 	return BControl::GetSupportedSuites(message);
518 }
519 
520 
521 status_t
522 BCheckBox::Perform(perform_code code, void* _data)
523 {
524 	switch (code) {
525 		case PERFORM_CODE_MIN_SIZE:
526 			((perform_data_min_size*)_data)->return_value
527 				= BCheckBox::MinSize();
528 			return B_OK;
529 		case PERFORM_CODE_MAX_SIZE:
530 			((perform_data_max_size*)_data)->return_value
531 				= BCheckBox::MaxSize();
532 			return B_OK;
533 		case PERFORM_CODE_PREFERRED_SIZE:
534 			((perform_data_preferred_size*)_data)->return_value
535 				= BCheckBox::PreferredSize();
536 			return B_OK;
537 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
538 			((perform_data_layout_alignment*)_data)->return_value
539 				= BCheckBox::LayoutAlignment();
540 			return B_OK;
541 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
542 			((perform_data_has_height_for_width*)_data)->return_value
543 				= BCheckBox::HasHeightForWidth();
544 			return B_OK;
545 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
546 		{
547 			perform_data_get_height_for_width* data
548 				= (perform_data_get_height_for_width*)_data;
549 			BCheckBox::GetHeightForWidth(data->width, &data->min, &data->max,
550 				&data->preferred);
551 			return B_OK;
552 		}
553 		case PERFORM_CODE_SET_LAYOUT:
554 		{
555 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
556 			BCheckBox::SetLayout(data->layout);
557 			return B_OK;
558 		}
559 		case PERFORM_CODE_LAYOUT_INVALIDATED:
560 		{
561 			perform_data_layout_invalidated* data
562 				= (perform_data_layout_invalidated*)_data;
563 			BCheckBox::LayoutInvalidated(data->descendants);
564 			return B_OK;
565 		}
566 		case PERFORM_CODE_DO_LAYOUT:
567 		{
568 			BCheckBox::DoLayout();
569 			return B_OK;
570 		}
571 	}
572 
573 	return BControl::Perform(code, _data);
574 }
575 
576 
577 void
578 BCheckBox::LayoutInvalidated(bool descendants)
579 {
580 	// invalidate cached preferred size
581 	fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET);
582 }
583 
584 
585 // #pragma mark - FBC padding
586 
587 
588 void BCheckBox::_ReservedCheckBox1() {}
589 void BCheckBox::_ReservedCheckBox2() {}
590 void BCheckBox::_ReservedCheckBox3() {}
591 
592 
593 BRect
594 BCheckBox::_CheckBoxFrame() const
595 {
596 	font_height fontHeight;
597 	GetFontHeight(&fontHeight);
598 
599 	return BRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent),
600 		ceilf(5.0f + fontHeight.ascent));
601 }
602 
603 
604 BSize
605 BCheckBox::_ValidatePreferredSize()
606 {
607 	if (!fPreferredSize.IsWidthSet()) {
608 		font_height fontHeight;
609 		GetFontHeight(&fontHeight);
610 
611 		float width = 12.0f + fontHeight.ascent;
612 
613 		if (Label())
614 			width += StringWidth(Label());
615 
616 		fPreferredSize.width = (float)ceil(width);
617 
618 		fPreferredSize.height = (float)ceil(6.0f + fontHeight.ascent
619 			+ fontHeight.descent);
620 
621 		ResetLayoutInvalidation();
622 	}
623 
624 	return fPreferredSize;
625 }
626 
627 
628 BCheckBox &
629 BCheckBox::operator=(const BCheckBox &)
630 {
631 	return *this;
632 }
633 
634 
635 extern "C" void
636 B_IF_GCC_2(InvalidateLayout__9BCheckBoxb, _ZN9BCheckBox16InvalidateLayoutEb)(
637 	BCheckBox* box, bool descendants)
638 {
639 	perform_data_layout_invalidated data;
640 	data.descendants = descendants;
641 
642 	box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
643 }
644 
645