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