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