1177548Scsjp/*
2177548Scsjp * Copyright 2001-2006, Haiku Inc.
3177548Scsjp * Distributed under the terms of the MIT License.
4177548Scsjp *
5230108Seadler * Authors:
6177548Scsjp *		Marc Flerackers (mflerackers@androme.be)
7177548Scsjp */
8177548Scsjp
9177548Scsjp/*!	BControl is the base class for user-event handling objects. */
10177548Scsjp
11177548Scsjp
12177548Scsjp#include <stdlib.h>
13177548Scsjp#include <string.h>
14177548Scsjp
15177548Scsjp#include <Control.h>
16177548Scsjp#include <PropertyInfo.h>
17177548Scsjp#include <Window.h>
18177548Scsjp
19177548Scsjp#include <binary_compatibility/Interface.h>
20177548Scsjp
21177548Scsjp
22177548Scsjpstatic property_info sPropertyList[] = {
23177548Scsjp	{
24177548Scsjp		"Enabled",
25177548Scsjp		{ B_GET_PROPERTY, B_SET_PROPERTY },
26177548Scsjp		{ B_DIRECT_SPECIFIER },
27177548Scsjp		NULL, 0,
28177548Scsjp		{ B_BOOL_TYPE }
29177548Scsjp	},
30177548Scsjp	{
31177548Scsjp		"Label",
32177548Scsjp		{ B_GET_PROPERTY, B_SET_PROPERTY },
33177548Scsjp		{ B_DIRECT_SPECIFIER },
34177548Scsjp		NULL, 0,
35177548Scsjp		{ B_STRING_TYPE }
36177548Scsjp	},
37177548Scsjp	{
38177548Scsjp		"Value",
39177548Scsjp		{ B_GET_PROPERTY, B_SET_PROPERTY },
40177548Scsjp		{ B_DIRECT_SPECIFIER },
41177548Scsjp		NULL, 0,
42177548Scsjp		{ B_INT32_TYPE }
43177966Srwatson	},
44177548Scsjp	{}
45180310Scsjp};
46177548Scsjp
47177966Srwatson
48177548ScsjpBControl::BControl(BRect frame, const char *name, const char *label,
49177548Scsjp	BMessage *message, uint32 resizingMode, uint32 flags)
50177548Scsjp	: BView(frame, name, resizingMode, flags)
51177548Scsjp{
52177548Scsjp	InitData(NULL);
53177548Scsjp
54177548Scsjp	SetLabel(label);
55177548Scsjp	SetMessage(message);
56177548Scsjp}
57
58
59BControl::BControl(const char *name, const char *label, BMessage *message,
60	uint32 flags)
61	: BView(name, flags)
62{
63	InitData(NULL);
64
65	SetLabel(label);
66	SetMessage(message);
67}
68
69
70BControl::~BControl()
71{
72	free(fLabel);
73	SetMessage(NULL);
74}
75
76
77BControl::BControl(BMessage *archive)
78	: BView(archive)
79{
80	InitData(archive);
81
82	BMessage message;
83	if (archive->FindMessage("_msg", &message) == B_OK)
84		SetMessage(new BMessage(message));
85
86	const char *label;
87	if (archive->FindString("_label", &label) == B_OK)
88		SetLabel(label);
89
90	int32 value;
91	if (archive->FindInt32("_val", &value) == B_OK)
92		SetValue(value);
93
94	bool toggle;
95	if (archive->FindBool("_disable", &toggle) == B_OK)
96		SetEnabled(!toggle);
97
98	if (archive->FindBool("be:wants_nav", &toggle) == B_OK)
99		fWantsNav = toggle;
100}
101
102
103BArchivable *
104BControl::Instantiate(BMessage *archive)
105{
106	if (validate_instantiation(archive, "BControl"))
107		return new BControl(archive);
108
109	return NULL;
110}
111
112
113status_t
114BControl::Archive(BMessage *archive, bool deep) const
115{
116	status_t status = BView::Archive(archive, deep);
117
118	if (status == B_OK && Message())
119		status = archive->AddMessage("_msg", Message());
120
121	if (status == B_OK && fLabel)
122		status = archive->AddString("_label", fLabel);
123
124	if (status == B_OK && fValue != B_CONTROL_OFF)
125		status = archive->AddInt32("_val", fValue);
126
127	if (status == B_OK && !fEnabled)
128		status = archive->AddBool("_disable", true);
129
130	return status;
131}
132
133
134void
135BControl::WindowActivated(bool active)
136{
137	BView::WindowActivated(active);
138
139	if (IsFocus())
140		Invalidate();
141}
142
143
144void
145BControl::AttachedToWindow()
146{
147	rgb_color color;
148
149	BView* parent = Parent();
150	if (parent != NULL) {
151		// inherit the color from parent
152		color = parent->ViewColor();
153		if (color == B_TRANSPARENT_COLOR)
154			color = ui_color(B_PANEL_BACKGROUND_COLOR);
155	} else
156		color = ui_color(B_PANEL_BACKGROUND_COLOR);
157
158	SetViewColor(color);
159	SetLowColor(color);
160
161	if (!Messenger().IsValid())
162		SetTarget(Window());
163
164	BView::AttachedToWindow();
165}
166
167
168void
169BControl::DetachedFromWindow()
170{
171	BView::DetachedFromWindow();
172}
173
174
175void
176BControl::AllAttached()
177{
178	BView::AllAttached();
179}
180
181
182void
183BControl::AllDetached()
184{
185	BView::AllDetached();
186}
187
188
189void
190BControl::MessageReceived(BMessage *message)
191{
192	if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
193		BMessage reply(B_REPLY);
194		bool handled = false;
195
196		BMessage specifier;
197		int32 index;
198		int32 form;
199		const char *property;
200		if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
201			if (strcmp(property, "Label") == 0) {
202				if (message->what == B_GET_PROPERTY) {
203					reply.AddString("result", fLabel);
204					handled = true;
205				} else {
206					// B_SET_PROPERTY
207					const char *label;
208					if (message->FindString("data", &label) == B_OK) {
209						SetLabel(label);
210						reply.AddInt32("error", B_OK);
211						handled = true;
212					}
213				}
214			} else if (strcmp(property, "Value") == 0) {
215				if (message->what == B_GET_PROPERTY) {
216					reply.AddInt32("result", fValue);
217					handled = true;
218				} else {
219					// B_SET_PROPERTY
220					int32 value;
221					if (message->FindInt32("data", &value) == B_OK) {
222						SetValue(value);
223						reply.AddInt32("error", B_OK);
224						handled = true;
225					}
226				}
227			} else if (strcmp(property, "Enabled") == 0) {
228				if (message->what == B_GET_PROPERTY) {
229					reply.AddBool("result", fEnabled);
230					handled = true;
231				} else {
232					// B_SET_PROPERTY
233					bool enabled;
234					if (message->FindBool("data", &enabled) == B_OK) {
235						SetEnabled(enabled);
236						reply.AddInt32("error", B_OK);
237						handled = true;
238					}
239				}
240			}
241		}
242
243		if (handled) {
244			message->SendReply(&reply);
245			return;
246		}
247	}
248
249	BView::MessageReceived(message);
250}
251
252
253void
254BControl::MakeFocus(bool focused)
255{
256	if (focused == IsFocus())
257		return;
258
259	BView::MakeFocus(focused);
260
261 	if (Window()) {
262		fFocusChanging = true;
263		Invalidate(Bounds());
264		Flush();
265		fFocusChanging = false;
266	}
267}
268
269
270void
271BControl::KeyDown(const char *bytes, int32 numBytes)
272{
273	if (*bytes == B_ENTER || *bytes == B_SPACE) {
274		if (!fEnabled)
275			return;
276
277		SetValue(Value() ? B_CONTROL_OFF : B_CONTROL_ON);
278		Invoke();
279	} else
280		BView::KeyDown(bytes, numBytes);
281}
282
283
284void
285BControl::MouseDown(BPoint point)
286{
287	BView::MouseDown(point);
288}
289
290
291void
292BControl::MouseUp(BPoint point)
293{
294	BView::MouseUp(point);
295}
296
297
298void
299BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
300{
301	BView::MouseMoved(point, transit, message);
302}
303
304
305void
306BControl::SetLabel(const char *label)
307{
308	if (label != NULL && !label[0])
309		label = NULL;
310
311	// Has the label been changed?
312	if ((fLabel && label && !strcmp(fLabel, label))
313		|| ((fLabel == NULL || !fLabel[0]) && label == NULL))
314		return;
315
316	free(fLabel);
317	fLabel = label ? strdup(label) : NULL;
318
319	InvalidateLayout();
320	Invalidate();
321}
322
323
324const char *
325BControl::Label() const
326{
327	return fLabel;
328}
329
330
331void
332BControl::SetValue(int32 value)
333{
334	if (value == fValue)
335		return;
336
337	fValue = value;
338	Invalidate();
339}
340
341
342void
343BControl::SetValueNoUpdate(int32 value)
344{
345	fValue = value;
346}
347
348
349int32
350BControl::Value() const
351{
352	return fValue;
353}
354
355
356void
357BControl::SetEnabled(bool enabled)
358{
359	if (fEnabled == enabled)
360		return;
361
362	fEnabled = enabled;
363
364	if (fEnabled && fWantsNav)
365		SetFlags(Flags() | B_NAVIGABLE);
366	else if (!fEnabled && (Flags() & B_NAVIGABLE)) {
367		fWantsNav = true;
368		SetFlags(Flags() & ~B_NAVIGABLE);
369	} else
370		fWantsNav = false;
371
372	if (Window()) {
373		Invalidate(Bounds());
374		Flush();
375	}
376}
377
378
379bool
380BControl::IsEnabled() const
381{
382	return fEnabled;
383}
384
385
386void
387BControl::GetPreferredSize(float *_width, float *_height)
388{
389	BView::GetPreferredSize(_width, _height);
390}
391
392
393void
394BControl::ResizeToPreferred()
395{
396	BView::ResizeToPreferred();
397}
398
399
400status_t
401BControl::Invoke(BMessage *message)
402{
403	bool notify = false;
404	uint32 kind = InvokeKind(&notify);
405
406	if (!message && !notify)
407		message = Message();
408
409	BMessage clone(kind);
410
411	if (!message) {
412		if (!IsWatched())
413			return B_BAD_VALUE;
414	} else
415		clone = *message;
416
417	clone.AddInt64("when", (int64)system_time());
418	clone.AddPointer("source", this);
419	clone.AddInt32("be:value", fValue);
420	clone.AddMessenger("be:sender", BMessenger(this));
421
422	// ToDo: is this correct? If message == NULL (even if IsWatched()), we always return B_BAD_VALUE
423	status_t err;
424	if (message)
425		err = BInvoker::Invoke(&clone);
426	else
427		err = B_BAD_VALUE;
428
429	// TODO: asynchronous messaging
430	SendNotices(kind, &clone);
431
432	return err;
433}
434
435
436BHandler *
437BControl::ResolveSpecifier(BMessage *message, int32 index,
438	BMessage *specifier, int32 what, const char *property)
439{
440	BPropertyInfo propInfo(sPropertyList);
441
442	if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
443		return this;
444
445	return BView::ResolveSpecifier(message, index, specifier, what,
446		property);
447}
448
449
450status_t
451BControl::GetSupportedSuites(BMessage *message)
452{
453	message->AddString("suites", "suite/vnd.Be-control");
454
455	BPropertyInfo propInfo(sPropertyList);
456	message->AddFlat("messages", &propInfo);
457
458	return BView::GetSupportedSuites(message);
459}
460
461
462status_t
463BControl::Perform(perform_code code, void* _data)
464{
465	switch (code) {
466		case PERFORM_CODE_MIN_SIZE:
467			((perform_data_min_size*)_data)->return_value
468				= BControl::MinSize();
469			return B_OK;
470		case PERFORM_CODE_MAX_SIZE:
471			((perform_data_max_size*)_data)->return_value
472				= BControl::MaxSize();
473			return B_OK;
474		case PERFORM_CODE_PREFERRED_SIZE:
475			((perform_data_preferred_size*)_data)->return_value
476				= BControl::PreferredSize();
477			return B_OK;
478		case PERFORM_CODE_LAYOUT_ALIGNMENT:
479			((perform_data_layout_alignment*)_data)->return_value
480				= BControl::LayoutAlignment();
481			return B_OK;
482		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
483			((perform_data_has_height_for_width*)_data)->return_value
484				= BControl::HasHeightForWidth();
485			return B_OK;
486		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
487		{
488			perform_data_get_height_for_width* data
489				= (perform_data_get_height_for_width*)_data;
490			BControl::GetHeightForWidth(data->width, &data->min, &data->max,
491				&data->preferred);
492			return B_OK;
493}
494		case PERFORM_CODE_SET_LAYOUT:
495		{
496			perform_data_set_layout* data = (perform_data_set_layout*)_data;
497			BControl::SetLayout(data->layout);
498			return B_OK;
499		}
500		case PERFORM_CODE_LAYOUT_INVALIDATED:
501		{
502			perform_data_layout_invalidated* data
503				= (perform_data_layout_invalidated*)_data;
504			BControl::LayoutInvalidated(data->descendants);
505			return B_OK;
506		}
507		case PERFORM_CODE_DO_LAYOUT:
508		{
509			BControl::DoLayout();
510			return B_OK;
511		}
512	}
513
514	return BView::Perform(code, _data);
515}
516
517
518bool
519BControl::IsFocusChanging() const
520{
521	return fFocusChanging;
522}
523
524
525bool
526BControl::IsTracking() const
527{
528	return fTracking;
529}
530
531
532void
533BControl::SetTracking(bool state)
534{
535	fTracking = state;
536}
537
538
539void BControl::_ReservedControl1() {}
540void BControl::_ReservedControl2() {}
541void BControl::_ReservedControl3() {}
542void BControl::_ReservedControl4() {}
543
544
545BControl &
546BControl::operator=(const BControl &)
547{
548	return *this;
549}
550
551
552void
553BControl::InitData(BMessage *data)
554{
555	fLabel = NULL;
556	SetLabel(B_EMPTY_STRING);
557	fValue = B_CONTROL_OFF;
558	fEnabled = true;
559	fFocusChanging = false;
560	fTracking = false;
561	fWantsNav = Flags() & B_NAVIGABLE;
562
563	if (data && data->HasString("_fname"))
564		SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE);
565}
566
567