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