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