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