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