1 /*
2 * Copyright 2001 Werner Freytag - please read to the LICENSE file
3 *
4 * Copyright 2002-2015, 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 <ControlLook.h>
15 #include <LayoutUtils.h>
16 #include <OS.h>
17 #include <Window.h>
18 #include <math.h>
19
20 #include "support_ui.h"
21
22 #include "rgb_hsv.h"
23
24
25 #define round(x) (int)(x+.5)
26
27 enum {
28 MSG_UPDATE = 'Updt',
29 };
30
31 #define MAX_X 255
32 #define MAX_Y 255
33
34
ColorSlider(SelectedColorMode mode,float value1,float value2,orientation dir,border_style border)35 ColorSlider::ColorSlider(SelectedColorMode mode,
36 float value1, float value2, orientation dir, border_style border)
37 : BControl("ColorSlider", "", new BMessage(MSG_COLOR_SLIDER),
38 B_WILL_DRAW | B_FRAME_EVENTS)
39 {
40 _Init(mode, value1, value2, dir, border);
41 FrameResized(Bounds().Width(), Bounds().Height());
42 }
43
44
ColorSlider(BPoint offsetPoint,SelectedColorMode mode,float value1,float value2,orientation dir,border_style border)45 ColorSlider::ColorSlider(BPoint offsetPoint, SelectedColorMode mode,
46 float value1, float value2, orientation dir, border_style border)
47 : BControl(BRect(0.0, 0.0, 35.0, 265.0).OffsetToCopy(offsetPoint),
48 "ColorSlider", "", new BMessage(MSG_COLOR_SLIDER),
49 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS)
50 {
51 _Init(mode, value1, value2, dir, border);
52 FrameResized(Bounds().Width(), Bounds().Height());
53 }
54
55
~ColorSlider()56 ColorSlider::~ColorSlider()
57 {
58 delete fBitmap;
59 }
60
61
62 BSize
MinSize()63 ColorSlider::MinSize()
64 {
65 BSize minSize;
66 if (fOrientation == B_VERTICAL)
67 minSize = BSize(36, 10 + MAX_Y / 17);
68 else
69 minSize = BSize(10 + MAX_X / 17, 10);
70 return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize);
71 }
72
73
74 BSize
PreferredSize()75 ColorSlider::PreferredSize()
76 {
77 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
78 }
79
80
81 BSize
MaxSize()82 ColorSlider::MaxSize()
83 {
84 BSize maxSize;
85 if (fOrientation == B_VERTICAL)
86 maxSize = BSize(36, 10 + MAX_Y);
87 else
88 maxSize = BSize(10 + MAX_X, 18);
89 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
90 }
91
92
93 void
AttachedToWindow()94 ColorSlider::AttachedToWindow()
95 {
96 }
97
98
99 status_t
Invoke(BMessage * message)100 ColorSlider::Invoke(BMessage* message)
101 {
102 if (message == NULL)
103 message = Message();
104
105 if (message == NULL)
106 return BControl::Invoke(message);
107
108 message->RemoveName("value");
109 message->RemoveName("begin");
110
111 switch (fMode) {
112 case R_SELECTED:
113 case G_SELECTED:
114 case B_SELECTED:
115 message->AddFloat("value", 1.0 - (float)Value() / 255);
116 break;
117
118 case H_SELECTED:
119 message->AddFloat("value", (1.0 - (float)Value() / 255) * 6);
120 break;
121
122 case S_SELECTED:
123 case V_SELECTED:
124 message->AddFloat("value", 1.0 - (float)Value() / 255);
125 break;
126 }
127
128 // some other parts of WonderBrush rely on this.
129 // if the flag is present, it triggers generating an undo action
130 // fMouseDown is not set yet the first message is sent
131 if (!fMouseDown)
132 message->AddBool("begin", true);
133
134 return BControl::Invoke(message);
135 }
136
137
138 void
Draw(BRect updateRect)139 ColorSlider::Draw(BRect updateRect)
140 {
141 if (fBitmapDirty && fBitmap != NULL) {
142 _FillBitmap(fBitmap, fMode, fFixedValue1, fFixedValue2, fOrientation);
143 fBitmapDirty = false;
144 }
145
146 BRect bounds = _BitmapRect();
147
148 // Frame
149 if (fBorderStyle == B_FANCY_BORDER) {
150 bounds.InsetBy(-2, -2);
151 rgb_color color = LowColor();
152 be_control_look->DrawTextControlBorder(this, bounds, updateRect,
153 color);
154 }
155
156 // Color slider fill
157 if (fBitmap != NULL)
158 DrawBitmap(fBitmap, bounds.LeftTop());
159 else {
160 SetHighColor(255, 0, 0);
161 FillRect(bounds);
162 }
163
164 // Marker background
165 if (fOrientation == B_VERTICAL) {
166 bounds.InsetBy(-2, -2);
167 BRect r = Bounds();
168 FillRect(BRect(r.left, r.top, bounds.left - 1, r.bottom),
169 B_SOLID_LOW);
170 FillRect(BRect(bounds.right + 1, r.top, r.right, r.bottom),
171 B_SOLID_LOW);
172 FillRect(
173 BRect(bounds.left, r.top, bounds.right, bounds.top - 1),
174 B_SOLID_LOW);
175 FillRect(
176 BRect(bounds.left, bounds.bottom + 1, bounds.right, r.bottom),
177 B_SOLID_LOW);
178 }
179
180 // marker
181 if (fOrientation == B_VERTICAL) {
182 // draw the triangle markers
183 SetHighColor(0, 0, 0);
184 BRect r = Bounds();
185 float offset = Value() * (r.Height() - 10) / 255.0;
186 _DrawTriangle(
187 BPoint(r.left, offset),
188 BPoint(r.left + 5.0, offset + 5.0),
189 BPoint(r.left, offset + 10.0));
190
191 _DrawTriangle(
192 BPoint(r.right, offset),
193 BPoint(r.right - 5.0, offset + 5.0),
194 BPoint(r.right, offset + 10.0));
195 } else {
196 BRect r = bounds;
197 float x = bounds.left - 2 + (255 - Value()) * bounds.Width() / 255.0;
198 if (x > r.left) {
199 SetHighColor(255, 255, 255);
200 StrokeLine(BPoint(x, bounds.top),
201 BPoint(x, bounds.bottom));
202 }
203 if (x + 1 > r.left) {
204 SetHighColor(0, 0, 0);
205 StrokeLine(BPoint(x + 1, bounds.top),
206 BPoint(x + 1, bounds.bottom));
207 }
208 if (x + 3 < r.right) {
209 SetHighColor(0, 0, 0);
210 StrokeLine(BPoint(x + 3, bounds.top),
211 BPoint(x + 3, bounds.bottom));
212 }
213 if (x + 4 < r.right) {
214 SetHighColor(255, 255, 255);
215 StrokeLine(BPoint(x + 4, bounds.top),
216 BPoint(x + 4, bounds.bottom));
217 }
218 }
219 }
220
221
222 void
FrameResized(float width,float height)223 ColorSlider::FrameResized(float width, float height)
224 {
225 BRect r = _BitmapRect();
226 _AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1);
227 Invalidate();
228 }
229
230
231 void
MouseDown(BPoint where)232 ColorSlider::MouseDown(BPoint where)
233 {
234 SetMouseEventMask(B_POINTER_EVENTS,
235 B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
236 _TrackMouse(where);
237 fMouseDown = true;
238 }
239
240
241 void
MouseUp(BPoint where)242 ColorSlider::MouseUp(BPoint where)
243 {
244 fMouseDown = false;
245 }
246
247
248 void
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)249 ColorSlider::MouseMoved( BPoint where, uint32 code,
250 const BMessage* dragMessage)
251 {
252 if (dragMessage != NULL || !fMouseDown)
253 return;
254
255 _TrackMouse(where);
256 }
257
258
259 void
SetValue(int32 value)260 ColorSlider::SetValue(int32 value)
261 {
262 value = max_c(min_c(value, 255), 0);
263 if (value != Value()) {
264 BControl::SetValue(value);
265 Invalidate();
266 }
267 }
268
269
270 void
SetModeAndValues(SelectedColorMode mode,float value1,float value2)271 ColorSlider::SetModeAndValues(SelectedColorMode mode,
272 float value1, float value2)
273 {
274 float R = 0;
275 float G = 0;
276 float B = 0;
277 float h = 0;
278 float s = 0;
279 float v = 0;
280
281 switch (fMode) {
282 case R_SELECTED:
283 R = 255 - Value();
284 G = round(fFixedValue1 * 255.0);
285 B = round(fFixedValue2 * 255.0);
286 break;
287
288 case G_SELECTED:
289 R = round(fFixedValue1 * 255.0);
290 G = 255 - Value();
291 B = round(fFixedValue2 * 255.0);
292 break;
293
294 case B_SELECTED:
295 R = round(fFixedValue1 * 255.0);
296 G = round(fFixedValue2 * 255.0);
297 B = 255 - Value();
298 break;
299
300 case H_SELECTED:
301 h = (1.0 - (float)Value()/255.0)*6.0;
302 s = fFixedValue1;
303 v = fFixedValue2;
304 break;
305
306 case S_SELECTED:
307 h = fFixedValue1;
308 s = 1.0 - (float)Value()/255.0;
309 v = fFixedValue2;
310 break;
311
312 case V_SELECTED:
313 h = fFixedValue1;
314 s = fFixedValue2;
315 v = 1.0 - (float)Value()/255.0;
316 break;
317 }
318
319 if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) {
320 HSV_to_RGB(h, s, v, R, G, B);
321 R *= 255.0;
322 G *= 255.0;
323 B *= 255.0;
324 }
325
326 rgb_color color = { (uint8)round(R), (uint8)round(G), (uint8)round(B),
327 255 };
328
329 fMode = mode;
330 SetOtherValues(value1, value2);
331
332 SetMarkerToColor(color);
333 _Update();
334 }
335
336
337 void
SetOtherValues(float value1,float value2)338 ColorSlider::SetOtherValues(float value1, float value2)
339 {
340 fFixedValue1 = value1;
341 fFixedValue2 = value2;
342
343 if (fMode != H_SELECTED)
344 _Update();
345 }
346
347
348 void
GetOtherValues(float * value1,float * value2) const349 ColorSlider::GetOtherValues(float* value1, float* value2) const
350 {
351 if (value1 != NULL)
352 *value1 = fFixedValue1;
353
354 if (value2 != NULL)
355 *value2 = fFixedValue2;
356 }
357
358
359 void
SetMarkerToColor(rgb_color color)360 ColorSlider::SetMarkerToColor(rgb_color color)
361 {
362 float h = 0;
363 float s = 0;
364 float v = 0;
365 if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) {
366 RGB_to_HSV(
367 (float)color.red / 255.0,
368 (float)color.green / 255.0,
369 (float)color.blue / 255.0,
370 h, s, v
371 );
372 }
373
374 switch (fMode) {
375 case R_SELECTED:
376 SetValue(255 - color.red);
377 break;
378
379 case G_SELECTED:
380 SetValue(255 - color.green);
381 break;
382
383 case B_SELECTED:
384 SetValue(255 - color.blue);
385 break;
386
387 case H_SELECTED:
388 SetValue(255.0 - round(h / 6.0 * 255.0));
389 break;
390
391 case S_SELECTED:
392 SetValue(255.0 - round(s * 255.0));
393 break;
394
395 case V_SELECTED:
396 SetValue(255.0 - round(v * 255.0));
397 break;
398 }
399 }
400
401
402 // #pragma mark - private
403
404
405 void
_Init(SelectedColorMode mode,float value1,float value2,orientation dir,border_style border)406 ColorSlider::_Init(SelectedColorMode mode, float value1, float value2,
407 orientation dir, border_style border)
408 {
409 fMode = mode;
410 fFixedValue1 = value1;
411 fFixedValue2 = value2;
412 fMouseDown = false;
413 fBitmap = NULL;
414 fBitmapDirty = true;
415 fOrientation = dir;
416 fBorderStyle = border;
417
418 SetViewColor(B_TRANSPARENT_COLOR);
419 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
420 }
421
422
423 void
_AllocBitmap(int32 width,int32 height)424 ColorSlider::_AllocBitmap(int32 width, int32 height)
425 {
426 if (width < 2 || height < 2)
427 return;
428
429 delete fBitmap;
430 fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32);
431
432 fBitmapDirty = true;
433 }
434
435
436 void
_Update()437 ColorSlider::_Update()
438 {
439 fBitmapDirty = true;
440 Invalidate();
441 }
442
443
444 BRect
_BitmapRect() const445 ColorSlider::_BitmapRect() const
446 {
447 BRect r = Bounds();
448 if (fOrientation == B_VERTICAL)
449 r.InsetBy(7, 3);
450 if (fBorderStyle == B_FANCY_BORDER)
451 r.InsetBy(2, 2);
452 return r;
453 }
454
455
456 void
_FillBitmap(BBitmap * bitmap,SelectedColorMode mode,float fixedValue1,float fixedValue2,orientation orient) const457 ColorSlider::_FillBitmap(BBitmap* bitmap, SelectedColorMode mode,
458 float fixedValue1, float fixedValue2, orientation orient) const
459 {
460 int32 width = bitmap->Bounds().IntegerWidth();
461 int32 height = bitmap->Bounds().IntegerHeight();
462 uint32 bpr = bitmap->BytesPerRow();
463
464 uint8* bits = (uint8*)bitmap->Bits();
465
466 float r = 0;
467 float g = 0;
468 float b = 0;
469 float h;
470 float s;
471 float v;
472
473 switch (mode) {
474 case R_SELECTED:
475 g = round(fixedValue1 * 255);
476 b = round(fixedValue2 * 255);
477 if (orient == B_VERTICAL) {
478 for (int y = 0; y <= height; y++) {
479 r = 255 - y * 255 / height;
480 _DrawColorLineY(bits, width, r, g, b);
481 bits += bpr;
482 }
483 } else {
484 for (int x = 0; x <= width; x++) {
485 r = x * 255 / width;
486 _DrawColorLineX(bits, height, bpr, r, g, b);
487 bits += 4;
488 }
489 }
490 break;
491
492 case G_SELECTED:
493 r = round(fixedValue1 * 255);
494 b = round(fixedValue2 * 255);
495 if (orient == B_VERTICAL) {
496 for (int y = 0; y <= height; y++) {
497 g = 255 - y * 255 / height;
498 _DrawColorLineY(bits, width, r, g, b);
499 bits += bpr;
500 }
501 } else {
502 for (int x = 0; x <= width; x++) {
503 g = 255 - x * 255 / width;
504 _DrawColorLineX(bits, height, bpr, r, g, b);
505 bits += 4;
506 }
507 }
508 break;
509
510 case B_SELECTED:
511 r = round(fixedValue1 * 255);
512 g = round(fixedValue2 * 255);
513 if (orient == B_VERTICAL) {
514 for (int y = 0; y <= height; y++) {
515 b = 255 - y * 255 / height;
516 _DrawColorLineY(bits, width, r, g, b);
517 bits += bpr;
518 }
519 } else {
520 for (int x = 0; x <= width; x++) {
521 b = x * 255 / width;
522 _DrawColorLineX(bits, height, bpr, r, g, b);
523 bits += 4;
524 }
525 }
526
527 case H_SELECTED:
528 s = 1.0;//fixedValue1;
529 v = 1.0;//fixedValue2;
530 if (orient == B_VERTICAL) {
531 for (int y = 0; y <= height; y++) {
532 HSV_to_RGB(6.0 - (float)y * 6.0 / height, s, v, r, g, b);
533 _DrawColorLineY(bits, width, r * 255, g * 255, b * 255);
534 bits += bpr;
535 }
536 } else {
537 for (int x = 0; x <= width; x++) {
538 HSV_to_RGB((float)x * 6.0 / width, s, v, r, g, b);
539 _DrawColorLineX(bits, height, bpr,
540 r * 255, g * 255, b * 255);
541 bits += 4;
542 }
543 }
544 break;
545
546 case S_SELECTED:
547 h = fixedValue1;
548 v = 1.0;//fixedValue2;
549 if (orient == B_VERTICAL) {
550 for (int y = 0; y <= height; y++) {
551 HSV_to_RGB(h, 1.0 - (float)y / height, v, r, g, b);
552 _DrawColorLineY(bits, width, r * 255, g * 255, b * 255);
553 bits += bpr;
554 }
555 } else {
556 for (int x = 0; x <= width; x++) {
557 HSV_to_RGB(h, 1.0 - (float)x / width, v, r, g, b);
558 _DrawColorLineX(bits, height, bpr,
559 r * 255, g * 255, b * 255);
560 bits += 4;
561 }
562 }
563 break;
564
565 case V_SELECTED:
566 h = fixedValue1;
567 s = 1.0;//fixedValue2;
568 if (orient == B_VERTICAL) {
569 for (int y = 0; y <= height; y++) {
570 HSV_to_RGB(h, s, 1.0 - (float)y / height, r, g, b);
571 _DrawColorLineY(bits, width, r * 255, g * 255, b * 255);
572 bits += bpr;
573 }
574 } else {
575 for (int x = 0; x <= width; x++) {
576 HSV_to_RGB(h, s, (float)x / width, r, g, b);
577 _DrawColorLineX(bits, height, bpr,
578 r * 255, g * 255, b * 255);
579 bits += 4;
580 }
581 }
582 break;
583 }
584 }
585
586
587 void
_DrawColorLineY(uint8 * bits,int width,int r,int g,int b)588 ColorSlider::_DrawColorLineY(uint8* bits, int width, int r, int g, int b)
589 {
590 for (int x = 0; x <= width; x++) {
591 bits[0] = b;
592 bits[1] = g;
593 bits[2] = r;
594 bits[3] = 255;
595 bits += 4;
596 }
597 }
598
599
600 void
_DrawColorLineX(uint8 * bits,int height,int bpr,int r,int g,int b)601 ColorSlider::_DrawColorLineX(uint8* bits, int height, int bpr,
602 int r, int g, int b)
603 {
604 for (int y = 0; y <= height; y++) {
605 bits[0] = b;
606 bits[1] = g;
607 bits[2] = r;
608 bits[3] = 255;
609 bits += bpr;
610 }
611 }
612
613
614 void
_DrawTriangle(BPoint point1,BPoint point2,BPoint point3)615 ColorSlider::_DrawTriangle(BPoint point1, BPoint point2, BPoint point3)
616 {
617 StrokeLine(point1, point2);
618 StrokeLine(point3);
619 StrokeLine(point1);
620 }
621
622
623 void
_TrackMouse(BPoint where)624 ColorSlider::_TrackMouse(BPoint where)
625 {
626 BRect b(_BitmapRect());
627
628 if (fOrientation == B_VERTICAL)
629 SetValue((int)(((where.y - b.top) / b.Height()) * 255.0));
630 else
631 SetValue(255 - (int)(((where.x - b.left) / b.Width()) * 255.0));
632
633 Invoke();
634 }
635