xref: /haiku/src/kits/interface/CheckBox.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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 fh;
363 	GetFontHeight(&fh);
364 
365 	*height = (float)ceil(6.0f + fh.ascent + fh.descent);
366 	*width = 12.0f + fh.ascent;
367 
368 	if (Label())
369 		*width += StringWidth(Label());
370 
371 	*width = (float)ceil(*width);
372 }
373 
374 
375 void
376 BCheckBox::ResizeToPreferred()
377 {
378 	BControl::ResizeToPreferred();
379 }
380 
381 
382 status_t
383 BCheckBox::Invoke(BMessage *message)
384 {
385 	return BControl::Invoke(message);
386 }
387 
388 
389 void
390 BCheckBox::FrameMoved(BPoint newLocation)
391 {
392 	BControl::FrameMoved(newLocation);
393 }
394 
395 
396 void
397 BCheckBox::FrameResized(float width, float height)
398 {
399 	BControl::FrameResized(width, height);
400 }
401 
402 
403 BHandler *
404 BCheckBox::ResolveSpecifier(BMessage *message, int32 index,
405 									  BMessage *specifier, int32 what,
406 									  const char *property)
407 {
408 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
409 }
410 
411 
412 status_t
413 BCheckBox::GetSupportedSuites(BMessage *message)
414 {
415 	return BControl::GetSupportedSuites(message);
416 }
417 
418 
419 void
420 BCheckBox::MakeFocus(bool focused)
421 {
422 	BControl::MakeFocus(focused);
423 }
424 
425 
426 void
427 BCheckBox::AllAttached()
428 {
429 	BControl::AllAttached();
430 }
431 
432 
433 void
434 BCheckBox::AllDetached()
435 {
436 	BControl::AllDetached();
437 }
438 
439 
440 status_t
441 BCheckBox::Perform(perform_code d, void *arg)
442 {
443 	return BControl::Perform(d, arg);
444 }
445 
446 
447 void BCheckBox::_ReservedCheckBox1() {}
448 void BCheckBox::_ReservedCheckBox2() {}
449 void BCheckBox::_ReservedCheckBox3() {}
450 
451 
452 BCheckBox &
453 BCheckBox::operator=(const BCheckBox &)
454 {
455 	return *this;
456 }
457 
458 // _CheckBoxFrame
459 BRect
460 BCheckBox::_CheckBoxFrame() const
461 {
462 	font_height fh;
463 	GetFontHeight(&fh);
464 
465 	return BRect(1.0f, 3.0f, ceilf(3.0f + fh.ascent), ceilf(5.0f + fh.ascent));
466 }
467