xref: /haiku/src/kits/interface/Control.cpp (revision 04a0e9c7b68cbe3a43d38e2bca8e860fd80936fb)
1 /*
2  * Copyright 2001-2013, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers, mflerackers@androme.be
7  *		Ingo Weinhold, ingo_weinhold@gmx.de
8  */
9 
10 /*!	BControl is the base class for user-event handling objects. */
11 
12 
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <Control.h>
17 #include <PropertyInfo.h>
18 #include <Window.h>
19 
20 #include <binary_compatibility/Interface.h>
21 #include <Icon.h>
22 
23 
24 static property_info sPropertyList[] = {
25 	{
26 		"Enabled",
27 		{ B_GET_PROPERTY, B_SET_PROPERTY },
28 		{ B_DIRECT_SPECIFIER },
29 		NULL, 0,
30 		{ B_BOOL_TYPE }
31 	},
32 	{
33 		"Label",
34 		{ B_GET_PROPERTY, B_SET_PROPERTY },
35 		{ B_DIRECT_SPECIFIER },
36 		NULL, 0,
37 		{ B_STRING_TYPE }
38 	},
39 	{
40 		"Value",
41 		{ B_GET_PROPERTY, B_SET_PROPERTY },
42 		{ B_DIRECT_SPECIFIER },
43 		NULL, 0,
44 		{ B_INT32_TYPE }
45 	},
46 	{}
47 };
48 
49 
50 BControl::BControl(BRect frame, const char *name, const char *label,
51 	BMessage *message, uint32 resizingMode, uint32 flags)
52 	: BView(frame, name, resizingMode, flags)
53 {
54 	InitData(NULL);
55 
56 	SetLabel(label);
57 	SetMessage(message);
58 }
59 
60 
61 BControl::BControl(const char *name, const char *label, BMessage *message,
62 	uint32 flags)
63 	: BView(name, flags)
64 {
65 	InitData(NULL);
66 
67 	SetLabel(label);
68 	SetMessage(message);
69 }
70 
71 
72 BControl::~BControl()
73 {
74 	free(fLabel);
75 	delete fIcon;
76 	SetMessage(NULL);
77 }
78 
79 
80 BControl::BControl(BMessage *archive)
81 	: BView(archive)
82 {
83 	InitData(archive);
84 
85 	BMessage message;
86 	if (archive->FindMessage("_msg", &message) == B_OK)
87 		SetMessage(new BMessage(message));
88 
89 	const char *label;
90 	if (archive->FindString("_label", &label) == B_OK)
91 		SetLabel(label);
92 
93 	int32 value;
94 	if (archive->FindInt32("_val", &value) == B_OK)
95 		SetValue(value);
96 
97 	bool toggle;
98 	if (archive->FindBool("_disable", &toggle) == B_OK)
99 		SetEnabled(!toggle);
100 
101 	if (archive->FindBool("be:wants_nav", &toggle) == B_OK)
102 		fWantsNav = toggle;
103 }
104 
105 
106 BArchivable *
107 BControl::Instantiate(BMessage *archive)
108 {
109 	if (validate_instantiation(archive, "BControl"))
110 		return new BControl(archive);
111 
112 	return NULL;
113 }
114 
115 
116 status_t
117 BControl::Archive(BMessage *archive, bool deep) const
118 {
119 	status_t status = BView::Archive(archive, deep);
120 
121 	if (status == B_OK && Message())
122 		status = archive->AddMessage("_msg", Message());
123 
124 	if (status == B_OK && fLabel)
125 		status = archive->AddString("_label", fLabel);
126 
127 	if (status == B_OK && fValue != B_CONTROL_OFF)
128 		status = archive->AddInt32("_val", fValue);
129 
130 	if (status == B_OK && !fEnabled)
131 		status = archive->AddBool("_disable", true);
132 
133 	return status;
134 }
135 
136 
137 void
138 BControl::WindowActivated(bool active)
139 {
140 	BView::WindowActivated(active);
141 
142 	if (IsFocus())
143 		Invalidate();
144 }
145 
146 
147 void
148 BControl::AttachedToWindow()
149 {
150 	rgb_color color;
151 
152 	BView* parent = Parent();
153 	if (parent != NULL) {
154 		// inherit the color from parent
155 		color = parent->ViewColor();
156 		if (color == B_TRANSPARENT_COLOR)
157 			color = ui_color(B_PANEL_BACKGROUND_COLOR);
158 	} else
159 		color = ui_color(B_PANEL_BACKGROUND_COLOR);
160 
161 	SetViewColor(color);
162 	SetLowColor(color);
163 
164 	if (!Messenger().IsValid())
165 		SetTarget(Window());
166 
167 	BView::AttachedToWindow();
168 }
169 
170 
171 void
172 BControl::DetachedFromWindow()
173 {
174 	BView::DetachedFromWindow();
175 }
176 
177 
178 void
179 BControl::AllAttached()
180 {
181 	BView::AllAttached();
182 }
183 
184 
185 void
186 BControl::AllDetached()
187 {
188 	BView::AllDetached();
189 }
190 
191 
192 void
193 BControl::MessageReceived(BMessage *message)
194 {
195 	if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
196 		BMessage reply(B_REPLY);
197 		bool handled = false;
198 
199 		BMessage specifier;
200 		int32 index;
201 		int32 form;
202 		const char *property;
203 		if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
204 			if (strcmp(property, "Label") == 0) {
205 				if (message->what == B_GET_PROPERTY) {
206 					reply.AddString("result", fLabel);
207 					handled = true;
208 				} else {
209 					// B_SET_PROPERTY
210 					const char *label;
211 					if (message->FindString("data", &label) == B_OK) {
212 						SetLabel(label);
213 						reply.AddInt32("error", B_OK);
214 						handled = true;
215 					}
216 				}
217 			} else if (strcmp(property, "Value") == 0) {
218 				if (message->what == B_GET_PROPERTY) {
219 					reply.AddInt32("result", fValue);
220 					handled = true;
221 				} else {
222 					// B_SET_PROPERTY
223 					int32 value;
224 					if (message->FindInt32("data", &value) == B_OK) {
225 						SetValue(value);
226 						reply.AddInt32("error", B_OK);
227 						handled = true;
228 					}
229 				}
230 			} else if (strcmp(property, "Enabled") == 0) {
231 				if (message->what == B_GET_PROPERTY) {
232 					reply.AddBool("result", fEnabled);
233 					handled = true;
234 				} else {
235 					// B_SET_PROPERTY
236 					bool enabled;
237 					if (message->FindBool("data", &enabled) == B_OK) {
238 						SetEnabled(enabled);
239 						reply.AddInt32("error", B_OK);
240 						handled = true;
241 					}
242 				}
243 			}
244 		}
245 
246 		if (handled) {
247 			message->SendReply(&reply);
248 			return;
249 		}
250 	}
251 
252 	BView::MessageReceived(message);
253 }
254 
255 
256 void
257 BControl::MakeFocus(bool focused)
258 {
259 	if (focused == IsFocus())
260 		return;
261 
262 	BView::MakeFocus(focused);
263 
264  	if (Window()) {
265 		fFocusChanging = true;
266 		Invalidate(Bounds());
267 		Flush();
268 		fFocusChanging = false;
269 	}
270 }
271 
272 
273 void
274 BControl::KeyDown(const char *bytes, int32 numBytes)
275 {
276 	if (*bytes == B_ENTER || *bytes == B_SPACE) {
277 		if (!fEnabled)
278 			return;
279 
280 		SetValue(Value() ? B_CONTROL_OFF : B_CONTROL_ON);
281 		Invoke();
282 	} else
283 		BView::KeyDown(bytes, numBytes);
284 }
285 
286 
287 void
288 BControl::MouseDown(BPoint point)
289 {
290 	BView::MouseDown(point);
291 }
292 
293 
294 void
295 BControl::MouseUp(BPoint point)
296 {
297 	BView::MouseUp(point);
298 }
299 
300 
301 void
302 BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
303 {
304 	BView::MouseMoved(point, transit, message);
305 }
306 
307 
308 void
309 BControl::SetLabel(const char *label)
310 {
311 	if (label != NULL && !label[0])
312 		label = NULL;
313 
314 	// Has the label been changed?
315 	if ((fLabel && label && !strcmp(fLabel, label))
316 		|| ((fLabel == NULL || !fLabel[0]) && label == NULL))
317 		return;
318 
319 	free(fLabel);
320 	fLabel = label ? strdup(label) : NULL;
321 
322 	InvalidateLayout();
323 	Invalidate();
324 }
325 
326 
327 const char *
328 BControl::Label() const
329 {
330 	return fLabel;
331 }
332 
333 
334 void
335 BControl::SetValue(int32 value)
336 {
337 	if (value == fValue)
338 		return;
339 
340 	fValue = value;
341 	Invalidate();
342 }
343 
344 
345 void
346 BControl::SetValueNoUpdate(int32 value)
347 {
348 	fValue = value;
349 }
350 
351 
352 int32
353 BControl::Value() const
354 {
355 	return fValue;
356 }
357 
358 
359 void
360 BControl::SetEnabled(bool enabled)
361 {
362 	if (fEnabled == enabled)
363 		return;
364 
365 	fEnabled = enabled;
366 
367 	if (fEnabled && fWantsNav)
368 		SetFlags(Flags() | B_NAVIGABLE);
369 	else if (!fEnabled && (Flags() & B_NAVIGABLE)) {
370 		fWantsNav = true;
371 		SetFlags(Flags() & ~B_NAVIGABLE);
372 	} else
373 		fWantsNav = false;
374 
375 	if (Window()) {
376 		Invalidate(Bounds());
377 		Flush();
378 	}
379 }
380 
381 
382 bool
383 BControl::IsEnabled() const
384 {
385 	return fEnabled;
386 }
387 
388 
389 void
390 BControl::GetPreferredSize(float *_width, float *_height)
391 {
392 	BView::GetPreferredSize(_width, _height);
393 }
394 
395 
396 void
397 BControl::ResizeToPreferred()
398 {
399 	BView::ResizeToPreferred();
400 }
401 
402 
403 status_t
404 BControl::Invoke(BMessage *message)
405 {
406 	bool notify = false;
407 	uint32 kind = InvokeKind(&notify);
408 
409 	if (!message && !notify)
410 		message = Message();
411 
412 	BMessage clone(kind);
413 
414 	if (!message) {
415 		if (!IsWatched())
416 			return B_BAD_VALUE;
417 	} else
418 		clone = *message;
419 
420 	clone.AddInt64("when", (int64)system_time());
421 	clone.AddPointer("source", this);
422 	clone.AddInt32("be:value", fValue);
423 	clone.AddMessenger("be:sender", BMessenger(this));
424 
425 	// ToDo: is this correct? If message == NULL (even if IsWatched()), we always return B_BAD_VALUE
426 	status_t err;
427 	if (message)
428 		err = BInvoker::Invoke(&clone);
429 	else
430 		err = B_BAD_VALUE;
431 
432 	// TODO: asynchronous messaging
433 	SendNotices(kind, &clone);
434 
435 	return err;
436 }
437 
438 
439 BHandler *
440 BControl::ResolveSpecifier(BMessage *message, int32 index,
441 	BMessage *specifier, int32 what, const char *property)
442 {
443 	BPropertyInfo propInfo(sPropertyList);
444 
445 	if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
446 		return this;
447 
448 	return BView::ResolveSpecifier(message, index, specifier, what,
449 		property);
450 }
451 
452 
453 status_t
454 BControl::GetSupportedSuites(BMessage *message)
455 {
456 	message->AddString("suites", "suite/vnd.Be-control");
457 
458 	BPropertyInfo propInfo(sPropertyList);
459 	message->AddFlat("messages", &propInfo);
460 
461 	return BView::GetSupportedSuites(message);
462 }
463 
464 
465 status_t
466 BControl::Perform(perform_code code, void* _data)
467 {
468 	switch (code) {
469 		case PERFORM_CODE_MIN_SIZE:
470 			((perform_data_min_size*)_data)->return_value
471 				= BControl::MinSize();
472 			return B_OK;
473 		case PERFORM_CODE_MAX_SIZE:
474 			((perform_data_max_size*)_data)->return_value
475 				= BControl::MaxSize();
476 			return B_OK;
477 		case PERFORM_CODE_PREFERRED_SIZE:
478 			((perform_data_preferred_size*)_data)->return_value
479 				= BControl::PreferredSize();
480 			return B_OK;
481 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
482 			((perform_data_layout_alignment*)_data)->return_value
483 				= BControl::LayoutAlignment();
484 			return B_OK;
485 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
486 			((perform_data_has_height_for_width*)_data)->return_value
487 				= BControl::HasHeightForWidth();
488 			return B_OK;
489 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
490 		{
491 			perform_data_get_height_for_width* data
492 				= (perform_data_get_height_for_width*)_data;
493 			BControl::GetHeightForWidth(data->width, &data->min, &data->max,
494 				&data->preferred);
495 			return B_OK;
496 }
497 		case PERFORM_CODE_SET_LAYOUT:
498 		{
499 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
500 			BControl::SetLayout(data->layout);
501 			return B_OK;
502 		}
503 		case PERFORM_CODE_LAYOUT_INVALIDATED:
504 		{
505 			perform_data_layout_invalidated* data
506 				= (perform_data_layout_invalidated*)_data;
507 			BControl::LayoutInvalidated(data->descendants);
508 			return B_OK;
509 		}
510 		case PERFORM_CODE_DO_LAYOUT:
511 		{
512 			BControl::DoLayout();
513 			return B_OK;
514 		}
515 		case PERFORM_CODE_SET_ICON:
516 		{
517 			perform_data_set_icon* data = (perform_data_set_icon*)_data;
518 			return BControl::SetIcon(data->icon, data->flags);
519 		}
520 	}
521 
522 	return BView::Perform(code, _data);
523 }
524 
525 
526 status_t
527 BControl::SetIcon(const BBitmap* bitmap, uint32 flags)
528 {
529 	status_t error = BIcon::UpdateIcon(bitmap, flags, fIcon);
530 
531 	if (error == B_OK) {
532 		InvalidateLayout();
533 		Invalidate();
534 	}
535 
536 	return error;
537 }
538 
539 
540 status_t
541 BControl::SetIconBitmap(const BBitmap* bitmap, uint32 which, uint32 flags)
542 {
543 	status_t error = BIcon::SetIconBitmap(bitmap, which, flags, fIcon);
544 
545 	if (error != B_OK) {
546 		InvalidateLayout();
547 		Invalidate();
548 	}
549 
550 	return error;
551 }
552 
553 
554 const BBitmap*
555 BControl::IconBitmap(uint32 which) const
556 {
557 	return fIcon != NULL ? fIcon->Bitmap(which) : NULL;
558 }
559 
560 
561 bool
562 BControl::IsFocusChanging() const
563 {
564 	return fFocusChanging;
565 }
566 
567 
568 bool
569 BControl::IsTracking() const
570 {
571 	return fTracking;
572 }
573 
574 
575 void
576 BControl::SetTracking(bool state)
577 {
578 	fTracking = state;
579 }
580 
581 
582 extern "C" status_t
583 B_IF_GCC_2(_ReservedControl1__8BControl, _ZN8BControl17_ReservedControl1Ev)(
584 	BControl* control, const BBitmap* icon, uint32 flags)
585 {
586 	// SetIcon()
587 	perform_data_set_icon data;
588 	data.icon = icon;
589 	data.flags = flags;
590 	return control->Perform(PERFORM_CODE_SET_ICON, &data);
591 }
592 
593 
594 void BControl::_ReservedControl2() {}
595 void BControl::_ReservedControl3() {}
596 void BControl::_ReservedControl4() {}
597 
598 
599 BControl &
600 BControl::operator=(const BControl &)
601 {
602 	return *this;
603 }
604 
605 
606 void
607 BControl::InitData(BMessage *data)
608 {
609 	fLabel = NULL;
610 	SetLabel(B_EMPTY_STRING);
611 	fValue = B_CONTROL_OFF;
612 	fEnabled = true;
613 	fFocusChanging = false;
614 	fTracking = false;
615 	fWantsNav = Flags() & B_NAVIGABLE;
616 	fIcon = NULL;
617 
618 	if (data && data->HasString("_fname"))
619 		SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE);
620 }
621 
622