1/*
2 * Copyright 2001 Werner Freytag - please read to the LICENSE file
3 *
4 * Copyright 2002-2006, Stephan Aßmus <superstippi@gmx.de>
5 * All rights reserved.
6 *
7 */
8
9#include "ColorSlider.h"
10
11#include <stdio.h>
12
13#include <Bitmap.h>
14#include <OS.h>
15#include <Window.h>
16#include <math.h>
17
18#include "selected_color_mode.h"
19#include "support_ui.h"
20
21#include "rgb_hsv.h"
22
23#define round(x) (int)(x+.5)
24
25enum {
26	MSG_UPDATE			= 'Updt',
27};
28
29#define MAX_X 255
30#define MAX_Y 255
31
32// constructor
33ColorSlider::ColorSlider(BPoint offset_point,
34						 selected_color_mode mode,
35						 float value1, float value2, orientation dir)
36	:  BControl(BRect(0.0, 0.0, 35.0, 265.0).OffsetToCopy(offset_point),
37				"ColorSlider", "", new BMessage(MSG_COLOR_SLIDER),
38				B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS),
39	  fMode(mode),
40	  fFixedValue1(value1),
41	  fFixedValue2(value2),
42	  fMouseDown(false),
43	  fBgBitmap(new BBitmap(Bounds(), B_RGB32, true)),
44	  fBgView(NULL),
45	  fUpdateThread(0),
46	  fUpdatePort(0),
47	  fOrientation(dir)
48{
49	SetViewColor(B_TRANSPARENT_32_BIT);
50
51	if (fBgBitmap->IsValid() && fBgBitmap->Lock()) {
52		fBgView = new BView(Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW);
53		fBgBitmap->AddChild(fBgView);
54/*		if (fOrientation == B_VERTICAL)
55			fBgView->SetOrigin(8.0, 2.0);
56		else
57			fBgView->SetOrigin(2.0, 2.0);*/
58		fBgBitmap->Unlock();
59	} else {
60		delete fBgBitmap;
61		fBgBitmap = NULL;
62		fBgView = this;
63	}
64
65	fUpdatePort = create_port(100, "color slider update port");
66
67	fUpdateThread = spawn_thread(ColorSlider::_UpdateThread, "color slider update thread", 10, this);
68	resume_thread( fUpdateThread );
69
70	Update(2);
71}
72
73// destructor
74ColorSlider::~ColorSlider()
75{
76	if (fUpdatePort)
77		delete_port(fUpdatePort);
78	if (fUpdateThread)
79		kill_thread(fUpdateThread);
80
81	delete fBgBitmap;
82}
83
84#if LIB_LAYOUT
85// layoutprefs
86minimax
87ColorSlider::layoutprefs()
88{
89	if (fOrientation == B_VERTICAL) {
90		mpm.mini.x = 36;
91		mpm.maxi.x = 36;
92		mpm.mini.y = 10 + MAX_Y / 17;
93		mpm.maxi.y = 10 + MAX_Y;
94	} else {
95		mpm.mini.x = 10 + MAX_X / 17;
96		mpm.maxi.x = 10 + MAX_X;
97		mpm.mini.y = 10.0;
98		mpm.maxi.y = 12.0;
99	}
100	mpm.weight = 1.0;
101
102	return mpm;
103}
104
105// layout
106BRect
107ColorSlider::layout(BRect frame)
108{
109	MoveTo(frame.LeftTop());
110	ResizeTo(frame.Width(), frame.Height());
111//	Update(2);
112	return Frame();
113}
114#endif // LIB_LAYOUT
115
116// AttachedToWindow
117void
118ColorSlider::AttachedToWindow()
119{
120	BControl::AttachedToWindow();
121
122	SetViewColor(B_TRANSPARENT_32_BIT);
123
124	if (fBgBitmap && fBgBitmap->Lock()) {
125		fBgView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
126		fBgView->FillRect(Bounds());
127		fBgBitmap->Unlock();
128	} else {
129		SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
130	}
131
132	Update(2);
133}
134
135
136// Invoke
137status_t
138ColorSlider::Invoke(BMessage *msg)
139{
140	if (!msg) msg = Message();
141
142	msg->RemoveName("value");
143	msg->RemoveName("begin");
144
145	switch (fMode) {
146
147		case R_SELECTED:
148		case G_SELECTED:
149		case B_SELECTED: {
150			msg->AddFloat("value", 1.0 - (float)Value() / 255);
151		} break;
152
153		case H_SELECTED: {
154			msg->AddFloat("value", (1.0 - (float)Value() / 255) * 6);
155		} break;
156
157		case S_SELECTED:
158		case V_SELECTED: {
159			msg->AddFloat("value", 1.0 - (float)Value() / 255);
160		} break;
161
162	}
163
164	// some other parts of WonderBrush rely on this.
165	// if the flag is present, it triggers generating an undo action
166	// fMouseDown is not set yet the first message is sent
167	if (!fMouseDown)
168		msg->AddBool("begin", true);
169
170	return BControl::Invoke(msg);
171}
172
173// Draw
174void
175ColorSlider::Draw(BRect updateRect)
176{
177	Update(0);
178}
179
180// FrameResized
181void
182ColorSlider::FrameResized(float width, float height)
183{
184	if (fBgBitmap) {
185		fBgBitmap->Lock();
186		delete fBgBitmap;
187	}
188	fBgBitmap = new BBitmap(Bounds(), B_RGB32, true);
189	if (fBgBitmap->IsValid() && fBgBitmap->Lock()) {
190		fBgView = new BView(Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW);
191		fBgBitmap->AddChild(fBgView);
192/*		if (fOrientation == B_VERTICAL)
193			fBgView->SetOrigin(8.0, 2.0);
194		else
195			fBgView->SetOrigin(2.0, 2.0);*/
196		fBgBitmap->Unlock();
197	} else {
198		delete fBgBitmap;
199		fBgBitmap = NULL;
200		fBgView = this;
201	}
202	Update(2);
203}
204
205// MouseDown
206void
207ColorSlider::MouseDown(BPoint where)
208{
209	Window()->Activate();
210
211	SetMouseEventMask(B_POINTER_EVENTS, B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
212	_TrackMouse(where);
213	fMouseDown = true;
214}
215
216// MouseUp
217void
218ColorSlider::MouseUp(BPoint where)
219{
220	fMouseDown = false;
221}
222
223// MouseMoved
224void
225ColorSlider::MouseMoved( BPoint where, uint32 code, const BMessage* dragMessage)
226{
227	if (dragMessage || !fMouseDown)
228		return;
229
230	_TrackMouse(where);
231}
232
233// SetValue
234void
235ColorSlider::SetValue(int32 value)
236{
237	value = max_c(min_c(value, 255), 0);
238	if (value != Value()) {
239		BControl::SetValue(value);
240
241		Update(1);
242	}
243}
244
245// Update
246void
247ColorSlider::Update(int depth)
248{
249	// depth: 0 = onscreen only, 1 = bitmap 1, 2 = bitmap 0
250	if (depth == 2) {
251		write_port(fUpdatePort, MSG_UPDATE, NULL, 0);
252		return;
253	}
254
255	if (!Parent())
256		return;
257
258	fBgBitmap->Lock();
259
260//	BRect r(fBgView->Bounds());
261	BRect r(Bounds());
262	BRect bounds(r);
263	if (fOrientation == B_VERTICAL) {
264//		r.OffsetBy(-8.0, -2.0);
265		r.InsetBy(6.0, 3.0);
266//		bounds.Set(-8.0, -2.0, r.right - 8.0, r.bottom - 2.0);
267//		bounds.OffsetBy(8.0, 2.0);
268	} else {
269//		r.OffsetBy(-2.0, -2.0);
270//		bounds.Set(-2.0, -2.0, r.right - 2.0, r.bottom - 2.0);
271//		bounds.OffsetBy(2.0, 2.0);
272	}
273
274	fBgBitmap->Unlock();
275
276	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
277	rgb_color shadow = tint_color(background, B_DARKEN_1_TINT);
278	rgb_color darkShadow = tint_color(background, B_DARKEN_3_TINT);
279	rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
280
281	if (depth >= 1) {
282
283		fBgBitmap->Lock();
284
285
286		// frame
287		stroke_frame(fBgView, r, shadow, shadow, light, light);
288		r.InsetBy(1.0, 1.0);
289		stroke_frame(fBgView, r, darkShadow, darkShadow, background, background);
290
291		if (fOrientation == B_VERTICAL) {
292			// clear area left and right from slider
293			fBgView->SetHighColor( background );
294			fBgView->FillRect( BRect(bounds.left, bounds.top, bounds.left + 5.0, bounds.bottom) );
295			fBgView->FillRect( BRect(bounds.right - 5.0, bounds.top, bounds.right, bounds.bottom) );
296		}
297/*
298		// marker
299		if (fOrientation == B_VERTICAL) {
300			// clear area left and right from slider
301			fBgView->SetHighColor( background );
302			fBgView->FillRect( BRect(bounds.left, bounds.top, bounds.left + 5.0, bounds.bottom) );
303			fBgView->FillRect( BRect(bounds.right - 5.0, bounds.top, bounds.right, bounds.bottom) );
304			// draw the triangle markers
305			fBgView->SetHighColor( 0, 0, 0 );
306			float value = Value();
307			fBgView->StrokeLine( BPoint(bounds.left, value - 2.0), BPoint(bounds.left + 5.0, value + 3.0));
308			fBgView->StrokeLine( BPoint(bounds.left, value + 8.0));
309			fBgView->StrokeLine( BPoint(bounds.left, value - 2.0));
310
311			fBgView->StrokeLine( BPoint(bounds.right, value - 2.0), BPoint(bounds.right - 5.0, value + 3.0));
312			fBgView->StrokeLine( BPoint(bounds.right, value + 8.0));
313			fBgView->StrokeLine( BPoint(bounds.right, value - 2.0));
314		} else {
315			r.InsetBy(1.0, 1.0);
316			float value = (Value() / 255.0) * (bounds.Width() - 4.0);
317			if (value - 2 > r.left) {
318				fBgView->SetHighColor( 255, 255, 255 );
319				fBgView->StrokeLine( BPoint(value - 2, bounds.top + 2.0),
320									 BPoint(value - 2, bounds.bottom - 2.0));
321			}
322			if (value - 1 > r.left) {
323				fBgView->SetHighColor( 0, 0, 0 );
324				fBgView->StrokeLine( BPoint(value - 1, bounds.top + 2.0),
325									 BPoint(value - 1, bounds.bottom - 2.0));
326			}
327			if (value + 1 < r.right) {
328				fBgView->SetHighColor( 0, 0, 0 );
329				fBgView->StrokeLine( BPoint(value + 1, bounds.top + 2.0),
330									 BPoint(value + 1, bounds.bottom - 2.0));
331			}
332			if (value + 2 < r.right) {
333				fBgView->SetHighColor( 255, 255, 255 );
334				fBgView->StrokeLine( BPoint(value + 2, bounds.top + 2.0),
335									 BPoint(value + 2, bounds.bottom - 2.0));
336			}
337		}*/
338
339		fBgView->Sync();
340
341		fBgBitmap->Unlock();
342	} else
343		r.InsetBy(1.0, 1.0);
344
345	DrawBitmap(fBgBitmap, BPoint(0.0, 0.0));
346
347	// marker
348	if (fOrientation == B_VERTICAL) {
349		// draw the triangle markers
350		SetHighColor( 0, 0, 0 );
351		float value = Value();
352		StrokeLine( BPoint(bounds.left, value),
353					BPoint(bounds.left + 5.0, value + 5.0));
354		StrokeLine( BPoint(bounds.left, value + 10.0));
355		StrokeLine( BPoint(bounds.left, value));
356
357		StrokeLine( BPoint(bounds.right, value),
358					BPoint(bounds.right - 5.0, value + 5.0));
359		StrokeLine( BPoint(bounds.right, value + 10.0));
360		StrokeLine( BPoint(bounds.right, value));
361	} else {
362		r.InsetBy(1.0, 1.0);
363		float value = (Value() / 255.0) * (bounds.Width() - 4.0);
364		if (value > r.left) {
365			SetHighColor( 255, 255, 255 );
366			StrokeLine( BPoint(value, bounds.top + 2.0),
367						BPoint(value, bounds.bottom - 2.0));
368		}
369		if (value + 1 > r.left) {
370			SetHighColor( 0, 0, 0 );
371			StrokeLine( BPoint(value + 1, bounds.top + 2.0),
372						BPoint(value + 1, bounds.bottom - 2.0));
373		}
374		if (value + 3 < r.right) {
375			SetHighColor( 0, 0, 0 );
376			StrokeLine( BPoint(value + 3, bounds.top + 2.0),
377						BPoint(value + 3, bounds.bottom - 2.0));
378		}
379		if (value + 4 < r.right) {
380			SetHighColor( 255, 255, 255 );
381			StrokeLine( BPoint(value + 4, bounds.top + 2.0),
382						BPoint(value + 4, bounds.bottom - 2.0));
383		}
384	}
385	SetOrigin(0.0, 0.0);
386}
387
388// SetModeAndValues
389void
390ColorSlider::SetModeAndValues(selected_color_mode mode,
391							  float value1, float value2)
392{
393	float R(0), G(0), B(0);
394	float h(0), s(0), v(0);
395
396	fBgBitmap->Lock();
397
398	switch (fMode) {
399
400		case R_SELECTED: {
401			R = 255 - Value();
402			G = round(fFixedValue1 * 255.0);
403			B = round(fFixedValue2 * 255.0);
404		}; break;
405
406		case G_SELECTED: {
407			R = round(fFixedValue1 * 255.0);
408			G = 255 - Value();
409			B = round(fFixedValue2 * 255.0);
410		}; break;
411
412		case B_SELECTED: {
413			R = round(fFixedValue1 * 255.0);
414			G = round(fFixedValue2 * 255.0);
415			B = 255 - Value();
416		}; break;
417
418		case H_SELECTED: {
419			h = (1.0 - (float)Value()/255.0)*6.0;
420			s = fFixedValue1;
421			v = fFixedValue2;
422		}; break;
423
424		case S_SELECTED: {
425			h = fFixedValue1;
426			s = 1.0 - (float)Value()/255.0;
427			v = fFixedValue2;
428		}; break;
429
430		case V_SELECTED: {
431			h = fFixedValue1;
432			s = fFixedValue2;
433			v = 1.0 - (float)Value()/255.0;
434		}; break;
435	}
436
437	if (fMode & (H_SELECTED|S_SELECTED|V_SELECTED) ) {
438		HSV_to_RGB(h, s, v, R, G, B);
439		R*=255.0; G*=255.0; B*=255.0;
440	}
441
442	rgb_color color = { round(R), round(G), round(B), 255 };
443
444	fMode = mode;
445	SetOtherValues(value1, value2);
446	fBgBitmap->Unlock();
447
448	SetMarkerToColor( color );
449	Update(2);
450}
451
452// SetOtherValues
453void
454ColorSlider::SetOtherValues(float value1, float value2)
455{
456	fFixedValue1 = value1;
457	fFixedValue2 = value2;
458
459	if (fMode != H_SELECTED) {
460		Update(2);
461	}
462}
463
464// GetOtherValues
465void
466ColorSlider::GetOtherValues(float* value1, float* value2) const
467{
468	if (value1 && value2) {
469		*value1 = fFixedValue1;
470		*value2 = fFixedValue2;
471	}
472}
473
474// SetMarkerToColor
475void
476ColorSlider::SetMarkerToColor(rgb_color color)
477{
478	float h = 0.0f;
479	float s = 0.0f;
480	float v = 0.0f;
481	if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) {
482		RGB_to_HSV((float)color.red / 255.0f, (float)color.green / 255.0f,
483			(float)color.blue / 255.0f, h, s, v);
484	}
485
486	switch (fMode) {
487		case R_SELECTED:
488			SetValue(255 - color.red);
489			break;
490
491		case G_SELECTED:
492			SetValue(255 - color.green);
493			break;
494
495		case B_SELECTED:
496			SetValue(255 - color.blue);
497			break;
498
499		case H_SELECTED:
500			SetValue(255.0 - round(h / 6.0 * 255.0));
501			break;
502
503		case S_SELECTED:
504			SetValue(255.0 - round(s * 255.0));
505			break;
506
507		case V_SELECTED:
508			SetValue(255.0 - round(v * 255.0));
509			break;
510	}
511}
512
513// _UpdateThread
514status_t
515ColorSlider::_UpdateThread(void* data)
516{
517	// initializing
518	ColorSlider* colorSlider = (ColorSlider*)data;
519
520	bool looperLocked = colorSlider->LockLooper();
521
522	port_id	port = colorSlider->fUpdatePort;
523	orientation orient = colorSlider->fOrientation;
524
525	if (looperLocked)
526		colorSlider->UnlockLooper();
527
528	float h, s, v, r, g, b;
529	int R, G, B;
530
531	// drawing
532
533    int32 msg_code;
534    char msg_buffer;
535
536	while (true) {
537
538		port_info info;
539
540		do {
541
542			read_port(port, &msg_code, &msg_buffer, sizeof(msg_buffer));
543			get_port_info(port, &info);
544
545		} while (info.queue_count);
546
547		if (colorSlider->LockLooper()) {
548
549			uint 	colormode = colorSlider->fMode;
550			float	fixedvalue1 = colorSlider->fFixedValue1;
551			float	fixedvalue2 = colorSlider->fFixedValue2;
552
553			BBitmap* bitmap = colorSlider->fBgBitmap;
554			BView* view = colorSlider->fBgView;
555
556			bitmap->Lock();
557
558			colorSlider->UnlockLooper();
559
560			view->BeginLineArray(256);
561
562			switch (colormode) {
563
564				case R_SELECTED: {
565					G = round(fixedvalue1 * 255);
566					B = round(fixedvalue2 * 255);
567					for (int R = 0; R < 256; ++R) {
568						_DrawColorLineY( view, R, R, G, B );
569					}
570				}; break;
571
572				case G_SELECTED: {
573					R = round(fixedvalue1 * 255);
574					B = round(fixedvalue2 * 255);
575					for (int G = 0; G < 256; ++G) {
576						_DrawColorLineY( view, G, R, G, B );
577					}
578				}; break;
579
580				case B_SELECTED: {
581					R = round(fixedvalue1 * 255);
582					G = round(fixedvalue2 * 255);
583					for (int B = 0; B < 256; ++B) {
584						_DrawColorLineY( view, B, R, G, B );
585					}
586				}; break;
587
588				case H_SELECTED: {
589					s = 1.0;//fixedvalue1;
590					v = 1.0;//fixedvalue2;
591					if (orient == B_VERTICAL) {
592						for (int y = 0; y < 256; ++y) {
593							HSV_to_RGB( (float)y*6.0/255.0, s, v, r, g, b );
594							_DrawColorLineY( view, y, r*255, g*255, b*255 );
595						}
596					} else {
597						for (int x = 0; x < 256; ++x) {
598							HSV_to_RGB( (float)x*6.0/255.0, s, v, r, g, b );
599							_DrawColorLineX( view, x, r*255, g*255, b*255 );
600						}
601					}
602				}; break;
603
604				case S_SELECTED: {
605					h = fixedvalue1;
606					v = 1.0;//fixedvalue2;
607					for (int y = 0; y < 256; ++y) {
608						HSV_to_RGB( h, (float)y/255, v, r, g, b );
609						_DrawColorLineY( view, y, r*255, g*255, b*255 );
610					}
611				}; break;
612
613				case V_SELECTED: {
614					h = fixedvalue1;
615					s = 1.0;//fixedvalue2;
616					for (int y = 0; y < 256; ++y) {
617						HSV_to_RGB( h, s, (float)y/255, r, g, b );
618						_DrawColorLineY( view, y, r*255, g*255, b*255 );
619					}
620				}; break;
621			}
622
623			view->EndLineArray();
624			view->Sync();
625			bitmap->Unlock();
626
627			if (colorSlider->LockLooper()) {
628				colorSlider->Update(1);
629				colorSlider->UnlockLooper();
630			}
631		}
632	}
633	return B_OK;
634}
635
636// _DrawColorLineY
637void
638ColorSlider::_DrawColorLineY(BView *view, float y,
639							 int r, int g, int b)
640{
641	rgb_color color = { r, g, b, 255 };
642	y = 255.0 - y;
643	view->AddLine( BPoint(8.0, y + 5.0), BPoint(27.0, y + 5.0), color );
644}
645
646// _DrawColorLineX
647void
648ColorSlider::_DrawColorLineX(BView *view, float x,
649							 int r, int g, int b)
650{
651	rgb_color color = { r, g, b, 255 };
652	BRect bounds(view->Bounds());
653	x = (255.0 - x) * (bounds.Width() - 2.0) / 255.0 + 2.0;
654	view->AddLine( BPoint(x, bounds.top + 2.0), BPoint(x, bounds.bottom - 2.0), color );
655}
656
657// _TrackMouse
658void
659ColorSlider::_TrackMouse(BPoint where)
660{
661	if (fOrientation == B_VERTICAL) {
662		SetValue((int)where.y - 2);
663	} else {
664		BRect b(Bounds());
665		SetValue((int)(((where.x - 2.0) / b.Width()) * 255.0));
666	}
667	Invoke();
668}
669