1/*
2 * Copyright 2006, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan Aßmus <superstippi@gmx.de>
7 *		Ingo Weinhold <bonefish@cs.tu-berlin.de>
8 */
9
10#include "SliderView.h"
11
12#include <math.h>
13#include <stdio.h>
14
15#include <Message.h>
16#include <Window.h>
17
18#include "PopupSlider.h"
19
20// constructor
21SliderView::SliderView(PopupSlider* target,
22					   int32 min, int32 max, int32 value,
23					   const char* formatString)
24	: PopupView("slider"),
25	  fTarget(target),
26	  fFormatString(formatString),
27	  fMin(min),
28	  fMax(max),
29	  fValue(value),
30	  fButtonRect(0.0, 0.0, -1.0, -1.0),
31	  fDragOffset(0.0)
32{
33	SetViewColor(B_TRANSPARENT_32_BIT);
34	if (Max() < Min())
35		SetMax(Min());
36	BFont font;
37	GetFont(&font);
38	float buttonWidth, buttonHeight;
39	GetSliderButtonDimensions(Max(), FormatString(), &font,
40							  buttonWidth, buttonHeight);
41
42	fButtonRect.right = fButtonRect.left + buttonWidth;
43	fButtonRect.bottom = fButtonRect.top + buttonHeight;
44	float size = Max() - Min();
45	if (size > 200)
46		size = 200;
47	ResizeTo(6.0 + fButtonRect.Width() + size + 6.0,
48			 6.0 + fButtonRect.Height() + 6.0);
49}
50
51// destructor
52SliderView::~SliderView()
53{
54}
55
56// minimax
57minimax
58SliderView::layoutprefs()
59{
60	mpm.mini.x = mpm.maxi.x = Bounds().Width() + 1.0;
61	mpm.mini.y = mpm.maxi.y = Bounds().Height() + 1.0;
62
63	mpm.weight = 1.0;
64
65	return mpm;
66}
67
68// layout
69BRect
70SliderView::layout(BRect frame)
71{
72	MoveTo(frame.LeftTop());
73	ResizeTo(frame.Width(), frame.Height());
74	return Frame();
75}
76
77// Draw
78void
79SliderView::Draw(BRect updateRect)
80{
81	fButtonRect.OffsetTo(ButtonOffset(), 6.0);
82
83	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
84	rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
85	rgb_color lightShadow = tint_color(background, B_DARKEN_1_TINT);
86	rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
87	rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
88
89	BRect r(Bounds());
90	BeginLineArray(24);
91		// outer dark line
92		AddLine(BPoint(r.left, r.bottom),
93				BPoint(r.left, r.top), lightShadow);
94		AddLine(BPoint(r.left + 1.0, r.top),
95				BPoint(r.right, r.top), lightShadow);
96		AddLine(BPoint(r.right, r.top + 1.0),
97				BPoint(r.right, r.bottom), darkShadow);
98		AddLine(BPoint(r.right - 1.0, r.bottom),
99				BPoint(r.left + 1.0, r.bottom), darkShadow);
100		// second line (raised)
101		r.InsetBy(1.0, 1.0);
102		AddLine(BPoint(r.left, r.bottom),
103				BPoint(r.left, r.top), light);
104		AddLine(BPoint(r.left + 1.0, r.top),
105				BPoint(r.right, r.top), light);
106		AddLine(BPoint(r.right, r.top + 1.0),
107				BPoint(r.right, r.bottom), shadow);
108		AddLine(BPoint(r.right - 1.0, r.bottom),
109				BPoint(r.left + 1.0, r.bottom), shadow);
110		// third line (normal)
111		r.InsetBy(1.0, 1.0);
112		AddLine(BPoint(r.left, r.bottom),
113				BPoint(r.left, r.top), background);
114		AddLine(BPoint(r.left + 1.0, r.top),
115				BPoint(r.right, r.top), background);
116		AddLine(BPoint(r.right, r.top + 1.0),
117				BPoint(r.right, r.bottom), background);
118		AddLine(BPoint(r.right - 1.0, r.bottom),
119				BPoint(r.left + 1.0, r.bottom), background);
120		// fourth line (normal)
121		r.InsetBy(1.0, 1.0);
122		AddLine(BPoint(r.left, r.bottom),
123				BPoint(r.left, r.top), background);
124		AddLine(BPoint(r.left + 1.0, r.top),
125				BPoint(r.right, r.top), background);
126		AddLine(BPoint(r.right, r.top + 1.0),
127				BPoint(r.right, r.bottom), background);
128		AddLine(BPoint(r.right - 1.0, r.bottom),
129				BPoint(r.left + 1.0, r.bottom), background);
130		// fifth line (depressed)
131		r.InsetBy(1.0, 1.0);
132		AddLine(BPoint(r.left, r.bottom),
133				BPoint(r.left, r.top), lightShadow);
134		AddLine(BPoint(r.left + 1.0, r.top),
135				BPoint(r.right, r.top), lightShadow);
136		AddLine(BPoint(r.right, r.top + 1.0),
137				BPoint(r.right, r.bottom), light);
138		AddLine(BPoint(r.right - 1.0, r.bottom),
139				BPoint(r.left + 1.0, r.bottom), light);
140		// fifth line (strongly depressed)
141		r.InsetBy(1.0, 1.0);
142		AddLine(BPoint(r.left, r.bottom),
143				BPoint(r.left, r.top), darkShadow);
144		AddLine(BPoint(r.left + 1.0, r.top),
145				BPoint(r.right, r.top), darkShadow);
146		AddLine(BPoint(r.right, r.top + 1.0),
147				BPoint(r.right, r.bottom), shadow);
148		AddLine(BPoint(r.right - 1.0, r.bottom),
149				BPoint(r.left + 1.0, r.bottom), shadow);
150	EndLineArray();
151
152	r.InsetBy(1.0, 1.0);
153	SetLowColor(lightShadow);
154	BRect leftOfButton(r.left + 1.0, r.top + 1.0, fButtonRect.left - 2.0, r.bottom);
155	if (leftOfButton.IsValid())
156		FillRect(leftOfButton, B_SOLID_LOW);
157	BRect rightOfButton(fButtonRect.right + 2.0, r.top + 1.0,
158						r.right, r.bottom);
159	if (rightOfButton.IsValid())
160		FillRect(rightOfButton, B_SOLID_LOW);
161
162	// inner shadow and knob out lines
163	BeginLineArray(5);
164		// shadow
165		AddLine(BPoint(r.left, r.bottom),
166				BPoint(r.left, r.top), shadow);
167		AddLine(BPoint(r.left + 1.0, r.top),
168				BPoint(r.right, r.top), shadow);
169		// at knob
170		if (fButtonRect.left == 6.0)
171			AddLine(BPoint(fButtonRect.left - 1.0, r.top),
172					BPoint(fButtonRect.left - 1.0, r.bottom), darkShadow);
173		else
174			AddLine(BPoint(fButtonRect.left - 1.0, r.top),
175					BPoint(fButtonRect.left - 1.0, r.bottom), shadow);
176		AddLine(BPoint(fButtonRect.left, fButtonRect.bottom + 1.0),
177				BPoint(fButtonRect.right + 1.0, fButtonRect.bottom + 1.0), darkShadow);
178		AddLine(BPoint(fButtonRect.right + 1.0, fButtonRect.bottom),
179				BPoint(fButtonRect.right + 1.0, fButtonRect.top), darkShadow);
180	EndLineArray();
181
182
183	DrawSliderButton(this, fButtonRect, fValue, fFormatString.String(), fTarget->IsEnabled());
184}
185
186// MouseUp
187void
188SliderView::MouseUp(BPoint where)
189{
190	PopupDone(false);
191	fTarget->TriggerValueChanged(fTarget->Message());
192}
193
194// MouseMoved
195void
196SliderView::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
197{
198	uint32 buttons = 0;
199	if (BMessage* message = Window()->CurrentMessage()) {
200		if (message->FindInt32("buttons", (int32*)&buttons) < B_OK)
201			buttons = 0;
202	}
203
204	if (buttons == 0) {
205		MouseUp(where);
206		return;
207	}
208
209	SetValue(_ValueAt(where.x - fDragOffset - 6.0));
210}
211
212// MessageReceived
213void
214SliderView::MessageReceived(BMessage* message)
215{
216	switch (message->what) {
217		default:
218			PopupView::MessageReceived(message);
219			break;
220	}
221}
222
223// SetValue
224void
225SliderView::SetValue(int32 value)
226{
227	if (value < fMin)
228		value = fMin;
229	if (value > fMax)
230		value = fMax;
231	if (value != fValue) {
232		fValue = value;
233		fTarget->SetValue(value);
234		Invalidate();
235	}
236}
237
238// Value
239int32
240SliderView::Value() const
241{
242	return fValue;
243}
244
245// SetMin
246void
247SliderView::SetMin(int32 min)
248{
249	if (min != fMax) {
250		fMin = min;
251		if (fValue < fMin)
252			SetValue(fMin);
253		Invalidate();
254	}
255}
256
257// Min
258int32
259SliderView::Min() const
260{
261	return fMin;
262}
263
264// SetMax
265void
266SliderView::SetMax(int32 max)
267{
268	if (max != fMax) {
269		fMax = max;
270		if (fValue > fMax)
271			SetValue(fMax);
272		Invalidate();
273	}
274}
275
276// Max
277int32
278SliderView::Max() const
279{
280	return fMax;
281}
282
283// SetFormatString
284void
285SliderView::SetFormatString(const char* formatString)
286{
287	// TODO: check if formatString contains "%ld"
288	fFormatString.SetTo(formatString);
289}
290
291// FormatString
292const char*
293SliderView::FormatString() const
294{
295	return fFormatString.String();
296}
297
298// SetDragOffset
299void
300SliderView::SetDragOffset(float offset)
301{
302	fDragOffset = offset;
303}
304
305// ButtonOffset
306float
307SliderView::ButtonOffset()
308{
309//	float range = Bounds().Width() - 12.0 - fButtonRect.Width() + 1.0;
310//	return 6.0 + range / (float)(fMax - fMin + 1) * (float)(fValue - fMin);
311	float ratio = fTarget->DeScale((float)(fValue - fMin) / (float)(fMax - fMin));
312	return 6.0 + ratio * (Bounds().Width() - 12.0 - fButtonRect.Width());
313}
314
315// GetSliderButtonDimensions
316void
317SliderView::GetSliderButtonDimensions(int32 max, const char* formatString,
318									  BFont* font,
319									  float& width, float& height)
320{
321	if (font) {
322		char label[256];
323		sprintf(label, formatString, max);
324		font_height fh;
325		font->GetHeight(&fh);
326		// 4 pixels room on each side,
327		// 1 pixel room at top and bottom
328		width = 5.0 + ceilf(font->StringWidth(label)) + 5.0;
329		height = 2.0 + ceilf(fh.ascent + fh.descent) + 2.0;
330	}
331}
332
333// DrawSliderButton
334void
335SliderView::DrawSliderButton(BView* v, BRect r, int32 value,
336							 const char* formatString, bool enabled)
337{
338	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
339	rgb_color light;
340	rgb_color shadow;
341	rgb_color button;
342	rgb_color black;
343	if (enabled) {
344		light = tint_color(background, B_LIGHTEN_MAX_TINT);
345		shadow = tint_color(background, B_DARKEN_1_TINT);
346		button = tint_color(background, B_LIGHTEN_1_TINT);
347		black = tint_color(background, B_DARKEN_MAX_TINT);
348	} else {
349		light = tint_color(background, B_LIGHTEN_1_TINT);
350		shadow = tint_color(background, 1.1);
351		button = tint_color(background, 0.8);
352		black = tint_color(background, B_DISABLED_LABEL_TINT);
353	}
354	// border
355	v->BeginLineArray(4);
356		v->AddLine(BPoint(r.left, r.bottom),
357				   BPoint(r.left, r.top), light);
358		v->AddLine(BPoint(r.left + 1.0, r.top),
359				   BPoint(r.right, r.top), light);
360		v->AddLine(BPoint(r.right, r.top + 1.0),
361				   BPoint(r.right, r.bottom), shadow);
362		v->AddLine(BPoint(r.right - 1.0, r.bottom),
363				   BPoint(r.left + 1.0, r.bottom), shadow);
364	v->EndLineArray();
365	// background & label
366	r.InsetBy(1.0, 1.0);
367	char label[256];
368	sprintf(label, formatString, value);
369	float width = v->StringWidth(label);
370	font_height fh;
371	v->GetFontHeight(&fh);
372	BPoint textPoint((r.left + r.right) / 2.0 - width / 2.0,
373					 (r.top + r.bottom) / 2.0 + fh.ascent / 2.0);
374	v->SetHighColor(black);
375	v->SetLowColor(button);
376	v->FillRect(r, B_SOLID_LOW);
377	v->DrawString(label, textPoint);
378}
379
380// _ValueAt
381int32
382SliderView::_ValueAt(float h)
383{
384/*	return fMin + (int32)(((float)(fMax - fMin + 1) * h) /
385		   (Bounds().Width() - fButtonRect.Width() - 12.0));*/
386	float ratio = h / (Bounds().Width() - fButtonRect.Width() - 12.0);
387	if (ratio < 0.0)
388		ratio = 0.0;
389	if (ratio > 1.0)
390		ratio = 1.0;
391	ratio = fTarget->Scale(ratio);
392	return fMin + (int32)((fMax - fMin + 1) * ratio);
393}
394
395
396
397
398