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