1/*
2 * Copyright 2001-2009, Stephan A��mus <superstippi@gmx.de>
3 * Copyright 2001-2009, Ingo Weinhold <ingo_weinhold@gmx.de>
4 * All rights reserved. Distributed under the terms of the MIT license.
5 */
6
7
8#include "SeparatorView.h"
9
10#include <new>
11
12#include <math.h>
13#include <stdio.h>
14
15#include <ControlLook.h>
16#include <LayoutUtils.h>
17#include <Region.h>
18
19
20static const float kMinBorderLength = 5.0f;
21
22
23// TODO: Actually implement alignment support!
24// TODO: More testing, especially archiving.
25
26
27BSeparatorView::BSeparatorView(orientation orientation, border_style border)
28	:
29	BView(NULL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
30{
31	_Init(NULL, NULL, orientation, BAlignment(B_ALIGN_HORIZONTAL_CENTER,
32		B_ALIGN_VERTICAL_CENTER), border);
33}
34
35
36BSeparatorView::BSeparatorView(const char* name, const char* label,
37	orientation orientation, border_style border, const BAlignment& alignment)
38	:
39	BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
40{
41	_Init(label, NULL, orientation, alignment, border);
42}
43
44
45BSeparatorView::BSeparatorView(const char* name, BView* labelView,
46	orientation orientation, border_style border, const BAlignment& alignment)
47	:
48	BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
49{
50	_Init(NULL, labelView, orientation, alignment, border);
51}
52
53
54BSeparatorView::BSeparatorView(const char* label,
55	orientation orientation, border_style border, const BAlignment& alignment)
56	:
57	BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
58{
59	_Init(label, NULL, orientation, alignment, border);
60}
61
62
63BSeparatorView::BSeparatorView(BView* labelView,
64	orientation orientation, border_style border, const BAlignment& alignment)
65	:
66	BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
67{
68	_Init(NULL, labelView, orientation, alignment, border);
69}
70
71
72BSeparatorView::BSeparatorView(BMessage* archive)
73	:
74	BView(archive),
75	fLabel(),
76	fLabelView(NULL),
77	fOrientation(B_HORIZONTAL),
78	fAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
79		B_ALIGN_VERTICAL_CENTER)),
80	fBorder(B_FANCY_BORDER)
81{
82	// NULL archives will be caught by BView.
83
84	const char* label;
85	if (archive->FindString("_labelview", &label) == B_OK) {
86		fLabelView = FindView(label);
87	} else if (archive->FindString("_label", &label) == B_OK) {
88		fLabel.SetTo(label);
89	}
90
91	int32 orientation;
92	if (archive->FindInt32("_orientation", &orientation) == B_OK)
93		fOrientation = (enum orientation)orientation;
94
95	int32 hAlignment;
96	int32 vAlignment;
97	if (archive->FindInt32("_halignment", &hAlignment) == B_OK
98		&& archive->FindInt32("_valignment", &vAlignment) == B_OK) {
99		fAlignment.horizontal = (alignment)hAlignment;
100		fAlignment.vertical = (vertical_alignment)vAlignment;
101	}
102
103	int32 borderStyle;
104	if (archive->FindInt32("_border", &borderStyle) != B_OK)
105		fBorder = (border_style)borderStyle;
106}
107
108
109BSeparatorView::~BSeparatorView()
110{
111}
112
113
114// #pragma mark - Archiving
115
116
117BArchivable*
118BSeparatorView::Instantiate(BMessage* archive)
119{
120	if (validate_instantiation(archive, "BSeparatorView"))
121		return new (std::nothrow) BSeparatorView(archive);
122
123	return NULL;
124}
125
126
127status_t
128BSeparatorView::Archive(BMessage* into, bool deep) const
129{
130	// TODO: Test this.
131	status_t result = BView::Archive(into, deep);
132	if (result != B_OK)
133		return result;
134
135	if (fLabelView != NULL)
136		result = into->AddString("_labelview", fLabelView->Name());
137	else
138		result = into->AddString("_label", fLabel.String());
139
140	if (result == B_OK)
141		result = into->AddInt32("_orientation", fOrientation);
142
143	if (result == B_OK)
144		result = into->AddInt32("_halignment", fAlignment.horizontal);
145
146	if (result == B_OK)
147		result = into->AddInt32("_valignment", fAlignment.vertical);
148
149	if (result == B_OK)
150		result = into->AddInt32("_border", fBorder);
151
152	return result;
153}
154
155
156// #pragma mark -
157
158
159void
160BSeparatorView::Draw(BRect updateRect)
161{
162	rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
163	rgb_color highColor = ui_color(B_PANEL_TEXT_COLOR);
164
165	if (BView* parent = Parent()) {
166		if (parent->ViewColor() != B_TRANSPARENT_COLOR) {
167			bgColor = parent->ViewColor();
168			highColor = parent->HighColor();
169		}
170	}
171
172	BRect labelBounds;
173	if (fLabelView != NULL) {
174		labelBounds = fLabelView->Frame();
175	} else if (fLabel.CountChars() > 0) {
176		labelBounds = _MaxLabelBounds();
177		float labelWidth = StringWidth(fLabel.String());
178		if (fOrientation == B_HORIZONTAL) {
179			switch (fAlignment.horizontal) {
180				case B_ALIGN_LEFT:
181				default:
182					labelBounds.right = labelBounds.left + labelWidth;
183					break;
184
185				case B_ALIGN_RIGHT:
186					labelBounds.left = labelBounds.right - labelWidth;
187					break;
188
189				case B_ALIGN_CENTER:
190					labelBounds.left = (labelBounds.left + labelBounds.right
191						- labelWidth) / 2;
192					labelBounds.right = labelBounds.left + labelWidth;
193					break;
194			}
195		} else {
196			switch (fAlignment.vertical) {
197				case B_ALIGN_TOP:
198				default:
199					labelBounds.bottom = labelBounds.top + labelWidth;
200					break;
201
202				case B_ALIGN_BOTTOM:
203					labelBounds.top = labelBounds.bottom - labelWidth;
204					break;
205
206				case B_ALIGN_MIDDLE:
207					labelBounds.top = (labelBounds.top + labelBounds.bottom
208						- labelWidth) / 2;
209					labelBounds.bottom = labelBounds.top + labelWidth;
210					break;
211			}
212		}
213	}
214
215	BRect bounds = Bounds();
216	BRegion region(bounds);
217	if (labelBounds.IsValid()) {
218		region.Exclude(labelBounds);
219		ConstrainClippingRegion(&region);
220	}
221
222	float borderSize = _BorderSize();
223	if (borderSize > 0.0f) {
224		if (fOrientation == B_HORIZONTAL) {
225			bounds.top = floorf((bounds.top + bounds.bottom + 1 - borderSize)
226				/ 2);
227			bounds.bottom = bounds.top + borderSize - 1;
228			region.Exclude(bounds);
229			be_control_look->DrawBorder(this, bounds, updateRect, bgColor,
230				fBorder, 0, BControlLook::B_TOP_BORDER);
231		} else {
232			bounds.left = floorf((bounds.left + bounds.right + 1 - borderSize)
233				/ 2);
234			bounds.right = bounds.left + borderSize - 1;
235			region.Exclude(bounds);
236			be_control_look->DrawBorder(this, bounds, updateRect, bgColor,
237				fBorder, 0, BControlLook::B_LEFT_BORDER);
238		}
239		if (labelBounds.IsValid())
240			region.Include(labelBounds);
241
242		ConstrainClippingRegion(&region);
243	}
244
245	SetLowColor(bgColor);
246	FillRect(updateRect, B_SOLID_LOW);
247
248	if (fLabel.CountChars() > 0) {
249		font_height fontHeight;
250		GetFontHeight(&fontHeight);
251
252		SetHighColor(highColor);
253
254		if (fOrientation == B_HORIZONTAL) {
255			DrawString(fLabel.String(), BPoint(labelBounds.left,
256				labelBounds.top + ceilf(fontHeight.ascent)));
257		} else {
258			DrawString(fLabel.String(), BPoint(labelBounds.left
259				+ ceilf(fontHeight.ascent), labelBounds.bottom));
260		}
261	}
262}
263
264
265void
266BSeparatorView::GetPreferredSize(float* _width, float* _height)
267{
268	float width = 0.0f;
269	float height = 0.0f;
270
271	if (fLabelView != NULL) {
272		fLabelView->GetPreferredSize(&width, &height);
273	} else if (fLabel.CountChars() > 0) {
274		width = StringWidth(fLabel.String());
275		font_height fontHeight;
276		GetFontHeight(&fontHeight);
277		height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
278		if (fOrientation == B_VERTICAL) {
279			// swap width and height
280			float temp = height;
281			height = width;
282			width = temp;
283		}
284	}
285
286	float borderSize = _BorderSize();
287
288	// Add some room for the border
289	if (fOrientation == B_HORIZONTAL) {
290		width += kMinBorderLength * 2.0f;
291		height = max_c(height, borderSize - 1.0f);
292	} else {
293		height += kMinBorderLength * 2.0f;
294		width = max_c(width, borderSize - 1.0f);
295	}
296
297	if (_width != NULL)
298		*_width = width;
299
300	if (_height != NULL)
301		*_height = height;
302}
303
304
305BSize
306BSeparatorView::MinSize()
307{
308	BSize size;
309	GetPreferredSize(&size.width, &size.height);
310	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
311}
312
313
314BSize
315BSeparatorView::MaxSize()
316{
317	BSize size(MinSize());
318	if (fOrientation == B_HORIZONTAL)
319		size.width = B_SIZE_UNLIMITED;
320	else
321		size.height = B_SIZE_UNLIMITED;
322
323	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
324}
325
326
327BSize
328BSeparatorView::PreferredSize()
329{
330	BSize size;
331	GetPreferredSize(&size.width, &size.height);
332
333	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
334}
335
336
337// #pragma mark -
338
339
340void
341BSeparatorView::SetOrientation(orientation orientation)
342{
343	if (orientation == fOrientation)
344		return;
345
346	fOrientation = orientation;
347
348	BFont font;
349	GetFont(&font);
350	if (fOrientation == B_VERTICAL)
351		font.SetRotation(90.0f);
352
353	SetFont(&font);
354
355	Invalidate();
356	InvalidateLayout();
357}
358
359
360void
361BSeparatorView::SetAlignment(const BAlignment& aligment)
362{
363	if (aligment == fAlignment)
364		return;
365
366	fAlignment = aligment;
367
368	Invalidate();
369	InvalidateLayout();
370}
371
372
373void
374BSeparatorView::SetBorderStyle(border_style border)
375{
376	if (border == fBorder)
377		return;
378
379	fBorder = border;
380
381	Invalidate();
382}
383
384
385void
386BSeparatorView::SetLabel(const char* label)
387{
388	if (label == NULL)
389		label = "";
390
391	if (fLabel == label)
392		return;
393
394	fLabel.SetTo(label);
395
396	Invalidate();
397	InvalidateLayout();
398}
399
400
401void
402BSeparatorView::SetLabel(BView* view, bool deletePrevious)
403{
404	if (fLabelView == view)
405		return;
406
407	if (fLabelView != NULL) {
408		fLabelView->RemoveSelf();
409		if (deletePrevious)
410			delete fLabelView;
411	}
412
413	fLabelView = view;
414
415	if (fLabelView != NULL)
416		AddChild(view);
417}
418
419
420status_t
421BSeparatorView::Perform(perform_code code, void* data)
422{
423	return BView::Perform(code, data);
424}
425
426
427// #pragma mark - protected/private
428
429
430void
431BSeparatorView::DoLayout()
432{
433	BView::DoLayout();
434
435	if (fLabelView == NULL)
436		return;
437
438	BRect bounds = _MaxLabelBounds();
439	BRect frame = BLayoutUtils::AlignInFrame(bounds, fLabelView->MaxSize(),
440		fAlignment);
441
442	fLabelView->MoveTo(frame.LeftTop());
443	fLabelView->ResizeTo(frame.Width(), frame.Height());
444}
445
446
447void
448BSeparatorView::_Init(const char* label, BView* labelView,
449	orientation orientation, BAlignment alignment, border_style border)
450{
451	fLabel = "";
452	fLabelView = NULL;
453
454	fOrientation = B_HORIZONTAL;
455	fAlignment = alignment;
456	fBorder = border;
457
458	SetFont(be_bold_font);
459	SetViewColor(B_TRANSPARENT_32_BIT);
460
461	SetLabel(label);
462	SetLabel(labelView, true);
463	SetOrientation(orientation);
464}
465
466
467float
468BSeparatorView::_BorderSize() const
469{
470	// TODO: Get these values from the BControlLook class.
471	switch (fBorder) {
472		case B_PLAIN_BORDER:
473			return 1.0f;
474
475		case B_FANCY_BORDER:
476			return 2.0f;
477
478		case B_NO_BORDER:
479		default:
480			return 0.0f;
481	}
482}
483
484
485BRect
486BSeparatorView::_MaxLabelBounds() const
487{
488	BRect bounds = Bounds();
489	if (fOrientation == B_HORIZONTAL)
490		bounds.InsetBy(kMinBorderLength, 0.0f);
491	else
492		bounds.InsetBy(0.0f, kMinBorderLength);
493
494	return bounds;
495}
496
497
498// #pragma mark - FBC padding
499
500
501void BSeparatorView::_ReservedSeparatorView1() {}
502void BSeparatorView::_ReservedSeparatorView2() {}
503void BSeparatorView::_ReservedSeparatorView3() {}
504void BSeparatorView::_ReservedSeparatorView4() {}
505void BSeparatorView::_ReservedSeparatorView5() {}
506void BSeparatorView::_ReservedSeparatorView6() {}
507void BSeparatorView::_ReservedSeparatorView7() {}
508void BSeparatorView::_ReservedSeparatorView8() {}
509void BSeparatorView::_ReservedSeparatorView9() {}
510void BSeparatorView::_ReservedSeparatorView10() {}
511