xref: /haiku/src/kits/interface/Box.cpp (revision a4f6a81235ca2522c01f532de13cad9b729d4029)
1 /*
2  * Copyright (c) 2001-2005, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		DarkWyrm <bpmagic@columbus.rr.com>
9  *		Axel Dörfler, axeld@pinc-software.de
10  */
11 
12 
13 #include <Box.h>
14 #include <Message.h>
15 #include <Region.h>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 
22 BBox::BBox(BRect frame, const char *name, uint32 resizingMode, uint32 flags,
23 		border_style border)
24 	: BView(frame, name, resizingMode, flags | B_FRAME_EVENTS),
25 		fStyle(border)
26 {
27 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
28 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
29 
30 	_InitObject();
31 }
32 
33 
34 BBox::BBox(BMessage *archive)
35 	: BView(archive)
36 {
37 	_InitObject(archive);
38 
39 	const char *string;
40 
41 	if (archive->FindString("_label", &string) == B_OK)
42 		SetLabel(string);
43 
44 	bool aBool;
45 	int32 anInt32;
46 
47 	if (archive->FindBool("_style", &aBool) == B_OK)
48 		fStyle = aBool ? B_FANCY_BORDER : B_PLAIN_BORDER;
49 	else if (archive->FindInt32("_style", &anInt32) == B_OK)
50 		fStyle = (border_style)anInt32;
51 
52 	if (archive->FindBool("_lblview", &aBool) == B_OK)
53 		fLabelView = ChildAt(0);
54 }
55 
56 
57 BBox::~BBox()
58 {
59 	_ClearLabel();
60 }
61 
62 
63 BArchivable *
64 BBox::Instantiate(BMessage *archive)
65 {
66 	if (validate_instantiation(archive, "BBox"))
67 		return new BBox(archive);
68 
69 	return NULL;
70 }
71 
72 
73 status_t
74 BBox::Archive(BMessage *archive, bool deep) const
75 {
76 	BView::Archive(archive, deep);
77 
78 	if (fLabel)
79 		 archive->AddString("_label", fLabel);
80 
81 	if (fLabelView)
82 		 archive->AddBool("_lblview", true);
83 
84 	if (fStyle != B_FANCY_BORDER)
85 		archive->AddInt32("_style", fStyle);
86 
87 	return B_OK;
88 }
89 
90 
91 void
92 BBox::SetBorder(border_style border)
93 {
94 	fStyle = border;
95 
96 	if (Window() != NULL && LockLooper()) {
97 		Invalidate();
98 		UnlockLooper();
99 	}
100 }
101 
102 
103 border_style
104 BBox::Border() const
105 {
106 	return fStyle;
107 }
108 
109 
110 void
111 BBox::SetLabel(const char *string)
112 {
113 	_ClearLabel();
114 
115 	if (string) {
116 		font_height fontHeight;
117 		GetFontHeight(&fontHeight);
118 
119 		fLabel = strdup(string);
120 
121 		// leave 6 pixels of the frame, and have a gap of 4 pixels between
122 		// the frame and the text on both sides
123 		fLabelBox = new BRect(6.0f, 0, StringWidth(string) + 14.0f,
124 			ceilf(fontHeight.ascent + fontHeight.descent));
125 	}
126 
127 	if (Window())
128 		Invalidate();
129 }
130 
131 
132 status_t
133 BBox::SetLabel(BView *viewLabel)
134 {
135 	_ClearLabel();
136 
137 	if (viewLabel) {
138 		fLabelView = viewLabel;
139 		fLabelView->MoveTo(10.0f, 0.0f);
140 		AddChild(fLabelView, ChildAt(0));
141 	}
142 
143 	if (Window())
144 		Invalidate();
145 
146 	return B_OK;
147 }
148 
149 
150 const char *
151 BBox::Label() const
152 {
153 	return fLabel;
154 }
155 
156 
157 BView *
158 BBox::LabelView() const
159 {
160 	return fLabelView;
161 }
162 
163 
164 void
165 BBox::Draw(BRect updateRect)
166 {
167 	PushState();
168 
169 	BRect labelBox = BRect(0, 0, 0, 0);
170 	if (fLabel != NULL) {
171 		labelBox = *fLabelBox;
172 		BRegion update(updateRect);
173 		update.Exclude(labelBox);
174 
175 		ConstrainClippingRegion(&update);
176 	} else if (fLabelView != NULL)
177 		labelBox = fLabelView->Bounds();
178 
179 	switch (fStyle) {
180 		case B_FANCY_BORDER:
181 			_DrawFancy(labelBox);
182 			break;
183 
184 		case B_PLAIN_BORDER:
185 			_DrawPlain(labelBox);
186 			break;
187 
188 		default:
189 			break;
190 	}
191 
192 	if (fLabel) {
193 		ConstrainClippingRegion(NULL);
194 
195 		font_height fontHeight;
196 		GetFontHeight(&fontHeight);
197 /*
198 		SetHighColor(ViewColor());
199 
200 		FillRect(BRect(6.0f, 1.0f, 12.0f + StringWidth(fLabel),
201 			(float)ceil(fh.ascent + fh.descent)), B_SOLID_LOW);
202 */
203 		SetHighColor(0, 0, 0);
204 		DrawString(fLabel, BPoint(10.0f,
205 			ceilf(fontHeight.ascent - fontHeight.descent) + 1.0f));
206 	}
207 
208 	PopState();
209 }
210 
211 
212 void
213 BBox::AttachedToWindow()
214 {
215 	if (Parent()) {
216 		SetViewColor(Parent()->ViewColor());
217 		SetLowColor(Parent()->ViewColor());
218 	}
219 
220 	// The box could have been resized in the mean time
221 	fBounds = Bounds();
222 }
223 
224 
225 void
226 BBox::DetachedFromWindow()
227 {
228 	BView::DetachedFromWindow();
229 }
230 
231 
232 void
233 BBox::AllAttached()
234 {
235 	BView::AllAttached();
236 }
237 
238 
239 void
240 BBox::AllDetached()
241 {
242 	BView::AllDetached();
243 }
244 
245 
246 void
247 BBox::FrameResized(float width, float height)
248 {
249 	BRect bounds(Bounds());
250 
251 	// invalidate the regions that the app_server did not
252 	// (for removing the previous or drawing the new border)
253 	if (fStyle != B_NO_BORDER) {
254 
255 		int32 borderSize = fStyle == B_PLAIN_BORDER ? 0 : 1;
256 
257 		BRect invalid(bounds);
258 		if (fBounds.right < bounds.right) {
259 			// enlarging
260 			invalid.left = fBounds.right - borderSize;
261 			invalid.right = fBounds.right;
262 
263 			Invalidate(invalid);
264 		} else if (fBounds.right > bounds.right) {
265 			// shrinking
266 			invalid.left = bounds.right - borderSize;
267 
268 			Invalidate(invalid);
269 		}
270 
271 		invalid = bounds;
272 		if (fBounds.bottom < bounds.bottom) {
273 			// enlarging
274 			invalid.top = fBounds.bottom - borderSize;
275 			invalid.bottom = fBounds.bottom;
276 
277 			Invalidate(invalid);
278 		} else if (fBounds.bottom > bounds.bottom) {
279 			// shrinking
280 			invalid.top = bounds.bottom - borderSize;
281 
282 			Invalidate(invalid);
283 		}
284 	}
285 
286 	fBounds.right = bounds.right;
287 	fBounds.bottom = bounds.bottom;
288 }
289 
290 
291 void
292 BBox::MessageReceived(BMessage *message)
293 {
294 	BView::MessageReceived(message);
295 }
296 
297 
298 void
299 BBox::MouseDown(BPoint point)
300 {
301 	BView::MouseDown(point);
302 }
303 
304 
305 void
306 BBox::MouseUp(BPoint point)
307 {
308 	BView::MouseUp(point);
309 }
310 
311 
312 void
313 BBox::WindowActivated(bool active)
314 {
315 	BView::WindowActivated(active);
316 }
317 
318 
319 void
320 BBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
321 {
322 	BView::MouseMoved(point, transit, message);
323 }
324 
325 
326 void
327 BBox::FrameMoved(BPoint newLocation)
328 {
329 	BView::FrameMoved(newLocation);
330 }
331 
332 
333 BHandler *
334 BBox::ResolveSpecifier(BMessage *message, int32 index,
335 	BMessage *specifier, int32 what,
336 	const char *property)
337 {
338 	return BView::ResolveSpecifier(message, index, specifier, what, property);
339 }
340 
341 
342 void
343 BBox::ResizeToPreferred()
344 {
345 	BView::ResizeToPreferred();
346 }
347 
348 
349 void
350 BBox::GetPreferredSize(float *_width, float *_height)
351 {
352 	float width, height;
353 	bool label = true;
354 
355 	// acount for label
356 	if (fLabelView) {
357 		fLabelView->GetPreferredSize(&width, &height);
358 		width += 10.0;
359 			// the label view is placed 10 pixels from the left
360 	} else if (fLabel) {
361 		font_height fh;
362 		GetFontHeight(&fh);
363 		width += ceilf(StringWidth(fLabel));
364 		height += ceilf(fh.ascent + fh.descent);
365 	} else {
366 		label = false;
367 		width = 0;
368 		height = 0;
369 	}
370 
371 	// acount for border
372 	switch (fStyle) {
373 		case B_NO_BORDER:
374 			break;
375 		case B_PLAIN_BORDER:
376 			// label: (1 pixel for border + 1 pixel for padding) * 2
377 			// no label: (1 pixel for border) * 2 + 1 pixel for padding
378 			width += label ? 4 : 3;
379 			// label: 1 pixel for bottom border + 1 pixel for padding
380 			// no label: (1 pixel for border) * 2 + 1 pixel for padding
381 			height += label ? 2 : 3;
382 			break;
383 		case B_FANCY_BORDER:
384 			// label: (2 pixel for border + 1 pixel for padding) * 2
385 			// no label: (2 pixel for border) * 2 + 1 pixel for padding
386 			width += label ? 6 : 5;
387 			// label: 2 pixel for bottom border + 1 pixel for padding
388 			// no label: (2 pixel for border) * 2 + 1 pixel for padding
389 			height += label ? 3 : 5;
390 			break;
391 	}
392 	// NOTE: children are ignored, you can use BBox::GetPreferredSize()
393 	// to get the minimum size of this object, then add the size
394 	// of your child(ren) plus inner padding for the final size
395 
396 	if (_width)
397 		*_width = width;
398 	if (_height)
399 		*_height = height;
400 }
401 
402 
403 void
404 BBox::MakeFocus(bool focused)
405 {
406 	BView::MakeFocus(focused);
407 }
408 
409 
410 status_t
411 BBox::GetSupportedSuites(BMessage *message)
412 {
413 	return BView::GetSupportedSuites(message);
414 }
415 
416 
417 status_t
418 BBox::Perform(perform_code d, void *arg)
419 {
420 	return BView::Perform(d, arg);
421 }
422 
423 
424 void BBox::_ReservedBox1() {}
425 void BBox::_ReservedBox2() {}
426 
427 
428 BBox &
429 BBox::operator=(const BBox &)
430 {
431 	return *this;
432 }
433 
434 
435 void
436 BBox::_InitObject(BMessage *data)
437 {
438 	fLabel = NULL;
439 	fBounds = Bounds();
440 	fLabelView = NULL;
441 
442 	int32 flags = 0;
443 
444 	BFont font(be_bold_font);
445 
446 	if (!data || !data->HasString("_fname"))
447 		flags = B_FONT_FAMILY_AND_STYLE;
448 
449 	if (!data || !data->HasFloat("_fflt"))
450 		flags |= B_FONT_SIZE;
451 
452 	if (flags != 0)
453 		SetFont(&font, flags);
454 }
455 
456 
457 void
458 BBox::_DrawPlain(BRect labelBox)
459 {
460 	BRect r = Bounds();
461 	r.top += labelBox.Height() / 2.0f;
462 
463 	rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
464 	rgb_color shadow = tint_color(ViewColor(), B_DARKEN_3_TINT);
465 
466 	BeginLineArray(4);
467 		AddLine(BPoint(r.left, r.bottom),
468 				BPoint(r.left, r.top), light);
469 		AddLine(BPoint(r.left + 1.0f, r.top),
470 				BPoint(r.right, r.top), light);
471 		AddLine(BPoint(r.left + 1.0f, r.bottom),
472 				BPoint(r.right, r.bottom), shadow);
473 		AddLine(BPoint(r.right, r.bottom - 1.0f),
474 				BPoint(r.right, r.top + 1.0f), shadow);
475 	EndLineArray();
476 }
477 
478 
479 void
480 BBox::_DrawFancy(BRect labelBox)
481 {
482 	BRect r = Bounds();
483 	r.top += labelBox.Height() / 2.0f;
484 
485 	rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
486 	rgb_color shadow = tint_color(ViewColor(), B_DARKEN_3_TINT);
487 
488 	BeginLineArray(8);
489 		AddLine(BPoint(r.left, r.bottom),
490 				BPoint(r.left, r.top), shadow);
491 		AddLine(BPoint(r.left + 1.0f, r.top),
492 				BPoint(r.right, r.top), shadow);
493 		AddLine(BPoint(r.left + 1.0f, r.bottom),
494 				BPoint(r.right, r.bottom), light);
495 		AddLine(BPoint(r.right, r.bottom - 1.0f),
496 				BPoint(r.right, r.top + 1.0f), light);
497 
498 		r.InsetBy(1.0, 1.0);
499 
500 		AddLine(BPoint(r.left, r.bottom),
501 				BPoint(r.left, r.top), light);
502 		AddLine(BPoint(r.left + 1.0f, r.top),
503 				BPoint(r.right, r.top), light);
504 		AddLine(BPoint(r.left + 1.0f, r.bottom),
505 				BPoint(r.right, r.bottom), shadow);
506 		AddLine(BPoint(r.right, r.bottom - 1.0f),
507 				BPoint(r.right, r.top + 1.0f), shadow);
508 	EndLineArray();
509 }
510 
511 
512 void
513 BBox::_ClearLabel()
514 {
515 	fBounds.top = 0;
516 
517 	if (fLabel) {
518 		delete fLabelBox;
519 		free(fLabel);
520 		fLabel = NULL;
521 		fLabelBox = NULL;
522 	} else if (fLabelView) {
523 		fLabelView->RemoveSelf();
524 		delete fLabelView;
525 		fLabelView = NULL;
526 	}
527 }
528