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
25static 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
52BControl::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
64BControl::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
76BControl::~BControl()
77{
78	free(fLabel);
79	delete fIcon;
80	SetMessage(NULL);
81}
82
83
84BControl::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
111BArchivable*
112BControl::Instantiate(BMessage* data)
113{
114	if (validate_instantiation(data, "BControl"))
115		return new BControl(data);
116
117	return NULL;
118}
119
120
121status_t
122BControl::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
142void
143BControl::WindowActivated(bool active)
144{
145	BView::WindowActivated(active);
146
147	if (IsFocus())
148		Invalidate();
149}
150
151
152void
153BControl::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
179void
180BControl::DetachedFromWindow()
181{
182	BView::DetachedFromWindow();
183}
184
185
186void
187BControl::AllAttached()
188{
189	BView::AllAttached();
190}
191
192
193void
194BControl::AllDetached()
195{
196	BView::AllDetached();
197}
198
199
200void
201BControl::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
264void
265BControl::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
281void
282BControl::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
295void
296BControl::MouseDown(BPoint where)
297{
298	BView::MouseDown(where);
299}
300
301
302void
303BControl::MouseUp(BPoint where)
304{
305	BView::MouseUp(where);
306}
307
308
309void
310BControl::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
311{
312	BView::MouseMoved(where, code, dragMessage);
313}
314
315
316void
317BControl::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
335const char*
336BControl::Label() const
337{
338	return fLabel;
339}
340
341
342void
343BControl::SetValue(int32 value)
344{
345	if (value == fValue)
346		return;
347
348	fValue = value;
349	Invalidate();
350}
351
352
353void
354BControl::SetValueNoUpdate(int32 value)
355{
356	fValue = value;
357}
358
359
360int32
361BControl::Value() const
362{
363	return fValue;
364}
365
366
367void
368BControl::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
390bool
391BControl::IsEnabled() const
392{
393	return fEnabled;
394}
395
396
397void
398BControl::GetPreferredSize(float* _width, float* _height)
399{
400	BView::GetPreferredSize(_width, _height);
401}
402
403
404void
405BControl::ResizeToPreferred()
406{
407	BView::ResizeToPreferred();
408}
409
410
411status_t
412BControl::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
447BHandler*
448BControl::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
461status_t
462BControl::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
473status_t
474BControl::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
534status_t
535BControl::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
548status_t
549BControl::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
562const BBitmap*
563BControl::IconBitmap(uint32 which) const
564{
565	return fIcon != NULL ? fIcon->Bitmap(which) : NULL;
566}
567
568
569bool
570BControl::IsFocusChanging() const
571{
572	return fFocusChanging;
573}
574
575
576bool
577BControl::IsTracking() const
578{
579	return fTracking;
580}
581
582
583void
584BControl::SetTracking(bool state)
585{
586	fTracking = state;
587}
588
589
590extern "C" status_t
591B_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
602void BControl::_ReservedControl2() {}
603void BControl::_ReservedControl3() {}
604void BControl::_ReservedControl4() {}
605
606
607BControl &
608BControl::operator=(const BControl &)
609{
610	return *this;
611}
612
613
614void
615BControl::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