xref: /haiku/src/kits/interface/CheckBox.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		CheckBox.cpp
23 //	Author:			Marc Flerackers (mflerackers@androme.be)
24 //	Description:	BCheckBox displays an on/off control.
25 //------------------------------------------------------------------------------
26 #include <CheckBox.h>
27 #include <Window.h>
28 
29 
30 BCheckBox::BCheckBox(BRect frame, const char *name, const char *label,
31 					 BMessage *message, uint32 resizingMode, uint32 flags)
32 	:	BControl(frame, name, label, message, resizingMode, flags),
33 		fOutlined(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 + fontHeight.descent);
39 	if (Bounds().Height() < minHeight)
40 		ResizeTo(Bounds().Width(), minHeight);
41 }
42 
43 
44 BCheckBox::~BCheckBox()
45 {
46 
47 }
48 
49 
50 BCheckBox::BCheckBox(BMessage *archive)
51 	:	BControl(archive),
52 		fOutlined(false)
53 {
54 }
55 
56 
57 BArchivable *
58 BCheckBox::Instantiate(BMessage *archive)
59 {
60 	if (validate_instantiation(archive, "BCheckBox"))
61 		return new BCheckBox(archive);
62 	else
63 		return NULL;
64 }
65 
66 
67 status_t
68 BCheckBox::Archive(BMessage *archive, bool deep) const
69 {
70 	return BControl::Archive(archive,deep);
71 }
72 
73 
74 void
75 BCheckBox::Draw(BRect updateRect)
76 {
77 	font_height fontHeight;
78 	GetFontHeight(&fontHeight);
79 
80 	// If the focus is changing, just redraw the focus indicator
81 	if (IsFocusChanging()) {
82 		float x = (float)ceil(10.0f + fontHeight.ascent);
83 		float y = 5.0f + (float)ceil(fontHeight.ascent);
84 
85 		if (IsFocus())
86 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
87 		else
88 			SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
89 
90 		StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
91 
92 		return;
93 	}
94 
95 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
96 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
97 	lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT),
98 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT),
99 	darken2 = tint_color(no_tint, B_DARKEN_2_TINT),
100 	darken3 = tint_color(no_tint, B_DARKEN_3_TINT),
101 	darken4 = tint_color(no_tint, B_DARKEN_4_TINT),
102 	darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
103 
104 	BRect rect = _CheckBoxFrame();
105 
106 	if (IsEnabled()) {
107 		// Filling
108 		SetHighColor(lightenmax);
109 		FillRect(rect);
110 
111 		// Box
112 		if (fOutlined) {
113 			SetHighColor(darken3);
114 			StrokeRect(rect);
115 
116 			rect.InsetBy(1,1);
117 
118 			BeginLineArray(6);
119 
120 			AddLine(BPoint(rect.left, rect.bottom),
121 					BPoint(rect.left, rect.top), darken2);
122 			AddLine(BPoint(rect.left, rect.top),
123 					BPoint(rect.right, rect.top), darken2);
124 			AddLine(BPoint(rect.left, rect.bottom),
125 					BPoint(rect.right, rect.bottom), darken4);
126 			AddLine(BPoint(rect.right, rect.bottom),
127 					BPoint(rect.right, rect.top), darken4);
128 
129 			EndLineArray();
130 		} else {
131 			BeginLineArray(6);
132 
133 			AddLine(BPoint(rect.left, rect.bottom),
134 					BPoint(rect.left, rect.top), darken1);
135 			AddLine(BPoint(rect.left, rect.top),
136 					BPoint(rect.right, rect.top), darken1);
137 			rect.InsetBy(1,1);
138 			AddLine(BPoint(rect.left, rect.bottom),
139 					BPoint(rect.left, rect.top), darken4);
140 			AddLine(BPoint(rect.left, rect.top),
141 					BPoint(rect.right, rect.top), darken4);
142 			AddLine(BPoint(rect.left + 1.0f, rect.bottom),
143 					BPoint(rect.right, rect.bottom), no_tint);
144 			AddLine(BPoint(rect.right, rect.bottom),
145 					BPoint(rect.right, rect.top + 1.0f), no_tint);
146 
147 			EndLineArray();
148 		}
149 
150 		// Checkmark
151 		if (Value() == B_CONTROL_ON) {
152 			rect.InsetBy(2,2);
153 
154 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
155 			SetPenSize(2);
156 // Yes, Haiku is differnt because of the anti-aliasing
157 SetDrawingMode(B_OP_OVER);
158 			StrokeLine(BPoint(rect.left, rect.top),
159 					   BPoint(rect.right, rect.bottom));
160 			StrokeLine(BPoint(rect.left, rect.bottom),
161 					   BPoint(rect.right, rect.top));
162 			SetPenSize(1);
163 SetDrawingMode(B_OP_COPY);
164 		}
165 
166 		// Label
167 		SetHighColor(darkenmax);
168 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
169 			3.0f + (float)ceil(fontHeight.ascent)));
170 
171 		// Focus
172 		if (IsFocus()) {
173 			float x = (float)ceil(10.0f + fontHeight.ascent);
174 			float y = 5.0f + (float)ceil(fontHeight.ascent);
175 
176 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
177 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
178 		}
179 	} else {
180 		// Filling
181 		SetHighColor(lighten1);
182 		FillRect(rect);
183 
184 		// Box
185 		BeginLineArray(6);
186 
187 		AddLine(BPoint(rect.left, rect.bottom),
188 				BPoint(rect.left, rect.top), no_tint);
189 		AddLine(BPoint(rect.left, rect.top),
190 				BPoint(rect.right, rect.top), no_tint);
191 		rect.InsetBy(1,1);
192 		AddLine(BPoint(rect.left, rect.bottom),
193 				BPoint(rect.left, rect.top), darken2);
194 		AddLine(BPoint(rect.left, rect.top),
195 				BPoint(rect.right, rect.top), darken2);
196 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
197 				BPoint(rect.right, rect.bottom), darken1);
198 		AddLine(BPoint(rect.right, rect.bottom),
199 				BPoint(rect.right, rect.top + 1.0f), darken1);
200 
201 		EndLineArray();
202 
203 		// Checkmark
204 		if (Value() == B_CONTROL_ON) {
205 			rect.InsetBy(2, 2);
206 
207 			SetHighColor(tint_color(ui_color(B_KEYBOARD_NAVIGATION_COLOR),
208 				B_DISABLED_MARK_TINT));
209 			SetPenSize(2);
210 // Yes, Haiku is differnt because of the anti-aliasing
211 SetDrawingMode(B_OP_OVER);
212 			StrokeLine(BPoint(rect.left, rect.top),
213 					   BPoint(rect.right, rect.bottom));
214 			StrokeLine(BPoint(rect.left, rect.bottom),
215 					   BPoint(rect.right, rect.top));
216 			SetPenSize(1);
217 SetDrawingMode(B_OP_COPY);
218 		}
219 
220 		// Label
221 		SetHighColor(tint_color(no_tint, B_DISABLED_LABEL_TINT));
222 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
223 			3.0f + (float)ceil(fontHeight.ascent)));
224 	}
225 }
226 
227 
228 void
229 BCheckBox::AttachedToWindow()
230 {
231 	BControl::AttachedToWindow();
232 }
233 
234 
235 void
236 BCheckBox::MouseDown(BPoint point)
237 {
238 	if (!IsEnabled())
239 		return;
240 
241 	fOutlined = true;
242 
243 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
244 		Invalidate();
245 		SetTracking(true);
246 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
247 	} else {
248 		BRect bounds = Bounds();
249 		uint32 buttons;
250 
251 		Draw(Bounds());
252 		Flush();
253 
254 		do {
255 			snooze(40000);
256 
257 			GetMouse(&point, &buttons, true);
258 
259 			bool inside = bounds.Contains(point);
260 
261 			if (fOutlined != inside) {
262 				fOutlined = inside;
263 				Draw(Bounds());
264 				Flush();
265 			}
266 		} while (buttons != 0);
267 
268 		if (fOutlined) {
269 			fOutlined = false;
270 			SetValue(!Value());
271 			Invoke();
272 		} else {
273 			Draw(Bounds());
274 			Flush();
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,
328 						   const BMessage *message)
329 {
330 	if (!IsTracking())
331 		return;
332 
333 	bool inside = Bounds().Contains(point);
334 
335 	if (fOutlined != inside) {
336 		fOutlined = inside;
337 		Invalidate();
338 	}
339 }
340 
341 
342 void
343 BCheckBox::DetachedFromWindow()
344 {
345 	BControl::DetachedFromWindow();
346 }
347 
348 
349 void
350 BCheckBox::SetValue(int32 value)
351 {
352 	if (value != Value()) {
353 		BControl::SetValue(value);
354 		Invalidate(_CheckBoxFrame());
355 	}
356 }
357 
358 
359 void
360 BCheckBox::GetPreferredSize(float* _width, float* _height)
361 {
362 	font_height fontHeight;
363 	GetFontHeight(&fontHeight);
364 
365 	if (_width) {
366 		float width = 12.0f + fontHeight.ascent;
367 
368 		if (Label())
369 			width += StringWidth(Label());
370 
371 		*_width = (float)ceil(width);
372 	}
373 
374 	if (_height)
375 		*_height = (float)ceil(6.0f + fontHeight.ascent + fontHeight.descent);
376 }
377 
378 
379 void
380 BCheckBox::ResizeToPreferred()
381 {
382 	BControl::ResizeToPreferred();
383 }
384 
385 
386 status_t
387 BCheckBox::Invoke(BMessage *message)
388 {
389 	return BControl::Invoke(message);
390 }
391 
392 
393 void
394 BCheckBox::FrameMoved(BPoint newLocation)
395 {
396 	BControl::FrameMoved(newLocation);
397 }
398 
399 
400 void
401 BCheckBox::FrameResized(float width, float height)
402 {
403 	BControl::FrameResized(width, height);
404 }
405 
406 
407 BHandler *
408 BCheckBox::ResolveSpecifier(BMessage *message, int32 index,
409 									  BMessage *specifier, int32 what,
410 									  const char *property)
411 {
412 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
413 }
414 
415 
416 status_t
417 BCheckBox::GetSupportedSuites(BMessage *message)
418 {
419 	return BControl::GetSupportedSuites(message);
420 }
421 
422 
423 void
424 BCheckBox::MakeFocus(bool focused)
425 {
426 	BControl::MakeFocus(focused);
427 }
428 
429 
430 void
431 BCheckBox::AllAttached()
432 {
433 	BControl::AllAttached();
434 }
435 
436 
437 void
438 BCheckBox::AllDetached()
439 {
440 	BControl::AllDetached();
441 }
442 
443 
444 status_t
445 BCheckBox::Perform(perform_code d, void *arg)
446 {
447 	return BControl::Perform(d, arg);
448 }
449 
450 
451 void BCheckBox::_ReservedCheckBox1() {}
452 void BCheckBox::_ReservedCheckBox2() {}
453 void BCheckBox::_ReservedCheckBox3() {}
454 
455 
456 BCheckBox &
457 BCheckBox::operator=(const BCheckBox &)
458 {
459 	return *this;
460 }
461 
462 // _CheckBoxFrame
463 BRect
464 BCheckBox::_CheckBoxFrame() const
465 {
466 	font_height fh;
467 	GetFontHeight(&fh);
468 
469 	return BRect(1.0f, 3.0f, ceilf(3.0f + fh.ascent), ceilf(5.0f + fh.ascent));
470 }
471