1/*
2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Mike Berg <mike@berg-net.us>
7 *		Julun <host.haiku@gmx.de>
8 *		Hamish Morrison <hamish@lavabit.com>
9 */
10
11
12#include "SectionEdit.h"
13
14#include <Bitmap.h>
15#include <ControlLook.h>
16#include <LayoutUtils.h>
17#include <List.h>
18#include <Window.h>
19
20#include "TimeMessages.h"
21
22
23const uint32 kArrowAreaWidth = 16;
24
25
26TSectionEdit::TSectionEdit(const char* name, uint32 sections)
27	:
28	BControl(name, NULL, NULL, B_WILL_DRAW | B_NAVIGABLE),
29	fFocus(-1),
30	fSectionCount(sections),
31	fHoldValue(0)
32{
33}
34
35
36TSectionEdit::~TSectionEdit()
37{
38}
39
40
41void
42TSectionEdit::AttachedToWindow()
43{
44	AdoptParentColors();
45}
46
47
48void
49TSectionEdit::Draw(BRect updateRect)
50{
51	DrawBorder(updateRect);
52
53	for (uint32 idx = 0; idx < fSectionCount; idx++) {
54		DrawSection(idx, FrameForSection(idx),
55			((uint32)fFocus == idx) && IsFocus());
56		if (idx < fSectionCount - 1)
57			DrawSeparator(idx, FrameForSeparator(idx));
58	}
59}
60
61
62void
63TSectionEdit::MouseDown(BPoint where)
64{
65	MakeFocus(true);
66
67	if (fUpRect.Contains(where))
68		DoUpPress();
69	else if (fDownRect.Contains(where))
70		DoDownPress();
71	else if (fSectionCount > 0) {
72		for (uint32 idx = 0; idx < fSectionCount; idx++) {
73			if (FrameForSection(idx).Contains(where)) {
74				SectionFocus(idx);
75				return;
76			}
77		}
78	}
79}
80
81
82BSize
83TSectionEdit::MaxSize()
84{
85	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
86		BSize(B_SIZE_UNLIMITED, PreferredHeight()));
87}
88
89
90BSize
91TSectionEdit::MinSize()
92{
93	BSize minSize;
94	minSize.height = PreferredHeight();
95	minSize.width = (SeparatorWidth() + MinSectionWidth())
96		* fSectionCount;
97	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
98		minSize);
99}
100
101
102BSize
103TSectionEdit::PreferredSize()
104{
105	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
106		MinSize());
107}
108
109
110BRect
111TSectionEdit::FrameForSection(uint32 index)
112{
113	BRect area = SectionArea();
114	float sepWidth = SeparatorWidth();
115
116	float width = (area.Width() -
117		sepWidth * (fSectionCount - 1))
118		/ fSectionCount;
119	area.left += index * (width + sepWidth);
120	area.right = area.left + width;
121
122	return area;
123}
124
125
126BRect
127TSectionEdit::FrameForSeparator(uint32 index)
128{
129	BRect area = SectionArea();
130	float sepWidth = SeparatorWidth();
131
132	float width = (area.Width() -
133		sepWidth * (fSectionCount - 1))
134		/ fSectionCount;
135	area.left += (index + 1) * width + index * sepWidth;
136	area.right = area.left + sepWidth;
137
138	return area;
139}
140
141
142void
143TSectionEdit::MakeFocus(bool focused)
144{
145	if (focused == IsFocus())
146		return;
147
148	BControl::MakeFocus(focused);
149
150	if (fFocus == -1)
151		SectionFocus(0);
152	else
153		SectionFocus(fFocus);
154}
155
156
157void
158TSectionEdit::KeyDown(const char* bytes, int32 numbytes)
159{
160	if (fFocus == -1)
161		SectionFocus(0);
162
163	switch (bytes[0]) {
164		case B_LEFT_ARROW:
165			fFocus -= 1;
166			if (fFocus < 0)
167				fFocus = fSectionCount - 1;
168			SectionFocus(fFocus);
169			break;
170
171		case B_RIGHT_ARROW:
172			fFocus += 1;
173			if ((uint32)fFocus >= fSectionCount)
174				fFocus = 0;
175			SectionFocus(fFocus);
176			break;
177
178		case B_UP_ARROW:
179			DoUpPress();
180			break;
181
182		case B_DOWN_ARROW:
183			DoDownPress();
184			break;
185
186		default:
187			BControl::KeyDown(bytes, numbytes);
188			break;
189	}
190	Draw(Bounds());
191}
192
193
194void
195TSectionEdit::DispatchMessage()
196{
197	BMessage message(H_USER_CHANGE);
198	BuildDispatch(&message);
199	Window()->PostMessage(&message);
200}
201
202
203uint32
204TSectionEdit::CountSections() const
205{
206	return fSectionCount;
207}
208
209
210int32
211TSectionEdit::FocusIndex() const
212{
213	return fFocus;
214}
215
216
217BRect
218TSectionEdit::SectionArea() const
219{
220	BRect sectionArea = Bounds().InsetByCopy(2, 2);
221	sectionArea.right -= kArrowAreaWidth;
222	return sectionArea;
223}
224
225
226void
227TSectionEdit::DrawBorder(const BRect& updateRect)
228{
229	BRect bounds(Bounds());
230	bool showFocus = (IsFocus() && Window() && Window()->IsActive());
231
232	be_control_look->DrawBorder(this, bounds, updateRect, ViewColor(),
233		B_FANCY_BORDER, showFocus ? BControlLook::B_FOCUSED : 0);
234
235	// draw up/down control
236
237	bounds.left = bounds.right - kArrowAreaWidth;
238	bounds.right = Bounds().right - 2;
239	fUpRect.Set(bounds.left + 3, bounds.top + 2, bounds.right,
240		bounds.bottom / 2.0);
241	fDownRect = fUpRect.OffsetByCopy(0, fUpRect.Height() + 2);
242
243	BPoint middle(floorf(fUpRect.left + fUpRect.Width() / 2),
244		fUpRect.top + 1);
245	BPoint left(fUpRect.left + 3, fUpRect.bottom - 1);
246	BPoint right(left.x + 2 * (middle.x - left.x), fUpRect.bottom - 1);
247
248	SetPenSize(2);
249	SetLowColor(ViewColor());
250
251	if (updateRect.Intersects(fUpRect)) {
252		FillRect(fUpRect, B_SOLID_LOW);
253		BeginLineArray(2);
254			AddLine(left, middle, HighColor());
255			AddLine(middle, right, HighColor());
256		EndLineArray();
257	}
258	if (updateRect.Intersects(fDownRect)) {
259		middle.y = fDownRect.bottom - 1;
260		left.y = right.y = fDownRect.top + 1;
261
262		FillRect(fDownRect, B_SOLID_LOW);
263		BeginLineArray(2);
264			AddLine(left, middle, HighColor());
265			AddLine(middle, right, HighColor());
266		EndLineArray();
267	}
268
269	SetPenSize(1);
270}
271