xref: /haiku/src/kits/interface/Control.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2001-2006, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  */
8 
9 /*!	BControl is the base class for user-event handling objects. */
10 
11 
12 #include <Control.h>
13 #include <PropertyInfo.h>
14 #include <Window.h>
15 
16 #include <stdlib.h>
17 #include <string.h>
18 
19 
20 static property_info sPropertyList[] = {
21 	{
22 		"Enabled",
23 		{ B_GET_PROPERTY, B_SET_PROPERTY },
24 		{ B_DIRECT_SPECIFIER },
25 		NULL, 0,
26 		{ B_BOOL_TYPE }
27 	},
28 	{
29 		"Label",
30 		{ B_GET_PROPERTY, B_SET_PROPERTY },
31 		{ B_DIRECT_SPECIFIER },
32 		NULL, 0,
33 		{ B_STRING_TYPE }
34 	},
35 	{
36 		"Value",
37 		{ B_GET_PROPERTY, B_SET_PROPERTY },
38 		{ B_DIRECT_SPECIFIER },
39 		NULL, 0,
40 		{ B_INT32_TYPE }
41 	},
42 	{}
43 };
44 
45 
46 BControl::BControl(BRect frame, const char *name, const char *label,
47 	BMessage *message, uint32 resizingMode, uint32 flags)
48 	: BView(frame, name, resizingMode, flags)
49 {
50 	InitData(NULL);
51 
52 	SetLabel(label);
53 	SetMessage(message);
54 }
55 
56 
57 BControl::BControl(const char *name, const char *label, BMessage *message,
58 	uint32 flags)
59 	: BView(name, flags)
60 {
61 	InitData(NULL);
62 
63 	SetLabel(label);
64 	SetMessage(message);
65 }
66 
67 
68 BControl::~BControl()
69 {
70 	free(fLabel);
71 	SetMessage(NULL);
72 }
73 
74 
75 BControl::BControl(BMessage *archive)
76 	: BView(archive)
77 {
78 	InitData(archive);
79 
80 	BMessage message;
81 	if (archive->FindMessage("_msg", &message) == B_OK)
82 		SetMessage(new BMessage(message));
83 
84 	const char *label;
85 	if (archive->FindString("_label", &label) == B_OK)
86 		SetLabel(label);
87 
88 	int32 value;
89 	if (archive->FindInt32("_val", &value) == B_OK)
90 		SetValue(value);
91 
92 	bool toggle;
93 	if (archive->FindBool("_disable", &toggle) == B_OK)
94 		SetEnabled(!toggle);
95 
96 	if (archive->FindBool("be:wants_nav", &toggle) == B_OK)
97 		fWantsNav = toggle;
98 }
99 
100 
101 BArchivable *
102 BControl::Instantiate(BMessage *archive)
103 {
104 	if (validate_instantiation(archive, "BControl"))
105 		return new BControl(archive);
106 
107 	return NULL;
108 }
109 
110 
111 status_t
112 BControl::Archive(BMessage *archive, bool deep) const
113 {
114 	status_t status = BView::Archive(archive, deep);
115 
116 	if (status == B_OK && Message())
117 		status = archive->AddMessage("_msg", Message());
118 
119 	if (status == B_OK && fLabel)
120 		status = archive->AddString("_label", fLabel);
121 
122 	if (status == B_OK && fValue != B_CONTROL_OFF)
123 		status = archive->AddInt32("_val", fValue);
124 
125 	if (status == B_OK && !fEnabled)
126 		status = archive->AddBool("_disable", true);
127 
128 	return status;
129 }
130 
131 
132 void
133 BControl::WindowActivated(bool active)
134 {
135 	BView::WindowActivated(active);
136 
137 	if (IsFocus())
138 		Invalidate();
139 }
140 
141 
142 void
143 BControl::AttachedToWindow()
144 {
145 	BView* parent = Parent();
146 	if (parent != NULL) {
147 		// inherit the color from parent
148 		rgb_color color = parent->ViewColor();
149 		if (color == B_TRANSPARENT_COLOR)
150 			color = ui_color(B_PANEL_BACKGROUND_COLOR);
151 
152 		SetViewColor(color);
153 		SetLowColor(color);
154 	}
155 
156 	if (!Messenger().IsValid())
157 		SetTarget(Window());
158 
159 	BView::AttachedToWindow();
160 }
161 
162 
163 void
164 BControl::MessageReceived(BMessage *message)
165 {
166 	if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) {
167 		BMessage reply(B_REPLY);
168 		bool handled = false;
169 
170 		BMessage specifier;
171 		int32 index;
172 		int32 form;
173 		const char *property;
174 		if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) {
175 			if (strcmp(property, "Label") == 0) {
176 				if (message->what == B_GET_PROPERTY) {
177 					reply.AddString("result", fLabel);
178 					handled = true;
179 				} else {
180 					// B_GET_PROPERTY
181 					const char *label;
182 					if (message->FindString("data", &label) == B_OK) {
183 						SetLabel(label);
184 						reply.AddInt32("error", B_OK);
185 						handled = true;
186 					}
187 				}
188 			} else if (strcmp(property, "Value") == 0) {
189 				if (message->what == B_GET_PROPERTY) {
190 					reply.AddInt32("result", fValue);
191 					handled = true;
192 				} else {
193 					// B_GET_PROPERTY
194 					int32 value;
195 					if (message->FindInt32("data", &value) == B_OK) {
196 						SetValue(value);
197 						reply.AddInt32("error", B_OK);
198 						handled = true;
199 					}
200 				}
201 			} else if (strcmp(property, "Enabled") == 0) {
202 				if (message->what == B_GET_PROPERTY) {
203 					reply.AddBool("result", fEnabled);
204 					handled = true;
205 				} else {
206 					// B_GET_PROPERTY
207 					bool enabled;
208 					if (message->FindBool("data", &enabled) == B_OK) {
209 						SetEnabled(enabled);
210 						reply.AddInt32("error", B_OK);
211 						handled = true;
212 					}
213 				}
214 			}
215 		}
216 
217 		if (handled) {
218 			message->SendReply(&reply);
219 			return;
220 		}
221 	}
222 
223 	BView::MessageReceived(message);
224 }
225 
226 
227 void
228 BControl::MakeFocus(bool focused)
229 {
230 	if (focused == IsFocus())
231 		return;
232 
233 	BView::MakeFocus(focused);
234 
235  	if (Window()) {
236 		fFocusChanging = true;
237 		Invalidate(Bounds());
238 		Flush();
239 		fFocusChanging = false;
240 	}
241 }
242 
243 
244 void
245 BControl::KeyDown(const char *bytes, int32 numBytes)
246 {
247 	if (*bytes == B_ENTER || *bytes == B_SPACE) {
248 		if (!fEnabled)
249 			return;
250 
251 		SetValue(Value() ? B_CONTROL_OFF : B_CONTROL_ON);
252 		Invoke();
253 	} else
254 		BView::KeyDown(bytes, numBytes);
255 }
256 
257 
258 void
259 BControl::MouseDown(BPoint point)
260 {
261 	BView::MouseDown(point);
262 }
263 
264 
265 void
266 BControl::MouseUp(BPoint point)
267 {
268 	BView::MouseUp(point);
269 }
270 
271 
272 void
273 BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
274 {
275 	BView::MouseMoved(point, transit, message);
276 }
277 
278 
279 void
280 BControl::DetachedFromWindow()
281 {
282 	BView::DetachedFromWindow();
283 }
284 
285 
286 void
287 BControl::SetLabel(const char *label)
288 {
289 	if (label != NULL && !label[0])
290 		label = NULL;
291 
292 	// Has the label been changed?
293 	if ((fLabel && label && !strcmp(fLabel, label))
294 		|| ((fLabel == NULL || !fLabel[0]) && label == NULL))
295 		return;
296 
297 	free(fLabel);
298 	fLabel = label ? strdup(label) : NULL;
299 
300 	InvalidateLayout();
301 	Invalidate();
302 }
303 
304 
305 const char *
306 BControl::Label() const
307 {
308 	return fLabel;
309 }
310 
311 
312 void
313 BControl::SetValue(int32 value)
314 {
315 	if (value == fValue)
316 		return;
317 
318 	fValue = value;
319 	Invalidate();
320 }
321 
322 
323 void
324 BControl::SetValueNoUpdate(int32 value)
325 {
326 	fValue = value;
327 }
328 
329 
330 int32
331 BControl::Value() const
332 {
333 	return fValue;
334 }
335 
336 
337 void
338 BControl::SetEnabled(bool enabled)
339 {
340 	if (fEnabled == enabled)
341 		return;
342 
343 	fEnabled = enabled;
344 
345 	if (fEnabled)
346 		BView::SetFlags(Flags() | B_NAVIGABLE);
347 	else
348 		BView::SetFlags(Flags() & ~B_NAVIGABLE);
349 
350 	if (Window()) {
351 		Invalidate(Bounds());
352 		Flush();
353 	}
354 }
355 
356 
357 bool
358 BControl::IsEnabled() const
359 {
360 	return fEnabled;
361 }
362 
363 
364 void
365 BControl::GetPreferredSize(float *_width, float *_height)
366 {
367 	BView::GetPreferredSize(_width, _height);
368 }
369 
370 
371 void
372 BControl::ResizeToPreferred()
373 {
374 	BView::ResizeToPreferred();
375 }
376 
377 
378 status_t
379 BControl::Invoke(BMessage *message)
380 {
381 	bool notify = false;
382 	uint32 kind = InvokeKind(&notify);
383 
384 	if (!message && !notify)
385 		message = Message();
386 
387 	BMessage clone(kind);
388 
389 	if (!message) {
390 		if (!IsWatched())
391 			return B_BAD_VALUE;
392 	} else
393 		clone = *message;
394 
395 	clone.AddInt64("when", (int64)system_time());
396 	clone.AddPointer("source", this);
397 	clone.AddInt32("be:value", fValue);
398 	clone.AddMessenger("be:sender", BMessenger(this));
399 
400 	// ToDo: is this correct? If message == NULL (even if IsWatched()), we always return B_BAD_VALUE
401 	status_t err;
402 	if (message)
403 		err = BInvoker::Invoke(&clone);
404 	else
405 		err = B_BAD_VALUE;
406 
407 	// TODO: asynchronous messaging
408 	SendNotices(kind, &clone);
409 
410 	return err;
411 }
412 
413 
414 BHandler *
415 BControl::ResolveSpecifier(BMessage *message, int32 index,
416 	BMessage *specifier, int32 what, const char *property)
417 {
418 	BPropertyInfo propInfo(sPropertyList);
419 
420 	if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK)
421 		return this;
422 
423 	return BView::ResolveSpecifier(message, index, specifier, what,
424 		property);
425 }
426 
427 
428 status_t
429 BControl::GetSupportedSuites(BMessage *message)
430 {
431 	message->AddString("suites", "suite/vnd.Be-control");
432 
433 	BPropertyInfo propInfo(sPropertyList);
434 	message->AddFlat("messages", &propInfo);
435 
436 	return BView::GetSupportedSuites(message);
437 }
438 
439 
440 void
441 BControl::AllAttached()
442 {
443 	BView::AllAttached();
444 }
445 
446 
447 void
448 BControl::AllDetached()
449 {
450 	BView::AllDetached();
451 }
452 
453 
454 status_t
455 BControl::Perform(perform_code d, void *arg)
456 {
457 	return BView::Perform(d, arg);
458 }
459 
460 
461 bool
462 BControl::IsFocusChanging() const
463 {
464 	return fFocusChanging;
465 }
466 
467 
468 bool
469 BControl::IsTracking() const
470 {
471 	return fTracking;
472 }
473 
474 
475 void
476 BControl::SetTracking(bool state)
477 {
478 	fTracking = state;
479 }
480 
481 
482 void BControl::_ReservedControl1() {}
483 void BControl::_ReservedControl2() {}
484 void BControl::_ReservedControl3() {}
485 void BControl::_ReservedControl4() {}
486 
487 
488 BControl &
489 BControl::operator=(const BControl &)
490 {
491 	return *this;
492 }
493 
494 
495 void
496 BControl::InitData(BMessage *data)
497 {
498 	fLabel = NULL;
499 	SetLabel(B_EMPTY_STRING);
500 	fValue = B_CONTROL_OFF;
501 	fEnabled = true;
502 	fFocusChanging = false;
503 	fTracking = false;
504 	fWantsNav = true;
505 
506 	if (data && data->HasString("_fname"))
507 		SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE);
508 }
509 
510