1/*
2 *  Copyright 2010-2012 Haiku, Inc. All rights reserved.
3 *  Distributed under the terms of the MIT license.
4 *
5 *	Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		John Scipione <jscipione@gmail.com>
8 */
9
10
11#include "FakeScrollBar.h"
12
13#include <Box.h>
14#include <ControlLook.h>
15#include <Message.h>
16#include <ScrollBar.h>
17#include <Shape.h>
18#include <Size.h>
19#include <Window.h>
20
21
22typedef enum {
23	ARROW_LEFT = 0,
24	ARROW_RIGHT,
25	ARROW_UP,
26	ARROW_DOWN,
27	ARROW_NONE
28} arrow_direction;
29
30
31FakeScrollBar::FakeScrollBar(bool drawArrows, bool doubleArrows,
32	BMessage* message)
33	:
34	BControl("FakeScrollBar", NULL, message, B_WILL_DRAW | B_NAVIGABLE),
35	fDrawArrows(drawArrows),
36	fDoubleArrows(doubleArrows)
37{
38	SetExplicitMinSize(BSize(160, 20));
39	SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 20));
40}
41
42
43FakeScrollBar::~FakeScrollBar(void)
44{
45}
46
47
48void
49FakeScrollBar::Draw(BRect updateRect)
50{
51	BRect bounds = Bounds();
52
53	rgb_color normal = ui_color(B_PANEL_BACKGROUND_COLOR);
54
55	if (IsFocus()) {
56		// draw the focus indicator
57		SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
58		StrokeRect(bounds);
59		bounds.InsetBy(1.0, 1.0);
60
61		// Draw the selected border (1px)
62		if (Value() == B_CONTROL_ON)
63			SetHighColor(ui_color(B_CONTROL_MARK_COLOR));
64		else
65			SetHighColor(normal);
66
67		StrokeRect(bounds);
68		bounds.InsetBy(1.0, 1.0);
69	} else {
70		// Draw the selected border (2px)
71		if (Value() == B_CONTROL_ON)
72			SetHighColor(ui_color(B_CONTROL_MARK_COLOR));
73		else
74			SetHighColor(normal);
75
76		StrokeRect(bounds);
77		bounds.InsetBy(1.0, 1.0);
78		StrokeRect(bounds);
79		bounds.InsetBy(1.0, 1.0);
80	}
81
82	// draw a gap (1px)
83	SetHighColor(normal);
84	StrokeRect(bounds);
85	bounds.InsetBy(1.0, 1.0);
86
87	// draw a border around control (1px)
88	SetHighColor(tint_color(normal, B_DARKEN_1_TINT));
89	StrokeRect(bounds);
90	bounds.InsetBy(1.0, 1.0);
91
92	BRect thumbBG = bounds;
93	BRect bgRect = bounds;
94
95	if (fDrawArrows) {
96		// draw arrows
97		SetDrawingMode(B_OP_OVER);
98
99		BRect buttonFrame(bounds.left, bounds.top,
100			bounds.left + bounds.Height(), bounds.bottom);
101
102		_DrawArrowButton(ARROW_LEFT, fDoubleArrows, buttonFrame, updateRect);
103
104		if (fDoubleArrows) {
105			buttonFrame.OffsetBy(bounds.Height() + 1, 0.0);
106			_DrawArrowButton(ARROW_RIGHT, fDoubleArrows, buttonFrame,
107				updateRect);
108
109			buttonFrame.OffsetTo(bounds.right - ((bounds.Height() * 2) + 1),
110				bounds.top);
111			_DrawArrowButton(ARROW_LEFT, fDoubleArrows, buttonFrame,
112				updateRect);
113
114			thumbBG.left += bounds.Height() * 2 + 2;
115			thumbBG.right -= bounds.Height() * 2 + 2;
116		} else {
117			thumbBG.left += bounds.Height() + 1;
118			thumbBG.right -= bounds.Height() + 1;
119		}
120
121		buttonFrame.OffsetTo(bounds.right - bounds.Height(), bounds.top);
122		_DrawArrowButton(ARROW_RIGHT, fDoubleArrows, buttonFrame, updateRect);
123
124		SetDrawingMode(B_OP_COPY);
125
126		bgRect = bounds.InsetByCopy(48, 0);
127	} else
128		bgRect = bounds.InsetByCopy(16, 0);
129
130	// fill background besides the thumb
131	BRect leftOfThumb(thumbBG.left, thumbBG.top, bgRect.left - 1,
132		thumbBG.bottom);
133	BRect rightOfThumb(bgRect.right + 1, thumbBG.top, thumbBG.right,
134		thumbBG.bottom);
135
136	be_control_look->DrawScrollBarBackground(this, leftOfThumb,
137		rightOfThumb, updateRect, normal, 0, B_HORIZONTAL);
138
139	// Draw scroll thumb
140
141	// fill the clickable surface of the thumb
142	be_control_look->DrawButtonBackground(this, bgRect, updateRect,
143		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
144}
145
146
147void
148FakeScrollBar::MouseDown(BPoint point)
149{
150	BControl::MouseDown(point);
151}
152
153
154void
155FakeScrollBar::MouseMoved(BPoint point, uint32 transit,
156	const BMessage* message)
157{
158	BControl::MouseMoved(point, transit, message);
159}
160
161
162void
163FakeScrollBar::MouseUp(BPoint point)
164{
165	SetValue(B_CONTROL_ON);
166	Invoke();
167
168	Invalidate();
169
170	BControl::MouseUp(point);
171}
172
173
174void
175FakeScrollBar::SetValue(int32 value)
176{
177	if (value != Value()) {
178		BControl::SetValueNoUpdate(value);
179		Invalidate();
180	}
181
182	if (!value)
183		return;
184
185	BView* parent = Parent();
186	BView* child = NULL;
187
188	if (parent != NULL) {
189		// If the parent is a BBox, the group parent is the parent of the BBox
190		BBox* box = dynamic_cast<BBox*>(parent);
191
192		if (box && box->LabelView() == this)
193			parent = box->Parent();
194
195		if (parent != NULL) {
196			BBox* box = dynamic_cast<BBox*>(parent);
197
198			// If the parent is a BBox, skip the label if there is one
199			if (box && box->LabelView())
200				child = parent->ChildAt(1);
201			else
202				child = parent->ChildAt(0);
203		} else
204			child = Window()->ChildAt(0);
205	} else if (Window())
206		child = Window()->ChildAt(0);
207
208	while (child) {
209		FakeScrollBar* scrollbar = dynamic_cast<FakeScrollBar*>(child);
210
211		if (scrollbar != NULL && (scrollbar != this))
212			scrollbar->SetValue(B_CONTROL_OFF);
213		else {
214			// If the child is a BBox, check if the label is a scrollbarbutton
215			BBox* box = dynamic_cast<BBox*>(child);
216
217			if (box && box->LabelView()) {
218				scrollbar = dynamic_cast<FakeScrollBar*>(box->LabelView());
219
220				if (scrollbar != NULL && (scrollbar != this))
221					scrollbar->SetValue(B_CONTROL_OFF);
222			}
223		}
224
225		child = child->NextSibling();
226	}
227
228	//ASSERT(Value() == B_CONTROL_ON);
229}
230
231
232//	#pragma mark -
233
234
235void
236FakeScrollBar::SetDoubleArrows(bool doubleArrows)
237{
238	fDoubleArrows = doubleArrows;
239	Invalidate();
240}
241
242
243void
244FakeScrollBar::SetKnobStyle(uint32 knobStyle)
245{
246	fKnobStyle = knobStyle;
247	Invalidate();
248}
249
250
251void
252FakeScrollBar::SetFromScrollBarInfo(const scroll_bar_info &info)
253{
254	fDoubleArrows = info.double_arrows;
255	fKnobStyle = info.knob;
256	Invalidate();
257}
258
259
260//	#pragma mark -
261
262
263void
264FakeScrollBar::_DrawArrowButton(int32 direction, bool doubleArrows, BRect r,
265	const BRect& updateRect)
266{
267	if (!updateRect.Intersects(r))
268		return;
269
270	rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
271	rgb_color light = tint_color(c, B_LIGHTEN_MAX_TINT);
272	rgb_color dark = tint_color(c, B_DARKEN_1_TINT);
273	rgb_color darker = tint_color(c, B_DARKEN_2_TINT);
274	rgb_color normal = c;
275	rgb_color arrow = tint_color(c,
276		(B_DARKEN_MAX_TINT + B_DARKEN_4_TINT) / 2.0);
277
278	BPoint tri1, tri2, tri3;
279	float hInset = r.Width() / 3;
280	float vInset = r.Height() / 3;
281	r.InsetBy(hInset, vInset);
282
283	switch (direction) {
284		case ARROW_LEFT:
285			tri1.Set(r.right, r.top);
286			tri2.Set(r.right - r.Width() / 1.33, (r.top + r.bottom + 1) / 2);
287			tri3.Set(r.right, r.bottom + 1);
288			break;
289
290		case ARROW_RIGHT:
291			tri1.Set(r.left, r.bottom + 1);
292			tri2.Set(r.left + r.Width() / 1.33, (r.top + r.bottom + 1) / 2);
293			tri3.Set(r.left, r.top);
294			break;
295
296		case ARROW_UP:
297			tri1.Set(r.left, r.bottom);
298			tri2.Set((r.left + r.right + 1) / 2, r.bottom - r.Height() / 1.33);
299			tri3.Set(r.right + 1, r.bottom);
300			break;
301
302		default:
303			tri1.Set(r.left, r.top);
304			tri2.Set((r.left + r.right + 1) / 2, r.top + r.Height() / 1.33);
305			tri3.Set(r.right + 1, r.top);
306			break;
307	}
308
309	r.InsetBy(-(hInset - 1), -(vInset - 1));
310	BRect temp(r.InsetByCopy(-1, -1));
311	be_control_look->DrawButtonBackground(this, temp, updateRect,
312		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
313
314	BShape arrowShape;
315	arrowShape.MoveTo(tri1);
316	arrowShape.LineTo(tri2);
317	arrowShape.LineTo(tri3);
318
319	SetHighColor(arrow);
320	SetPenSize(ceilf(hInset / 2.0));
321	StrokeShape(&arrowShape);
322	SetPenSize(1.0);
323}
324