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