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 */
8
9#include "AlphaSlider.h"
10
11#include <stdio.h>
12
13#include <AppDefs.h>
14#include <Bitmap.h>
15#include <Message.h>
16#include <Window.h>
17
18#include "ui_defines.h"
19#include "support_ui.h"
20
21// constructor
22AlphaSlider::AlphaSlider(orientation dir, BMessage* message)
23	: BControl(dir == B_HORIZONTAL ? BRect(0, 0, 255 + 4, 7 + 4)
24								   : BRect(0, 0, 7 + 4, 255 + 4),
25			   "alpha slider", NULL, message,
26
27			   B_FOLLOW_NONE,
28			   B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE),
29
30	  fBitmap(NULL),
31	  fColor(kBlack),
32	  fDragging(false),
33	  fOrientation(dir)
34{
35	FrameResized(Bounds().Width(), Bounds().Height());
36
37	SetViewColor(B_TRANSPARENT_32_BIT);
38	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
39
40	SetValue(255);
41}
42
43// destructor
44AlphaSlider::~AlphaSlider()
45{
46	delete fBitmap;
47}
48
49#if LIB_LAYOUT
50// layoutprefs
51minimax
52AlphaSlider::layoutprefs()
53{
54	mpm.mini.x = 256 + 4;
55	mpm.maxi.x = mpm.mini.x + 10000;
56	mpm.mini.y = 8 + 4;
57	mpm.maxi.y = mpm.mini.y + 10;
58
59	mpm.weight = 1.0;
60
61	return mpm;
62}
63
64// layout
65BRect
66AlphaSlider::layout(BRect frame)
67{
68	MoveTo(frame.LeftTop());
69	ResizeTo(frame.Width(), frame.Height());
70	return Frame();
71}
72#endif // LIB_LAYOUT
73
74// WindowActivated
75void
76AlphaSlider::WindowActivated(bool active)
77{
78	if (IsFocus())
79		Invalidate();
80}
81
82// MakeFocus
83void
84AlphaSlider::MakeFocus(bool focus)
85{
86	if (focus != IsFocus()) {
87		BControl::MakeFocus(focus);
88		Invalidate();
89	}
90}
91
92// MouseDown
93void
94AlphaSlider::MouseDown(BPoint where)
95{
96	if (!IsEnabled())
97		return;
98
99//	if (!IsFocus())
100//		MakeFocus(true);
101
102	fDragging = true;
103	SetValue(_ValueFor(where));
104
105	SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
106}
107
108// MouseUp
109void
110AlphaSlider::MouseUp(BPoint where)
111{
112	fDragging = false;
113}
114
115// MouseMoved
116void
117AlphaSlider::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
118{
119	if (!IsEnabled() || !fDragging)
120		return;
121
122	SetValue(_ValueFor(where));
123}
124
125// KeyDown
126void
127AlphaSlider::KeyDown(const char* bytes, int32 numBytes)
128{
129	if (!IsEnabled() || numBytes <= 0) {
130		BControl::KeyDown(bytes, numBytes);
131		return;
132	}
133
134	switch (bytes[0]) {
135		case B_HOME:
136			SetValue(0);
137			break;
138		case B_END:
139			SetValue(255);
140			break;
141
142		case B_LEFT_ARROW:
143		case B_UP_ARROW:
144			SetValue(max_c(0, Value() - 1));
145			break;
146		case B_RIGHT_ARROW:
147		case B_DOWN_ARROW:
148			SetValue(min_c(255, Value() + 1));
149			break;
150
151		default:
152			BControl::KeyDown(bytes, numBytes);
153			break;
154	}
155}
156
157// Draw
158void
159AlphaSlider::Draw(BRect updateRect)
160{
161	BRect b = _BitmapRect();
162	b.InsetBy(-2.0, -2.0);
163
164	bool isFocus = IsFocus() && Window()->IsActive();
165
166	rgb_color bg = LowColor();
167	rgb_color shadow;
168	rgb_color darkShadow;
169	rgb_color light;
170	rgb_color black;
171
172	if (IsEnabled()) {
173		shadow = tint_color(bg, B_DARKEN_1_TINT);
174		darkShadow = tint_color(bg, B_DARKEN_3_TINT);
175		light = tint_color(bg, B_LIGHTEN_MAX_TINT);
176		black = tint_color(bg, B_DARKEN_MAX_TINT);
177	} else {
178		shadow = bg;
179		darkShadow = tint_color(bg, B_DARKEN_1_TINT);
180		light = tint_color(bg, B_LIGHTEN_2_TINT);
181		black = tint_color(bg, B_DARKEN_2_TINT);
182	}
183
184	rgb_color focus = isFocus ? ui_color(B_KEYBOARD_NAVIGATION_COLOR)
185							  : black;
186
187	stroke_frame(this, b, shadow, shadow, light, light);
188	b.InsetBy(1.0, 1.0);
189	if (isFocus)
190		stroke_frame(this, b, focus, focus, focus, focus);
191	else
192		stroke_frame(this, b, darkShadow, darkShadow, bg, bg);
193	b.InsetBy(1.0, 1.0);
194
195	DrawBitmap(fBitmap, b.LeftTop());
196
197	// value marker
198	if (fOrientation == B_HORIZONTAL) {
199		float pos = floorf(b.left + Value() * b.Width() / 255.0 + 0.5);
200
201		if (pos - 2 >= b.left) {
202			SetHighColor(kWhite);
203			StrokeLine(BPoint(pos - 2, b.top), BPoint(pos - 2, b.bottom));
204		}
205		if (pos - 1 >= b.left) {
206			SetHighColor(kBlack);
207			StrokeLine(BPoint(pos - 1, b.top), BPoint(pos - 1, b.bottom));
208		}
209		if (pos + 1 <= b.right) {
210			SetHighColor(kBlack);
211			StrokeLine(BPoint(pos + 1, b.top), BPoint(pos + 1, b.bottom));
212		}
213		if (pos + 2 <= b.right) {
214			SetHighColor(kWhite);
215			StrokeLine(BPoint(pos + 2, b.top), BPoint(pos + 2, b.bottom));
216		}
217	} else {
218		float pos = floorf(b.top + Value() * b.Height() / 255.0 + 0.5);
219
220		if (pos - 2 >= b.top) {
221			SetHighColor(kWhite);
222			StrokeLine(BPoint(b.left, pos - 2), BPoint(b.right, pos - 2));
223		}
224		if (pos - 1 >= b.top) {
225			SetHighColor(kBlack);
226			StrokeLine(BPoint(b.left, pos - 1), BPoint(b.right, pos - 1));
227		}
228		if (pos + 1 <= b.bottom) {
229			SetHighColor(kBlack);
230			StrokeLine(BPoint(b.left, pos + 1), BPoint(b.right, pos + 1));
231		}
232		if (pos + 2 <= b.bottom) {
233			SetHighColor(kWhite);
234			StrokeLine(BPoint(b.left, pos + 2), BPoint(b.right, pos + 2));
235		}
236	}
237}
238
239// FrameResized
240void
241AlphaSlider::FrameResized(float width, float height)
242{
243	BRect r = _BitmapRect();
244	_AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1);
245	_UpdateColors();
246	Invalidate();
247
248}
249
250// SetValue
251void
252AlphaSlider::SetValue(int32 value)
253{
254	if (value == Value())
255		return;
256
257	Invoke(Message());
258	BControl::SetValue(value);
259}
260
261// SetEnabled
262void
263AlphaSlider::SetEnabled(bool enabled)
264{
265	if (enabled == IsEnabled())
266		return;
267
268	BControl::SetEnabled(enabled);
269
270	_UpdateColors();
271	Invalidate();
272}
273
274// #pragma mark -
275
276// SetColor
277void
278AlphaSlider::SetColor(const rgb_color& color)
279{
280	if ((uint32&)fColor == (uint32&)color)
281		return;
282
283	fColor = color;
284
285	_UpdateColors();
286	Invalidate();
287}
288
289// #pragma mark -
290
291// blend_colors
292inline void
293blend_colors(uint8* d, uint8 alpha, uint8 c1, uint8 c2, uint8 c3)
294{
295	if (alpha > 0) {
296		if (alpha == 255) {
297			d[0] = c1;
298			d[1] = c2;
299			d[2] = c3;
300		} else {
301			d[0] += (uint8)(((c1 - d[0]) * alpha) >> 8);
302			d[1] += (uint8)(((c2 - d[1]) * alpha) >> 8);
303			d[2] += (uint8)(((c3 - d[2]) * alpha) >> 8);
304		}
305	}
306}
307
308// _UpdateColors
309void
310AlphaSlider::_UpdateColors()
311{
312	if (!fBitmap || !fBitmap->IsValid())
313		return;
314
315	// fill in top row with alpha gradient
316	uint8* topRow = (uint8*)fBitmap->Bits();
317	uint32 width = fBitmap->Bounds().IntegerWidth() + 1;
318
319	uint8* p = topRow;
320	rgb_color color = fColor;
321	if (!IsEnabled()) {
322		// blend low color and color to give disabled look
323		rgb_color bg = LowColor();
324		color.red = (uint8)(((uint32)bg.red + color.red) / 2);
325		color.green = (uint8)(((uint32)bg.green + color.green) / 2);
326		color.blue = (uint8)(((uint32)bg.blue + color.blue) / 2);
327	}
328	for (uint32 x = 0; x < width; x++) {
329		p[0] = color.blue;
330		p[1] = color.green;
331		p[2] = color.red;
332		p[3] = x * 255 / width;
333		p += 4;
334	}
335	// copy top row to rest of bitmap
336	uint32 height = fBitmap->Bounds().IntegerHeight() + 1;
337	uint32 bpr = fBitmap->BytesPerRow();
338	uint8* dstRow = topRow + bpr;
339	for (uint32 i = 1; i < height; i++) {
340		memcpy(dstRow, topRow, bpr);
341		dstRow += bpr;
342	}
343	// post process bitmap to underlay it with a pattern to visualize alpha
344	uint8* row = topRow;
345	for (uint32 i = 0; i < height; i++) {
346		uint8* p = row;
347		for (uint32 x = 0; x < width; x++) {
348			uint8 alpha = p[3];
349			if (alpha < 255) {
350				p[3] = 255;
351				alpha = 255 - alpha;
352				if (x % 8 >= 4) {
353					if (i % 8 >= 4) {
354						blend_colors(p, alpha,
355									 kAlphaLow.blue,
356									 kAlphaLow.green,
357									 kAlphaLow.red);
358					} else {
359						blend_colors(p, alpha,
360									 kAlphaHigh.blue,
361									 kAlphaHigh.green,
362									 kAlphaHigh.red);
363					}
364				} else {
365					if (i % 8 >= 4) {
366						blend_colors(p, alpha,
367									 kAlphaHigh.blue,
368									 kAlphaHigh.green,
369									 kAlphaHigh.red);
370					} else {
371						blend_colors(p, alpha,
372									 kAlphaLow.blue,
373									 kAlphaLow.green,
374									 kAlphaLow.red);
375					}
376				}
377			}
378			p += 4;
379		}
380		row += bpr;
381	}
382}
383
384// _AllocBitmap
385void
386AlphaSlider::_AllocBitmap(int32 width, int32 height)
387{
388	if (width < 2 || height < 2)
389		return;
390
391	delete fBitmap;
392	fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32);
393}
394
395// _BitmapRect
396BRect
397AlphaSlider::_BitmapRect() const
398{
399	BRect r = Bounds();
400	r.InsetBy(2, 2);
401	return r;
402}
403
404// _ValueFor
405int32
406AlphaSlider::_ValueFor(BPoint where) const
407{
408	BRect r = _BitmapRect();
409
410	int32 value;
411	if (fOrientation == B_HORIZONTAL)
412		value = (int32)(255 * (where.x - r.left) / r.Width() + 0.5);
413	else
414		value = (int32)(255 * (where.y - r.top) / r.Height() + 0.5);
415
416	value = max_c(0, value);
417	value = min_c(255, value);
418
419	return value;
420}
421
422