xref: /haiku/src/kits/interface/Button.cpp (revision 4afae676ad98b8f1e219f704dfc9c8507bce106e)
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:		Button.cpp
23 //	Author:			Marc Flerackers (mflerackers@androme.be)
24 //	Description:	BButton displays and controls a button in a window.
25 //------------------------------------------------------------------------------
26 
27 // Standard Includes -----------------------------------------------------------
28 
29 // System Includes -------------------------------------------------------------
30 #include <Button.h>
31 #include <Window.h>
32 #include <Errors.h>
33 
34 // Project Includes ------------------------------------------------------------
35 
36 // Local Includes --------------------------------------------------------------
37 
38 // Local Defines ---------------------------------------------------------------
39 
40 // Globals ---------------------------------------------------------------------
41 
42 //------------------------------------------------------------------------------
43 BButton::BButton(BRect frame, const char *name, const char *label, BMessage *message,
44 				  uint32 resizingMode, uint32 flags)
45 	:	BControl(frame, name, label, message, resizingMode, flags),
46 		fDrawAsDefault(false)
47 {
48 	if (Bounds().Height() < 24.0f)
49 		ResizeTo(Bounds().Width(), 24.0f);
50 }
51 //------------------------------------------------------------------------------
52 BButton::~BButton()
53 {
54 }
55 //------------------------------------------------------------------------------
56 BButton::BButton(BMessage *archive)
57 	:	BControl (archive)
58 {
59 	if ( archive->FindBool ( "_default", &fDrawAsDefault ) != B_OK )
60 		fDrawAsDefault = false;
61 }
62 //------------------------------------------------------------------------------
63 BArchivable *BButton::Instantiate ( BMessage *archive )
64 {
65 	if (validate_instantiation(archive, "BButton"))
66 		return new BButton(archive);
67 	else
68 		return NULL;
69 }
70 //------------------------------------------------------------------------------
71 status_t BButton::Archive(BMessage* archive, bool deep) const
72 {
73 	status_t err = BControl::Archive(archive, deep);
74 
75 	if (err != B_OK)
76 		return err;
77 
78 	if (fDrawAsDefault)
79 		err = archive->AddBool("_default", fDrawAsDefault);
80 
81 	return err;
82 }
83 //------------------------------------------------------------------------------
84 void BButton::Draw(BRect updateRect)
85 {
86 	BRect rect = Bounds();
87 
88 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
89 		lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
90 		lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT),
91 		lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT),
92 		darken2 = tint_color(no_tint, B_DARKEN_2_TINT),
93 		darken4 = tint_color(no_tint, B_DARKEN_4_TINT),
94 		darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
95 
96 	if (IsDefault())
97 		rect = DrawDefault(rect, IsEnabled());
98 	else
99 		rect.InsetBy(1,1);
100 
101 	if (IsEnabled())
102 	{
103 		// This can be set outside draw
104 		SetHighColor(darken4);
105 
106 		// Dark border
107 		StrokeRect(rect);
108 
109 		BeginLineArray(8);
110 
111 		// Corners
112 		AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint);
113 		AddLine(rect.LeftTop(), rect.LeftTop(), no_tint);
114 		AddLine(rect.RightTop(), rect.RightTop(), no_tint);
115 		AddLine(rect.RightBottom(), rect.RightBottom(), no_tint);
116 
117 		rect.InsetBy(1, 1);
118 
119 		// First bevel
120 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
121 			BPoint(rect.right, rect.bottom), darken2);
122 		AddLine(BPoint(rect.right, rect.bottom - 1.0f),
123 			BPoint(rect.right, rect.top + 1.0f), darken2);
124 
125 		AddLine(BPoint(rect.left, rect.top),
126 			BPoint(rect.left, rect.bottom), lighten1);
127 		AddLine(BPoint(rect.left + 1.0f, rect.top),
128 			BPoint(rect.right, rect.top), lighten1);
129 
130 		EndLineArray();
131 
132 		rect.InsetBy(1, 1);
133 
134 		// Second bevel
135 		SetHighColor(lightenmax);
136 		FillRect(rect);
137 
138 		SetHighColor(no_tint);
139 		StrokeLine(BPoint(rect.right, rect.top + 1.0f),
140 			BPoint(rect.right, rect.bottom));
141 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom));
142 
143 		rect.InsetBy(1, 1);
144 
145 		// Filling
146 		rect.left += 1.0f;
147 		rect.top += 1.0f;
148 		SetHighColor(lighten1);
149 		FillRect(rect);
150 
151 		if (Value())
152 		{
153 			// Invert
154 			rect.left -= 3;
155 			rect.top -= 3;
156 			rect.right += 2;
157 			rect.bottom += 2;
158 			SetDrawingMode(B_OP_INVERT);
159 			SetHighColor(0, 0, 0);
160 			FillRect(rect);
161 			SetDrawingMode(B_OP_COPY);
162 		}
163 
164 		// Label
165 		BFont font;
166 		GetFont(&font);
167 
168 		float x = Bounds().Width() / 2 - StringWidth(Label()) / 2.0f;
169 		float y = Bounds().Height() / 2.0f + (float)ceil(font.Size() / 2.0f);
170 
171 		if (Value())
172 		{
173 			SetHighColor(lightenmax);
174 			SetLowColor(darkenmax);
175 		}
176 		else
177 		{
178 			SetHighColor(darkenmax);
179 			SetLowColor(lighten2);
180 		}
181 
182 		DrawString(Label(), BPoint(x, y));
183 
184 		// Focus
185 		if (IsFocus())
186 		{
187 			font_height fh;
188 			font.GetHeight(&fh);
189 
190 			y += 2.0f;
191 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
192 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
193 		}
194 	}
195 	else
196 	{
197 		// This can be set outside draw
198 		SetHighColor(darken2);
199 
200 		// Dark border
201 		StrokeRect(rect);
202 
203 		BeginLineArray(8);
204 
205 		// Corners
206 		AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint);
207 		AddLine(rect.LeftTop(), rect.LeftTop(), no_tint);
208 		AddLine(rect.RightTop(), rect.RightTop(), no_tint);
209 		AddLine(rect.RightBottom(), rect.RightBottom(), no_tint);
210 
211 		rect.InsetBy(1, 1);
212 
213 		// First bevel
214 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
215 			BPoint(rect.right, rect.bottom), no_tint);
216 		AddLine(BPoint(rect.right, rect.bottom - 1.0f),
217 			BPoint(rect.right, rect.top + 1.0f), no_tint);
218 
219 		AddLine(BPoint(rect.left, rect.top),
220 			BPoint(rect.left, rect.bottom), lighten1);
221 		AddLine(BPoint(rect.left + 1.0f, rect.top),
222 			BPoint(rect.right, rect.top), lighten1);
223 
224 		EndLineArray();
225 
226 		rect.InsetBy(1, 1);
227 
228 		// Second bevel
229 		SetHighColor(lightenmax);
230 		FillRect(rect);
231 
232 		SetHighColor(no_tint);
233 		StrokeLine(BPoint(rect.right, rect.top + 1.0f),
234 			BPoint(rect.right, rect.bottom));
235 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom));
236 
237 		rect.InsetBy(1, 1);
238 
239 		// Filling
240 		rect.left += 1.0f;
241 		rect.top += 1.0f;
242 		SetHighColor(lighten1);
243 		FillRect(rect);
244 
245 		// Label
246 		BFont font;
247 		GetFont(&font);
248 
249 		float x = Bounds().Width() / 2 - StringWidth(Label()) / 2.0f;
250 		float y = Bounds().Height() / 2.0f + (float)ceil(font.Size() / 2.0f);
251 
252 		SetHighColor(tint_color(no_tint, B_DISABLED_LABEL_TINT));
253 		SetLowColor(lighten2);
254 		DrawString(Label(), BPoint(x, y));
255 	}
256 }
257 //------------------------------------------------------------------------------
258 void BButton::MouseDown(BPoint point)
259 {
260 	if (!IsEnabled())
261 	{
262 		BControl::MouseDown(point);
263 		return;
264 	}
265 
266 	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS);
267 
268 	SetValue(B_CONTROL_ON);
269 	SetTracking(true);
270 }
271 //------------------------------------------------------------------------------
272 void BButton::AttachedToWindow()
273 {
274 	BControl::AttachedToWindow();
275 
276 	if (IsDefault())
277 		Window()->SetDefaultButton(this);
278 }
279 //------------------------------------------------------------------------------
280 void BButton::KeyDown ( const char *bytes, int32 numBytes )
281 {
282 	if (numBytes == 1)
283 	{
284 		switch (bytes[0])
285 		{
286 			case B_ENTER:
287 			case B_SPACE:
288 				SetValue(B_CONTROL_ON);
289 				snooze(50000);
290 				SetValue(B_CONTROL_OFF);
291 				Invoke();
292 				break;
293 
294 			default:
295 				BControl::KeyDown(bytes, numBytes);
296 		}
297 	}
298 	else
299 		BControl::KeyDown(bytes, numBytes);
300 }
301 //------------------------------------------------------------------------------
302 void BButton::MakeDefault(bool flag)
303 {
304 	if (flag == IsDefault())
305 		return;
306 
307 	fDrawAsDefault = flag;
308 	BWindow *window = Window();
309 
310 	if (flag)
311 	{
312 		ResizeBy(6.0f, 6.0f);
313 		MoveBy(-3.0f, -3.0f);
314 
315 		if (window)
316 			window->SetDefaultButton((BButton*)this);
317 	}
318 	else
319 	{
320 		ResizeBy(-6.0f, -6.0f);
321 		MoveBy(3.0f, 3.0f);
322 
323 		if (window)
324 			window->SetDefaultButton(NULL);
325 	}
326 }
327 //------------------------------------------------------------------------------
328 void BButton::SetLabel(const char *string)
329 {
330 	BControl::SetLabel(string);
331 }
332 //------------------------------------------------------------------------------
333 bool BButton::IsDefault() const
334 {
335 	return fDrawAsDefault;
336 }
337 //------------------------------------------------------------------------------
338 void BButton::MessageReceived(BMessage *message)
339 {
340 	BControl::MessageReceived(message);
341 }
342 //------------------------------------------------------------------------------
343 void BButton::WindowActivated(bool active)
344 {
345 	BControl::WindowActivated(active);
346 }
347 //------------------------------------------------------------------------------
348 void BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
349 {
350 	if (IsEnabled() && IsTracking())
351 	{
352 		if (transit == B_EXITED_VIEW)
353 			SetValue(B_CONTROL_OFF);
354 		else if (transit == B_ENTERED_VIEW)
355 			SetValue(B_CONTROL_ON);
356 	}
357 	else
358 		BControl::MouseMoved(point, transit, message);
359 }
360 //------------------------------------------------------------------------------
361 void BButton::MouseUp(BPoint point)
362 {
363 	if (IsEnabled() && IsTracking())
364 	{
365 		if (Bounds().Contains(point))
366 		{
367 			if ( Value() == B_CONTROL_ON)
368 			{
369 				SetValue(B_CONTROL_OFF);
370 				BControl::Invoke();
371 			}
372 		}
373 
374 		SetTracking(false);
375 	}
376 }
377 //------------------------------------------------------------------------------
378 void BButton::DetachedFromWindow()
379 {
380 	BControl::DetachedFromWindow();
381 }
382 //------------------------------------------------------------------------------
383 void BButton::SetValue(int32 value)
384 {
385 	BControl::SetValue(value);
386 }
387 //------------------------------------------------------------------------------
388 void BButton::GetPreferredSize (float *width, float *height)
389 {
390 	font_height fh;
391 
392 	GetFontHeight(&fh);
393 
394 	*height = (float)ceil(fh.ascent + fh.descent + fh.leading) + 12.0f;
395 	*width = 20.0f + (float)ceil(StringWidth(Label()));
396 
397 	if (*width < 75.0f)
398 		*width = 75.0f;
399 
400 	if (fDrawAsDefault)
401 	{
402 		*width += 6.0f;
403 		*height += 6.0f;
404 	}
405 }
406 //------------------------------------------------------------------------------
407 void BButton::ResizeToPreferred()
408 {
409 	 BControl::ResizeToPreferred();
410 }
411 //------------------------------------------------------------------------------
412 status_t BButton::Invoke(BMessage *message)
413 {
414 	return BControl::Invoke(message);
415 }
416 //------------------------------------------------------------------------------
417 void BButton::FrameMoved(BPoint newLocation)
418 {
419 	BControl::FrameMoved(newLocation);
420 }
421 //------------------------------------------------------------------------------
422 void BButton::FrameResized(float width, float height)
423 {
424 	BControl::FrameResized(width, height);
425 }
426 //------------------------------------------------------------------------------
427 void BButton::MakeFocus(bool focused)
428 {
429 	BControl::MakeFocus(focused);
430 }
431 //------------------------------------------------------------------------------
432 void BButton::AllAttached()
433 {
434 	BControl::AllAttached();
435 }
436 //------------------------------------------------------------------------------
437 void BButton::AllDetached()
438 {
439 	BControl::AllDetached();
440 }
441 //------------------------------------------------------------------------------
442 BHandler *BButton::ResolveSpecifier(BMessage *message, int32 index,
443 									BMessage *specifier, int32 what,
444 									const char *property)
445 {
446 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
447 }
448 //------------------------------------------------------------------------------
449 status_t BButton::GetSupportedSuites(BMessage *message)
450 {
451 	return BControl::GetSupportedSuites(message);
452 }
453 //------------------------------------------------------------------------------
454 status_t BButton::Perform(perform_code d, void *arg)
455 {
456 	return B_ERROR;
457 }
458 
459 //------------------------------------------------------------------------------
460 void BButton::_ReservedButton1() {}
461 void BButton::_ReservedButton2() {}
462 void BButton::_ReservedButton3() {}
463 //------------------------------------------------------------------------------
464 BButton &BButton::operator=(const BButton &)
465 {
466 	return *this;
467 }
468 //------------------------------------------------------------------------------
469 BRect BButton::DrawDefault(BRect bounds, bool enabled)
470 {
471 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
472 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
473 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT),
474 	darken4 = tint_color(no_tint, B_DARKEN_4_TINT);
475 
476 	if (enabled)
477 	{
478 		// Dark border
479 		BeginLineArray(4);
480 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
481 			BPoint(bounds.left, bounds.top + 1.0f), darken4);
482 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
483 			BPoint(bounds.right - 1.0f, bounds.top), darken4);
484 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
485 			BPoint(bounds.right, bounds.bottom - 1.0f), darken4);
486 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
487 			BPoint(bounds.right - 1.0f, bounds.bottom), darken4);
488 		EndLineArray();
489 
490 		bounds.InsetBy(1.0f, 1.0f);
491 
492 		// Bevel
493 		SetHighColor(darken1);
494 		StrokeRect(bounds);
495 
496 		bounds.InsetBy(1.0f, 1.0f);
497 
498 		// Filling
499 		SetHighColor(lighten1);
500 		FillRect(bounds);
501 
502 		bounds.InsetBy(2.0f, 2.0f);
503 	}
504 	else
505 	{
506 		// Dark border
507 		BeginLineArray(4);
508 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
509 			BPoint(bounds.left, bounds.top + 1.0f), darken1);
510 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
511 			BPoint(bounds.right - 1.0f, bounds.top), darken1);
512 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
513 			BPoint(bounds.right, bounds.bottom - 1.0f), darken1);
514 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
515 			BPoint(bounds.right - 1.0f, bounds.bottom), darken1);
516 		EndLineArray();
517 
518 		bounds.InsetBy(1.0f, 1.0f);
519 
520 		// Filling
521 		SetHighColor(lighten1);
522 		FillRect(bounds);
523 
524 		bounds.InsetBy(3.0f, 3.0f);
525 	}
526 
527 	return bounds;
528 }
529 //------------------------------------------------------------------------------
530 status_t Execute()
531 {
532 	// TODO:
533 	return B_ERROR;
534 }
535 //------------------------------------------------------------------------------
536 
537 /*
538  * $Log $
539  *
540  * $Id  $
541  *
542  */
543