xref: /haiku/src/kits/interface/Control.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 <stdlib.h>
13 #include <string.h>
14 
15 #include <Control.h>
16 #include <PropertyInfo.h>
17 #include <Window.h>
18 
19 #include <binary_compatibility/Interface.h>
20 
21 
22 static property_info sPropertyList[] = {
23 	{
24 		"Enabled",
25 		{ B_GET_PROPERTY, B_SET_PROPERTY },
26 		{ B_DIRECT_SPECIFIER },
27 		NULL, 0,
28 		{ B_BOOL_TYPE }
29 	},
30 	{
31 		"Label",
32 		{ B_GET_PROPERTY, B_SET_PROPERTY },
33 		{ B_DIRECT_SPECIFIER },
34 		NULL, 0,
35 		{ B_STRING_TYPE }
36 	},
37 	{
38 		"Value",
39 		{ B_GET_PROPERTY, B_SET_PROPERTY },
40 		{ B_DIRECT_SPECIFIER },
41 		NULL, 0,
42 		{ B_INT32_TYPE }
43 	},
44 	{}
45 };
46 
47 
48 BControl::BControl(BRect frame, const char *name, const char *label,
49 	BMessage *message, uint32 resizingMode, uint32 flags)
50 	: BView(frame, name, resizingMode, flags)
51 {
52 	InitData(NULL);
53 
54 	SetLabel(label);
55 	SetMessage(message);
56 }
57 
58 
59 BControl::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 
70 BControl::~BControl()
71 {
72 	free(fLabel);
73 	SetMessage(NULL);
74 }
75 
76 
77 BControl::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 
103 BArchivable *
104 BControl::Instantiate(BMessage *archive)
105 {
106 	if (validate_instantiation(archive, "BControl"))
107 		return new BControl(archive);
108 
109 	return NULL;
110 }
111 
112 
113 status_t
114 BControl::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 
134 void
135 BControl::WindowActivated(bool active)
136 {
137 	BView::WindowActivated(active);
138 
139 	if (IsFocus())
140 		Invalidate();
141 }
142 
143 
144 void
145 BControl::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 
168 void
169 BControl::DetachedFromWindow()
170 {
171 	BView::DetachedFromWindow();
172 }
173 
174 
175 void
176 BControl::AllAttached()
177 {
178 	BView::AllAttached();
179 }
180 
181 
182 void
183 BControl::AllDetached()
184 {
185 	BView::AllDetached();
186 }
187 
188 
189 void
190 BControl::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 
253 void
254 BControl::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 
270 void
271 BControl::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 
284 void
285 BControl::MouseDown(BPoint point)
286 {
287 	BView::MouseDown(point);
288 }
289 
290 
291 void
292 BControl::MouseUp(BPoint point)
293 {
294 	BView::MouseUp(point);
295 }
296 
297 
298 void
299 BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
300 {
301 	BView::MouseMoved(point, transit, message);
302 }
303 
304 
305 void
306 BControl::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 
324 const char *
325 BControl::Label() const
326 {
327 	return fLabel;
328 }
329 
330 
331 void
332 BControl::SetValue(int32 value)
333 {
334 	if (value == fValue)
335 		return;
336 
337 	fValue = value;
338 	Invalidate();
339 }
340 
341 
342 void
343 BControl::SetValueNoUpdate(int32 value)
344 {
345 	fValue = value;
346 }
347 
348 
349 int32
350 BControl::Value() const
351 {
352 	return fValue;
353 }
354 
355 
356 void
357 BControl::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 
379 bool
380 BControl::IsEnabled() const
381 {
382 	return fEnabled;
383 }
384 
385 
386 void
387 BControl::GetPreferredSize(float *_width, float *_height)
388 {
389 	BView::GetPreferredSize(_width, _height);
390 }
391 
392 
393 void
394 BControl::ResizeToPreferred()
395 {
396 	BView::ResizeToPreferred();
397 }
398 
399 
400 status_t
401 BControl::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 
436 BHandler *
437 BControl::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 
450 status_t
451 BControl::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 
462 status_t
463 BControl::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 
518 bool
519 BControl::IsFocusChanging() const
520 {
521 	return fFocusChanging;
522 }
523 
524 
525 bool
526 BControl::IsTracking() const
527 {
528 	return fTracking;
529 }
530 
531 
532 void
533 BControl::SetTracking(bool state)
534 {
535 	fTracking = state;
536 }
537 
538 
539 void BControl::_ReservedControl1() {}
540 void BControl::_ReservedControl2() {}
541 void BControl::_ReservedControl3() {}
542 void BControl::_ReservedControl4() {}
543 
544 
545 BControl &
546 BControl::operator=(const BControl &)
547 {
548 	return *this;
549 }
550 
551 
552 void
553 BControl::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