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 "PopupSlider.h"
11
12#include <math.h>
13#include <stdio.h>
14
15#include <Message.h>
16
17#include <MDividable.h>
18#include <MWindow.h>
19
20#include "SliderView.h"
21
22// constructor
23PopupSlider::PopupSlider(const char* name, const char* label,
24						 BMessage* model, BHandler* target,
25						 int32 min, int32 max, int32 value,
26						 const char* formatString)
27	: PopupControl(name, fSlider = new SliderView(this, min, max, value,
28												  formatString)),
29	  MDividable(),
30	  fModel(model),
31	  fPressModel(NULL),
32	  fReleaseModel(NULL),
33	  fTarget(target),
34	  fLabel(label),
35	  fSliderButtonRect(0.0, 0.0, -1.0, -1.0),
36	  fEnabled(true),
37	  fTracking(false)
38{
39	SetViewColor(B_TRANSPARENT_32_BIT);
40}
41
42// destructor
43PopupSlider::~PopupSlider()
44{
45	delete fModel;
46	if (BWindow* window = fSlider->Window()) {
47		window->Lock();
48		window->RemoveChild(fSlider);
49		window->Unlock();
50	}
51	delete fSlider;
52}
53
54// layoutprefs
55minimax
56PopupSlider::layoutprefs()
57{
58	BFont font;
59	GetFont(&font);
60	font_height fh;
61	font.GetHeight(&fh);
62	float labelHeight = 2.0 + ceilf(fh.ascent + fh.descent) + 2.0;
63	float sliderWidth, sliderHeight;
64	SliderView::GetSliderButtonDimensions(Max(), FormatString(), &font,
65										  sliderWidth, sliderHeight);
66
67	float height = labelHeight > sliderHeight + 2.0 ?
68						labelHeight : sliderHeight + 2.0;
69
70	float minLabelWidth = LabelWidth();
71	if (rolemodel)
72		labelwidth = rolemodel->LabelWidth();
73	labelwidth = minLabelWidth > labelwidth ? minLabelWidth : labelwidth;
74
75	fSliderButtonRect.left = labelwidth;
76	fSliderButtonRect.right = fSliderButtonRect.left + sliderWidth + 2.0;
77	fSliderButtonRect.top = floorf(height / 2.0 - (sliderHeight + 2.0) / 2.0);
78	fSliderButtonRect.bottom = fSliderButtonRect.top + sliderHeight + 2.0;
79
80	fSliderButtonRect.OffsetTo(Bounds().right - fSliderButtonRect.Width(),
81							   fSliderButtonRect.top);
82
83	mpm.mini.x = labelwidth + fSliderButtonRect.Width() + 1.0;
84	mpm.maxi.x = 10000.0;
85	mpm.mini.y = mpm.maxi.y = height + 1.0;
86
87	mpm.weight = 1.0;
88
89	return mpm;
90}
91
92// layout
93BRect
94PopupSlider::layout(BRect frame)
95{
96	MoveTo(frame.LeftTop());
97	ResizeTo(frame.Width(), frame.Height());
98
99	fSliderButtonRect.OffsetTo(Bounds().right - fSliderButtonRect.Width(),
100							   fSliderButtonRect.top);
101	return Frame();
102}
103
104// MessageReceived
105void
106PopupSlider::MessageReceived(BMessage* message)
107{
108	switch (message->what) {
109		default:
110			PopupControl::MessageReceived(message);
111			break;
112	}
113}
114
115// AttachedToWindow
116void
117PopupSlider::AttachedToWindow()
118{
119	fSliderButtonRect.OffsetTo(Bounds().right - fSliderButtonRect.Width(),
120							   fSliderButtonRect.top);
121	PopupControl::AttachedToWindow();
122}
123
124// Draw
125void
126PopupSlider::Draw(BRect updateRect)
127{
128	bool enabled = IsEnabled();
129	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
130	rgb_color black;
131	if (enabled) {
132		black = tint_color(background, B_DARKEN_MAX_TINT);
133	} else {
134		black = tint_color(background, B_DISABLED_LABEL_TINT);
135	}
136	// draw label
137	BRect r(Bounds());
138	r.right = fSliderButtonRect.left - 1.0;
139	font_height fh;
140	GetFontHeight(&fh);
141	BPoint textPoint(0.0, (r.top + r.bottom) / 2.0 + fh.ascent / 2.0);
142	SetLowColor(background);
143	SetHighColor(black);
144	FillRect(r, B_SOLID_LOW);
145	DrawString(fLabel.String(), textPoint);
146	// draw slider button
147	DrawSlider(fSliderButtonRect, enabled);
148}
149
150// MouseDown
151void
152PopupSlider::MouseDown(BPoint where)
153{
154	if (fEnabled && fSliderButtonRect.Contains(where) &&
155		!fSlider->LockLooper()) {
156
157		SetPopupLocation(BPoint(fSliderButtonRect.left + 1.0
158								- fSlider->ButtonOffset(),
159								-5.0));
160		where.x -= fSliderButtonRect.left + 1.0;
161		fSlider->SetDragOffset(where.x);
162		// just to be on the safe side (avoid a dead lock)
163		fTracking = true;
164		ShowPopup(&where);
165//		fSlider->SetDragOffset(where.x);
166	}
167}
168
169// PopupShown
170void
171PopupSlider::PopupShown()
172{
173	TriggerValueChanged(fPressModel);
174	fTracking = true;
175}
176
177// PopupHidden
178void
179PopupSlider::PopupHidden(bool canceled)
180{
181	TriggerValueChanged(fReleaseModel);
182	fTracking = false;
183}
184
185// SetValue
186void
187PopupSlider::SetValue(int32 value)
188{
189	if (!fTracking) {
190/*		if (fSlider->LockLooper()) {
191			fSlider->SetValue(value);
192			fSlider->UnlockLooper();
193		} else*/
194		if (value != Value()) {
195			fSlider->SetValue(value);
196			if (LockLooperWithTimeout(0) >= B_OK) {
197				Invalidate();
198				UnlockLooper();
199			}
200		}
201	} else
202		ValueChanged(value);
203}
204
205// Value
206int32
207PopupSlider::Value() const
208{
209	int32 value = 0;
210/*	if (fSlider->LockLooper()) {
211		value = fSlider->Value();
212		fSlider->UnlockLooper();
213	} else*/
214		value = fSlider->Value();
215	return value;
216}
217
218// SetEnabled
219void
220PopupSlider::SetEnabled(bool enable)
221{
222	if (enable != fEnabled) {
223		fEnabled = enable;
224		if (LockLooper()) {
225			Invalidate();
226			UnlockLooper();
227		}
228	}
229}
230
231// SetEnabled
232bool
233PopupSlider::IsEnabled() const
234{
235	return fEnabled;
236}
237
238// TriggerValueChanged
239void
240PopupSlider::TriggerValueChanged(const BMessage* message) const
241{
242	if (message && fTarget) {
243		BMessage msg(*message);
244		msg.AddInt64("be:when", system_time());
245		msg.AddInt32("be:value", Value());
246		msg.AddPointer("be:source", (void*)this);
247		if (BLooper* looper = fTarget->Looper())
248			looper->PostMessage(&msg, fTarget);
249	}
250}
251
252// IsTracking
253bool
254PopupSlider::IsTracking() const
255{
256	return fTracking;
257}
258
259// ValueChanged
260void
261PopupSlider::ValueChanged(int32 newValue)
262{
263	TriggerValueChanged(fModel);
264}
265
266// DrawSlider
267void
268PopupSlider::DrawSlider(BRect frame, bool enabled)
269{
270	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
271	rgb_color lightShadow;
272	rgb_color darkShadow;
273	if (enabled) {
274		lightShadow = tint_color(background, B_DARKEN_2_TINT);
275		darkShadow = tint_color(background, B_DARKEN_4_TINT);
276	} else {
277		lightShadow = tint_color(background, B_DARKEN_1_TINT);
278		darkShadow = tint_color(background, B_DARKEN_2_TINT);
279	}
280
281	BeginLineArray(4);
282		AddLine(BPoint(frame.left, frame.bottom),
283				BPoint(frame.left, frame.top), lightShadow);
284		AddLine(BPoint(frame.left + 1.0, frame.top),
285				BPoint(frame.right, frame.top), lightShadow);
286		AddLine(BPoint(frame.right, frame.top + 1.0),
287				BPoint(frame.right, frame.bottom), darkShadow);
288		AddLine(BPoint(frame.right - 1.0, frame.bottom),
289				BPoint(frame.left + 1.0, frame.bottom), darkShadow);
290	EndLineArray();
291
292	frame.InsetBy(1.0, 1.0);
293	SliderView::DrawSliderButton(this, frame, Value(), FormatString(), enabled);
294}
295
296// Scale
297float
298PopupSlider::Scale(float ratio) const
299{
300	return ratio;
301}
302
303// DeScale
304float
305PopupSlider::DeScale(float ratio) const
306{
307	return ratio;
308}
309
310// SetMessage
311void
312PopupSlider::SetMessage(BMessage* message)
313{
314	delete fModel;
315	fModel = message;
316}
317
318// SetPressedMessage
319void
320PopupSlider::SetPressedMessage(BMessage* message)
321{
322	delete fPressModel;
323	fPressModel = message;
324}
325
326// SetReleasedMessage
327void
328PopupSlider::SetReleasedMessage(BMessage* message)
329{
330	delete fReleaseModel;
331	fReleaseModel = message;
332}
333
334// SetMin
335void
336PopupSlider::SetMin(int32 min)
337{
338/*	if (fSlider->LockLooper()) {
339		fSlider->SetMin(min);
340		fSlider->UnlockLooper();
341	} else*/
342		fSlider->SetMin(min);
343}
344
345// Min
346int32
347PopupSlider::Min() const
348{
349	int32 value = 0;
350/*	if (fSlider->LockLooper()) {
351		value = fSlider->Min();
352		fSlider->UnlockLooper();
353	} else*/
354		value = fSlider->Min();
355	return value;
356}
357
358// SetMax
359void
360PopupSlider::SetMax(int32 max)
361{
362/*	if (fSlider->LockLooper()) {
363		fSlider->SetMax(max);
364		fSlider->UnlockLooper();
365	} else*/
366		fSlider->SetMax(max);
367}
368
369// Max
370int32
371PopupSlider::Max() const
372{
373	int32 value = 0;
374/*	if (fSlider->LockLooper()) {
375		value = fSlider->Max();
376		fSlider->UnlockLooper();
377	} else*/
378		value = fSlider->Max();
379	return value;
380}
381
382// SetLabel
383void
384PopupSlider::SetLabel(const char* label)
385{
386	fLabel.SetTo(label);
387	Invalidate();
388}
389
390// Label
391const char*
392PopupSlider::Label() const
393{
394	return fLabel.String();
395}
396
397// LabelWidth
398float
399PopupSlider::LabelWidth()
400{
401	return _MinLabelWidth();
402}
403
404// StringForValue
405const char*
406PopupSlider::StringForValue(int32 value)
407{
408	return NULL;
409}
410
411// MaxValueStringWidth
412float
413PopupSlider::MaxValueStringWidth()
414{
415	return 0.0;
416}
417
418// SetFormatString
419void
420PopupSlider::SetFormatString(const char* formatString)
421{
422/*	if (fSlider->LockLooper()) {
423		fSlider->SetFormatString(formatString);
424		fSlider->UnlockLooper();
425	} else*/
426		fSlider->SetFormatString(formatString);
427}
428
429// FormatString
430const char*
431PopupSlider::FormatString() const
432{
433	return fSlider->FormatString();
434}
435
436// _MinLabelWidth
437float
438PopupSlider::_MinLabelWidth() const
439{
440	return ceilf(StringWidth(fLabel.String())) + 5.0;
441}
442
443/*
444// StringForValue
445const char*
446PercentSlider::StringForValue(int32 value)
447{
448	BString string;
449	string << (value * 100) / Max() << "%";
450	return
451}
452*/
453
454
455