1/*
2 * Copyright 2001-2013 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Alexandre Deckner, alex@zappotek.com
7 *		Axel D��rfler, axeld@pinc-software.de
8 *		J��r��me Duval
9 *		Marc Flerackers, mflerackers@androme.be
10 *		John Scipione, jscipione@gmail.com
11 */
12
13/**	BColorControl displays a palette of selectable colors. */
14
15#include <ColorControl.h>
16
17#include <algorithm>
18
19#include <stdio.h>
20#include <stdlib.h>
21
22#include <iostream>
23
24#include <ControlLook.h>
25#include <Bitmap.h>
26#include <TextControl.h>
27#include <Region.h>
28#include <Screen.h>
29#include <SystemCatalog.h>
30#include <Window.h>
31
32using BPrivate::gSystemCatalog;
33
34#include <binary_compatibility/Interface.h>
35
36
37#undef B_TRANSLATION_CONTEXT
38#define B_TRANSLATION_CONTEXT "ColorControl"
39
40static const uint32 kMsgColorEntered = 'ccol';
41static const float kMinCellSize = 6.0f;
42static const float kSelectorPenSize = 2.0f;
43static const float kSelectorSize = 4.0f;
44static const float kSelectorHSpacing = 2.0f;
45static const float kTextFieldsHSpacing = 6.0f;
46static const float kDefaultFontSize = 12.0f;
47static const float kBevelSpacing = 2.0f;
48static const uint32 kRampCount = 4;
49
50
51BColorControl::BColorControl(BPoint leftTop, color_control_layout layout,
52	float cellSize, const char* name, BMessage* message, bool useOffscreen)
53	:
54	BControl(BRect(leftTop, leftTop), name, NULL, message,
55		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE),
56	fRedText(NULL),
57	fGreenText(NULL),
58	fBlueText(NULL),
59	fOffscreenBitmap(NULL)
60{
61	_InitData(layout, cellSize, useOffscreen, NULL);
62}
63
64
65BColorControl::BColorControl(BMessage* data)
66	:
67	BControl(data),
68	fRedText(NULL),
69	fGreenText(NULL),
70	fBlueText(NULL),
71	fOffscreenBitmap(NULL)
72{
73	int32 layout;
74	float cellSize;
75	bool useOffscreen;
76
77	data->FindInt32("_layout", &layout);
78	data->FindFloat("_csize", &cellSize);
79	data->FindBool("_use_off", &useOffscreen);
80
81	_InitData((color_control_layout)layout, cellSize, useOffscreen, data);
82}
83
84
85BColorControl::~BColorControl()
86{
87	delete fOffscreenBitmap;
88}
89
90
91void
92BColorControl::_InitData(color_control_layout layout, float size,
93	bool useOffscreen, BMessage* data)
94{
95	fPaletteMode = BScreen(B_MAIN_SCREEN_ID).ColorSpace() == B_CMAP8;
96		//TODO: we don't support workspace and colorspace changing for now
97		//		so we take the main_screen colorspace at startup
98	fColumns = layout;
99	fRows = 256 / fColumns;
100
101	_SetCellSize(size);
102
103	fSelectedPaletteColorIndex = -1;
104	fPreviousSelectedPaletteColorIndex = -1;
105	fFocusedRamp = !fPaletteMode && IsFocus() ? 1 : -1;
106	fClickedRamp = -1;
107
108	const char* red = B_TRANSLATE_MARK("Red:");
109	const char* green = B_TRANSLATE_MARK("Green:");
110	const char* blue = B_TRANSLATE_MARK("Blue:");
111	red = gSystemCatalog.GetString(red, "ColorControl");
112	green = gSystemCatalog.GetString(green, "ColorControl");
113	blue = gSystemCatalog.GetString(blue, "ColorControl");
114
115	if (data != NULL) {
116		fRedText = (BTextControl*)FindView("_red");
117		fGreenText = (BTextControl*)FindView("_green");
118		fBlueText = (BTextControl*)FindView("_blue");
119
120		int32 value = 0;
121		data->FindInt32("_val", &value);
122
123		SetValue(value);
124	} else {
125		BRect textRect(0.0f, 0.0f, 0.0f, 0.0f);
126		float labelWidth = std::max(StringWidth(red),
127			std::max(StringWidth(green), StringWidth(blue)))
128				+ kTextFieldsHSpacing;
129		textRect.right = labelWidth + StringWidth("999999");
130			// enough room for 3 digits plus 3 digits of padding
131		font_height fontHeight;
132		GetFontHeight(&fontHeight);
133		float labelHeight = fontHeight.ascent + fontHeight.descent;
134		textRect.bottom = labelHeight;
135
136		// red
137
138		fRedText = new BTextControl(textRect, "_red", red, "0",
139			new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP,
140			B_WILL_DRAW | B_NAVIGABLE);
141		fRedText->SetDivider(labelWidth);
142
143		for (int32 i = 0; i < 256; i++)
144			fRedText->TextView()->DisallowChar(i);
145		for (int32 i = '0'; i <= '9'; i++)
146			fRedText->TextView()->AllowChar(i);
147		fRedText->TextView()->SetMaxBytes(3);
148
149		// green
150
151		textRect.OffsetBy(0, _TextRectOffset());
152		fGreenText = new BTextControl(textRect, "_green", green, "0",
153			new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP,
154			B_WILL_DRAW | B_NAVIGABLE);
155		fGreenText->SetDivider(labelWidth);
156
157		for (int32 i = 0; i < 256; i++)
158			fGreenText->TextView()->DisallowChar(i);
159		for (int32 i = '0'; i <= '9'; i++)
160			fGreenText->TextView()->AllowChar(i);
161		fGreenText->TextView()->SetMaxBytes(3);
162
163		// blue
164
165		textRect.OffsetBy(0, _TextRectOffset());
166		fBlueText = new BTextControl(textRect, "_blue", blue, "0",
167			new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP,
168			B_WILL_DRAW | B_NAVIGABLE);
169		fBlueText->SetDivider(labelWidth);
170
171		for (int32 i = 0; i < 256; i++)
172			fBlueText->TextView()->DisallowChar(i);
173		for (int32 i = '0'; i <= '9'; i++)
174			fBlueText->TextView()->AllowChar(i);
175		fBlueText->TextView()->SetMaxBytes(3);
176
177		AddChild(fRedText);
178		AddChild(fGreenText);
179		AddChild(fBlueText);
180	}
181
182	ResizeToPreferred();
183
184	if (useOffscreen) {
185		if (fOffscreenBitmap != NULL) {
186			BRect bounds = _PaletteFrame();
187			fOffscreenBitmap = new BBitmap(bounds, B_RGB32, true, false);
188			BView* offscreenView = new BView(bounds, "off_view", 0, 0);
189
190			fOffscreenBitmap->Lock();
191			fOffscreenBitmap->AddChild(offscreenView);
192			fOffscreenBitmap->Unlock();
193		}
194	} else {
195		delete fOffscreenBitmap;
196		fOffscreenBitmap = NULL;
197	}
198}
199
200
201void
202BColorControl::_LayoutView()
203{
204	fPaletteFrame.Set(0, 0, fColumns * fCellSize, fRows * fCellSize);
205	fPaletteFrame.OffsetBy(kBevelSpacing, kBevelSpacing);
206	if (!fPaletteMode) {
207		// Reduce the inner space by 1 pixel so that the frame
208		// is exactly rows * cellsize pixels in height
209		fPaletteFrame.bottom -= 1;
210	}
211
212	float rampHeight = (float)(fRows * fCellSize / kRampCount);
213	float offset = _TextRectOffset();
214	float y = 0;
215	if (rampHeight > fRedText->Frame().Height()) {
216		// there is enough room to fit kRampCount labels,
217		// shift text controls down by one ramp
218		offset = rampHeight;
219		y = floorf(offset + (offset - fRedText->Frame().Height()) / 2);
220	}
221
222	BRect rect = _PaletteFrame();
223	fRedText->MoveTo(rect.right + kTextFieldsHSpacing, y);
224
225	y += offset;
226	fGreenText->MoveTo(rect.right + kTextFieldsHSpacing, y);
227
228	y += offset;
229	fBlueText->MoveTo(rect.right + kTextFieldsHSpacing, y);
230}
231
232
233BArchivable*
234BColorControl::Instantiate(BMessage* data)
235{
236	if (validate_instantiation(data, "BColorControl"))
237		return new BColorControl(data);
238
239	return NULL;
240}
241
242
243status_t
244BColorControl::Archive(BMessage* data, bool deep) const
245{
246	status_t status = BControl::Archive(data, deep);
247
248	if (status == B_OK)
249		status = data->AddInt32("_layout", Layout());
250
251	if (status == B_OK)
252		status = data->AddFloat("_csize", fCellSize);
253
254	if (status == B_OK)
255		status = data->AddBool("_use_off", fOffscreenBitmap != NULL);
256
257	return status;
258}
259
260
261void
262BColorControl::SetLayout(BLayout* layout)
263{
264	// We need to implement this method, since we have another SetLayout()
265	// method and C++ has this special method hiding "feature".
266	BControl::SetLayout(layout);
267}
268
269
270void
271BColorControl::SetValue(int32 value)
272{
273	rgb_color c1 = ValueAsColor();
274	rgb_color c2;
275	c2.red = (value & 0xFF000000) >> 24;
276	c2.green = (value & 0x00FF0000) >> 16;
277	c2.blue = (value & 0x0000FF00) >> 8;
278	c2.alpha = 255;
279
280	if (fPaletteMode) {
281		//workaround when two indexes have the same color
282		rgb_color c
283			= BScreen(Window()).ColorForIndex(fSelectedPaletteColorIndex);
284		c.alpha = 255;
285		if (fSelectedPaletteColorIndex == -1 || c != c2) {
286				//here SetValue hasn't been called by mouse tracking
287			fSelectedPaletteColorIndex = BScreen(Window()).IndexForColor(c2);
288		}
289
290		c2 = BScreen(Window()).ColorForIndex(fSelectedPaletteColorIndex);
291
292		Invalidate(_PaletteSelectorFrame(fPreviousSelectedPaletteColorIndex));
293		Invalidate(_PaletteSelectorFrame(fSelectedPaletteColorIndex));
294
295		fPreviousSelectedPaletteColorIndex = fSelectedPaletteColorIndex;
296	} else if (c1 != c2)
297		Invalidate();
298
299	// Set the value here, since BTextControl will trigger
300	// Window()->UpdateIfNeeded() which will cause us to draw the indicators
301	// at the old offset.
302	if (Value() != value)
303		BControl::SetValueNoUpdate(value);
304
305	// the textcontrols have to be updated even when the color
306	// hasn't changed since the value is clamped upstream
307	// and the textcontrols would still show the unclamped value
308	char string[4];
309	sprintf(string, "%d", c2.red);
310	fRedText->SetText(string);
311	sprintf(string, "%d", c2.green);
312	fGreenText->SetText(string);
313	sprintf(string, "%d", c2.blue);
314	fBlueText->SetText(string);
315}
316
317
318rgb_color
319BColorControl::ValueAsColor()
320{
321	int32 value = Value();
322	rgb_color color;
323
324	color.red = (value & 0xFF000000) >> 24;
325	color.green = (value & 0x00FF0000) >> 16;
326	color.blue = (value & 0x0000FF00) >> 8;
327	color.alpha = 255;
328
329	return color;
330}
331
332
333void
334BColorControl::SetEnabled(bool enabled)
335{
336	BControl::SetEnabled(enabled);
337
338	fRedText->SetEnabled(enabled);
339	fGreenText->SetEnabled(enabled);
340	fBlueText->SetEnabled(enabled);
341}
342
343
344void
345BColorControl::AttachedToWindow()
346{
347	BControl::AttachedToWindow();
348
349	AdoptParentColors();
350
351	fRedText->SetTarget(this);
352	fGreenText->SetTarget(this);
353	fBlueText->SetTarget(this);
354
355	if (fOffscreenBitmap != NULL)
356		_InitOffscreen();
357}
358
359
360void
361BColorControl::MessageReceived(BMessage* message)
362{
363	switch (message->what) {
364		case kMsgColorEntered:
365		{
366			rgb_color color;
367			color.red = min_c(strtol(fRedText->Text(), NULL, 10), 255);
368			color.green = min_c(strtol(fGreenText->Text(), NULL, 10), 255);
369			color.blue = min_c(strtol(fBlueText->Text(), NULL, 10), 255);
370			color.alpha = 255;
371
372			SetValue(color);
373			Invoke();
374			break;
375		}
376
377		case B_SCREEN_CHANGED:
378		{
379			BRect frame;
380			uint32 mode;
381			if (message->FindRect("frame", &frame) == B_OK
382				&& message->FindInt32("mode", (int32*)&mode) == B_OK) {
383				if ((fPaletteMode && mode == B_CMAP8)
384					|| (!fPaletteMode && mode != B_CMAP8)) {
385					// not switching to or from B_CMAP8, break
386					break;
387				}
388
389				// fake an archive message (so we don't rebuild views)
390				BMessage* data = new BMessage();
391				data->AddInt32("_val", Value());
392
393				// reinititialize
394				bool useOffscreen = fOffscreenBitmap != NULL;
395				_InitData((color_control_layout)fColumns, fCellSize,
396					useOffscreen, data);
397				if (useOffscreen)
398					_InitOffscreen();
399
400				// cleanup
401				delete data;
402			}
403			break;
404		}
405
406		default:
407			BControl::MessageReceived(message);
408	}
409}
410
411
412void
413BColorControl::Draw(BRect updateRect)
414{
415	if (fOffscreenBitmap != NULL)
416		DrawBitmap(fOffscreenBitmap, B_ORIGIN);
417	else
418		_DrawColorArea(this, updateRect);
419
420	_DrawSelectors(this);
421}
422
423
424void
425BColorControl::_DrawColorArea(BView* target, BRect updateRect)
426{
427	BRect rect = _PaletteFrame();
428	bool enabled = IsEnabled();
429
430	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
431	rgb_color darken1 = tint_color(base, B_DARKEN_1_TINT);
432
433	uint32 flags = be_control_look->Flags(this);
434	be_control_look->DrawTextControlBorder(target, rect, updateRect,
435		base, flags);
436
437	if (fPaletteMode) {
438		int colBegin = max_c(0, -1 + int(updateRect.left) / int(fCellSize));
439		int colEnd = min_c(fColumns,
440			2 + int(updateRect.right) / int(fCellSize));
441		int rowBegin = max_c(0, -1 + int(updateRect.top) / int(fCellSize));
442		int rowEnd = min_c(fRows, 2 + int(updateRect.bottom)
443			/ int(fCellSize));
444
445		// grid
446		target->SetHighColor(enabled ? darken1 : base);
447
448		for (int xi = 0; xi < fColumns + 1; xi++) {
449			float x = fPaletteFrame.left + float(xi) * fCellSize;
450			target->StrokeLine(BPoint(x, fPaletteFrame.top),
451				BPoint(x, fPaletteFrame.bottom));
452		}
453		for (int yi = 0; yi < fRows + 1; yi++) {
454			float y = fPaletteFrame.top + float(yi) * fCellSize;
455			target->StrokeLine(BPoint(fPaletteFrame.left, y),
456				BPoint(fPaletteFrame.right, y));
457		}
458
459		// colors
460		for (int col = colBegin; col < colEnd; col++) {
461			for (int row = rowBegin; row < rowEnd; row++) {
462				uint8 colorIndex = row * fColumns + col;
463				float x = fPaletteFrame.left + col * fCellSize;
464				float y = fPaletteFrame.top + row * fCellSize;
465
466				target->SetHighColor(system_colors()->color_list[colorIndex]);
467				target->FillRect(BRect(x + 1, y + 1,
468					x + fCellSize - 1, y + fCellSize - 1));
469			}
470		}
471	} else {
472		rgb_color white = { 255, 255, 255, 255 };
473		rgb_color red   = { 255, 0, 0, 255 };
474		rgb_color green = { 0, 255, 0, 255 };
475		rgb_color blue  = { 0, 0, 255, 255 };
476
477		rgb_color compColor = { 0, 0, 0, 255 };
478		if (!enabled) {
479			compColor.red = compColor.green = compColor.blue = 156;
480			red.red = green.green = blue.blue = 70;
481			white.red = white.green = white.blue = 70;
482		}
483		_DrawColorRamp(_RampFrame(0), target, white, compColor, 0, false,
484			updateRect);
485		_DrawColorRamp(_RampFrame(1), target, red, compColor, 0, false,
486			updateRect);
487		_DrawColorRamp(_RampFrame(2), target, green, compColor, 0, false,
488			updateRect);
489		_DrawColorRamp(_RampFrame(3), target, blue, compColor, 0, false,
490			updateRect);
491	}
492}
493
494
495void
496BColorControl::_DrawSelectors(BView* target)
497{
498	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
499	rgb_color lightenmax = tint_color(base, B_LIGHTEN_MAX_TINT);
500
501	if (fPaletteMode) {
502		if (fSelectedPaletteColorIndex != -1) {
503			target->SetHighColor(lightenmax);
504			target->StrokeRect(
505				_PaletteSelectorFrame(fSelectedPaletteColorIndex));
506		}
507	} else {
508		rgb_color color = ValueAsColor();
509		target->SetHighColor(255, 255, 255);
510		target->SetLowColor(0, 0, 0);
511
512		int components[4] = { color.alpha, color.red, color.green, color.blue };
513
514		for (int i = 1; i < 4; i++) {
515			BPoint center = _SelectorPosition(_RampFrame(i), components[i]);
516
517			target->SetPenSize(kSelectorPenSize);
518			target->StrokeEllipse(center, kSelectorSize / 2, kSelectorSize / 2);
519			target->SetPenSize(kSelectorPenSize / 2);
520			target->StrokeEllipse(center, kSelectorSize, kSelectorSize,
521				B_SOLID_LOW);
522			if (i == fFocusedRamp) {
523				target->StrokeEllipse(center,
524					kSelectorSize / 2, kSelectorSize / 2, B_SOLID_LOW);
525			}
526		}
527
528		target->SetPenSize(1.0f);
529	}
530}
531
532
533void
534BColorControl::_DrawColorRamp(BRect rect, BView* target,
535	rgb_color baseColor, rgb_color compColor, int16 flag, bool focused,
536	BRect updateRect)
537{
538	float width = rect.Width() + 1;
539	rgb_color color = ValueAsColor();
540	color.alpha = 255;
541
542	updateRect = updateRect & rect;
543
544	if (updateRect.IsValid() && updateRect.Width() >= 0) {
545		target->BeginLineArray((int32)updateRect.Width() + 1);
546
547		for (float i = (updateRect.left - rect.left);
548				i <= (updateRect.right - rect.left) + 1; i++) {
549			if (baseColor.red == 255)
550				color.red = (uint8)(i * 255 / width) + compColor.red;
551			if (baseColor.green == 255)
552				color.green = (uint8)(i * 255 / width) + compColor.green;
553			if (baseColor.blue == 255)
554				color.blue = (uint8)(i * 255 / width) + compColor.blue;
555
556			target->AddLine(BPoint(rect.left + i, rect.top),
557				BPoint(rect.left + i, rect.bottom - 1), color);
558		}
559
560		target->EndLineArray();
561	}
562}
563
564
565BPoint
566BColorControl::_SelectorPosition(const BRect& rampRect, uint8 shade) const
567{
568	float radius = kSelectorSize / 2 + kSelectorPenSize / 2;
569
570	return BPoint(rampRect.left + kSelectorHSpacing + radius +
571		shade * (rampRect.Width() - 2 * (kSelectorHSpacing + radius)) / 255,
572		rampRect.top + rampRect.Height() / 2);
573}
574
575
576BRect
577BColorControl::_PaletteFrame() const
578{
579	return fPaletteFrame.InsetByCopy(-kBevelSpacing, -kBevelSpacing);
580}
581
582
583BRect
584BColorControl::_RampFrame(uint8 rampIndex) const
585{
586	float rampHeight = (float)(fRows * fCellSize / kRampCount);
587
588	return BRect(fPaletteFrame.left,
589		fPaletteFrame.top + float(rampIndex) * rampHeight,
590		fPaletteFrame.right,
591		fPaletteFrame.top + float(rampIndex + 1) * rampHeight);
592}
593
594
595void
596BColorControl::_SetCellSize(float size)
597{
598	BFont font;
599	GetFont(&font);
600	fCellSize = std::max(kMinCellSize,
601		ceilf(size * font.Size() / kDefaultFontSize));
602}
603
604
605float
606BColorControl::_TextRectOffset()
607{
608	return std::max(fRedText->Bounds().Height(),
609		ceilf(_PaletteFrame().Height() / 3));
610}
611
612
613BRect
614BColorControl::_PaletteSelectorFrame(uint8 colorIndex) const
615{
616	uint32 row = colorIndex / fColumns;
617	uint32 column = colorIndex % fColumns;
618	float x = fPaletteFrame.left + column * fCellSize;
619	float y = fPaletteFrame.top + row * fCellSize;
620	return BRect(x, y, x + fCellSize, y + fCellSize);
621}
622
623
624void
625BColorControl::_InitOffscreen()
626{
627	if (fOffscreenBitmap->Lock()) {
628		BView* offscreenView = fOffscreenBitmap->ChildAt((int32)0);
629		if (offscreenView != NULL) {
630			_DrawColorArea(offscreenView, _PaletteFrame());
631			offscreenView->Sync();
632		}
633		fOffscreenBitmap->Unlock();
634	}
635}
636
637
638void
639BColorControl::_InvalidateSelector(int16 ramp, rgb_color color, bool focused)
640{
641	if (fPaletteMode)
642		return;
643
644	if (ramp < 1 || ramp > 3)
645		return;
646
647	float invalidateRadius = focused
648		? kSelectorSize + kSelectorPenSize / 2
649		: kSelectorSize / 2 + kSelectorPenSize;
650
651	uint8 colorValue = ramp == 1 ? color.red : ramp == 2 ? color.green
652		: color.blue;
653
654	BPoint pos = _SelectorPosition(_RampFrame(ramp), colorValue);
655	Invalidate(BRect(pos.x - invalidateRadius, pos.y - invalidateRadius,
656		pos.x + invalidateRadius, pos.y + invalidateRadius));
657}
658
659
660void
661BColorControl::SetCellSize(float size)
662{
663	_SetCellSize(size);
664	ResizeToPreferred();
665}
666
667
668float
669BColorControl::CellSize() const
670{
671	return fCellSize;
672}
673
674
675void
676BColorControl::SetLayout(color_control_layout layout)
677{
678	switch (layout) {
679		case B_CELLS_4x64:
680			fColumns = 4;
681			fRows = 64;
682			break;
683
684		case B_CELLS_8x32:
685			fColumns = 8;
686			fRows = 32;
687			break;
688
689		case B_CELLS_16x16:
690			fColumns = 16;
691			fRows = 16;
692			break;
693
694		case B_CELLS_32x8:
695			fColumns = 32;
696			fRows = 8;
697			break;
698
699		case B_CELLS_64x4:
700			fColumns = 64;
701			fRows = 4;
702			break;
703	}
704
705	ResizeToPreferred();
706	Invalidate();
707}
708
709
710color_control_layout
711BColorControl::Layout() const
712{
713	if (fColumns == 4 && fRows == 64)
714		return B_CELLS_4x64;
715
716	if (fColumns == 8 && fRows == 32)
717		return B_CELLS_8x32;
718
719	if (fColumns == 16 && fRows == 16)
720		return B_CELLS_16x16;
721
722	if (fColumns == 32 && fRows == 8)
723		return B_CELLS_32x8;
724
725	if (fColumns == 64 && fRows == 4)
726		return B_CELLS_64x4;
727
728	return B_CELLS_32x8;
729}
730
731
732void
733BColorControl::WindowActivated(bool state)
734{
735	BControl::WindowActivated(state);
736}
737
738
739void
740BColorControl::KeyDown(const char* bytes, int32 numBytes)
741{
742	if (IsFocus() && !fPaletteMode && numBytes == 1) {
743		rgb_color color = ValueAsColor();
744
745		switch (bytes[0]) {
746			case B_UP_ARROW:
747			{
748				int16 oldFocus = fFocusedRamp;
749				fFocusedRamp--;
750				if (fFocusedRamp < 1)
751					fFocusedRamp = 3;
752
753				_InvalidateSelector(oldFocus, color, true);
754				_InvalidateSelector(fFocusedRamp, color, true);
755				break;
756			}
757
758			case B_DOWN_ARROW:
759			{
760				int16 oldFocus = fFocusedRamp;
761				fFocusedRamp++;
762				if (fFocusedRamp > 3)
763					fFocusedRamp = 1;
764
765				_InvalidateSelector(oldFocus, color, true);
766				_InvalidateSelector(fFocusedRamp, color, true);
767				break;
768			}
769
770			case B_LEFT_ARROW:
771			{
772				bool goFaster = false;
773				if (Window() != NULL) {
774					BMessage* message = Window()->CurrentMessage();
775					if (message != NULL && message->what == B_KEY_DOWN) {
776						int32 repeats = 0;
777						if (message->FindInt32("be:key_repeat", &repeats)
778								== B_OK && repeats > 4) {
779							goFaster = true;
780						}
781					}
782				}
783
784				if (fFocusedRamp == 1) {
785					if (goFaster && color.red >= 5)
786						color.red -= 5;
787					else if (color.red > 0)
788						color.red--;
789				} else if (fFocusedRamp == 2) {
790					if (goFaster && color.green >= 5)
791						color.green -= 5;
792					else if (color.green > 0)
793						color.green--;
794				} else if (fFocusedRamp == 3) {
795				 	if (goFaster && color.blue >= 5)
796						color.blue -= 5;
797					else if (color.blue > 0)
798						color.blue--;
799				}
800
801				SetValue(color);
802				Invoke();
803				break;
804			}
805
806			case B_RIGHT_ARROW:
807			{
808				bool goFaster = false;
809				if (Window() != NULL) {
810					BMessage* message = Window()->CurrentMessage();
811					if (message != NULL && message->what == B_KEY_DOWN) {
812						int32 repeats = 0;
813						if (message->FindInt32("be:key_repeat", &repeats)
814								== B_OK && repeats > 4) {
815							goFaster = true;
816						}
817					}
818				}
819
820				if (fFocusedRamp == 1) {
821					if (goFaster && color.red <= 250)
822						color.red += 5;
823					else if (color.red < 255)
824						color.red++;
825				} else if (fFocusedRamp == 2) {
826					if (goFaster && color.green <= 250)
827						color.green += 5;
828					else if (color.green < 255)
829						color.green++;
830				} else if (fFocusedRamp == 3) {
831				 	if (goFaster && color.blue <= 250)
832						color.blue += 5;
833					else if (color.blue < 255)
834						color.blue++;
835				}
836
837				SetValue(color);
838				Invoke();
839				break;
840			}
841		}
842	}
843
844	BControl::KeyDown(bytes, numBytes);
845}
846
847
848void
849BColorControl::MouseUp(BPoint point)
850{
851	fClickedRamp = -1;
852	SetTracking(false);
853}
854
855
856void
857BColorControl::MouseDown(BPoint point)
858{
859	if (!IsEnabled())
860		return;
861	if (!fPaletteFrame.Contains(point))
862		return;
863
864	if (fPaletteMode) {
865		int col = (int)((point.x - fPaletteFrame.left) / fCellSize);
866		int row = (int)((point.y - fPaletteFrame.top) / fCellSize);
867		int colorIndex = row * fColumns + col;
868		if (colorIndex >= 0 && colorIndex < 256) {
869			fSelectedPaletteColorIndex = colorIndex;
870			SetValue(system_colors()->color_list[colorIndex]);
871		}
872	} else {
873		rgb_color color = ValueAsColor();
874
875		uint8 shade = (unsigned char)max_c(0,
876			min_c((point.x - _RampFrame(0).left) * 255
877				/ _RampFrame(0).Width(), 255));
878
879		if (_RampFrame(0).Contains(point)) {
880			color.red = color.green = color.blue = shade;
881			fClickedRamp = 0;
882		} else if (_RampFrame(1).Contains(point)) {
883			color.red = shade;
884			fClickedRamp = 1;
885		} else if (_RampFrame(2).Contains(point)) {
886			color.green = shade;
887			fClickedRamp = 2;
888		} else if (_RampFrame(3).Contains(point)) {
889			color.blue = shade;
890			fClickedRamp = 3;
891		}
892
893		SetValue(color);
894	}
895
896	Invoke();
897
898	SetTracking(true);
899	SetMouseEventMask(B_POINTER_EVENTS,
900		B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
901}
902
903
904void
905BColorControl::MouseMoved(BPoint point, uint32 transit,
906	const BMessage* message)
907{
908	if (!IsTracking())
909		return;
910
911	if (fPaletteMode && fPaletteFrame.Contains(point)) {
912		int col = (int)((point.x - fPaletteFrame.left) / fCellSize);
913		int row = (int)((point.y - fPaletteFrame.top) / fCellSize);
914		int colorIndex = row * fColumns + col;
915		if (colorIndex >= 0 && colorIndex < 256) {
916			fSelectedPaletteColorIndex = colorIndex;
917			SetValue(system_colors()->color_list[colorIndex]);
918		}
919	} else {
920		if (fClickedRamp < 0 || fClickedRamp > 3)
921			return;
922
923		rgb_color color = ValueAsColor();
924
925		uint8 shade = (unsigned char)max_c(0,
926			min_c((point.x - _RampFrame(0).left) * 255
927				/ _RampFrame(0).Width(), 255));
928
929		if (fClickedRamp == 0)
930			color.red = color.green = color.blue = shade;
931		else if (fClickedRamp == 1)
932			color.red = shade;
933		else if (fClickedRamp == 2)
934			color.green = shade;
935		else if (fClickedRamp == 3)
936			color.blue = shade;
937
938		SetValue(color);
939	}
940
941	Invoke();
942}
943
944
945void
946BColorControl::DetachedFromWindow()
947{
948	BControl::DetachedFromWindow();
949}
950
951
952void
953BColorControl::GetPreferredSize(float* _width, float* _height)
954{
955	BRect rect = _PaletteFrame();
956
957	if (rect.Height() < fBlueText->Frame().bottom) {
958		// adjust the height to fit
959		rect.bottom = fBlueText->Frame().bottom;
960	}
961
962	if (_width) {
963		*_width = rect.Width() + kTextFieldsHSpacing
964			+ fRedText->Bounds().Width();
965	}
966
967	if (_height)
968		*_height = rect.Height();
969}
970
971
972void
973BColorControl::ResizeToPreferred()
974{
975	_LayoutView();
976	BControl::ResizeToPreferred();
977}
978
979
980status_t
981BColorControl::Invoke(BMessage* message)
982{
983	return BControl::Invoke(message);
984}
985
986
987void
988BColorControl::FrameMoved(BPoint newPosition)
989{
990	BControl::FrameMoved(newPosition);
991}
992
993
994void
995BColorControl::FrameResized(float newWidth, float newHeight)
996{
997	BControl::FrameResized(newWidth, newHeight);
998}
999
1000
1001BHandler*
1002BColorControl::ResolveSpecifier(BMessage* message, int32 index,
1003	BMessage* specifier, int32 form, const char* property)
1004{
1005	return BControl::ResolveSpecifier(message, index, specifier, form,
1006		property);
1007}
1008
1009
1010status_t
1011BColorControl::GetSupportedSuites(BMessage* data)
1012{
1013	return BControl::GetSupportedSuites(data);
1014}
1015
1016
1017void
1018BColorControl::MakeFocus(bool focused)
1019{
1020	fFocusedRamp = !fPaletteMode && focused ? 1 : -1;
1021	BControl::MakeFocus(focused);
1022}
1023
1024
1025void
1026BColorControl::AllAttached()
1027{
1028	BControl::AllAttached();
1029}
1030
1031
1032void
1033BColorControl::AllDetached()
1034{
1035	BControl::AllDetached();
1036}
1037
1038
1039status_t
1040BColorControl::SetIcon(const BBitmap* icon, uint32 flags)
1041{
1042	return BControl::SetIcon(icon, flags);
1043}
1044
1045
1046status_t
1047BColorControl::Perform(perform_code code, void* _data)
1048{
1049	switch (code) {
1050		case PERFORM_CODE_MIN_SIZE:
1051			((perform_data_min_size*)_data)->return_value
1052				= BColorControl::MinSize();
1053			return B_OK;
1054
1055		case PERFORM_CODE_MAX_SIZE:
1056			((perform_data_max_size*)_data)->return_value
1057				= BColorControl::MaxSize();
1058			return B_OK;
1059
1060		case PERFORM_CODE_PREFERRED_SIZE:
1061			((perform_data_preferred_size*)_data)->return_value
1062				= BColorControl::PreferredSize();
1063			return B_OK;
1064
1065		case PERFORM_CODE_LAYOUT_ALIGNMENT:
1066			((perform_data_layout_alignment*)_data)->return_value
1067				= BColorControl::LayoutAlignment();
1068			return B_OK;
1069
1070		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
1071			((perform_data_has_height_for_width*)_data)->return_value
1072				= BColorControl::HasHeightForWidth();
1073			return B_OK;
1074
1075		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
1076		{
1077			perform_data_get_height_for_width* data
1078				= (perform_data_get_height_for_width*)_data;
1079			BColorControl::GetHeightForWidth(data->width, &data->min,
1080				&data->max, &data->preferred);
1081			return B_OK;
1082		}
1083
1084		case PERFORM_CODE_SET_LAYOUT:
1085		{
1086			perform_data_set_layout* data = (perform_data_set_layout*)_data;
1087			BColorControl::SetLayout(data->layout);
1088			return B_OK;
1089		}
1090
1091		case PERFORM_CODE_LAYOUT_INVALIDATED:
1092		{
1093			perform_data_layout_invalidated* data
1094				= (perform_data_layout_invalidated*)_data;
1095			BColorControl::LayoutInvalidated(data->descendants);
1096			return B_OK;
1097		}
1098
1099		case PERFORM_CODE_DO_LAYOUT:
1100		{
1101			BColorControl::DoLayout();
1102			return B_OK;
1103		}
1104
1105		case PERFORM_CODE_SET_ICON:
1106		{
1107			perform_data_set_icon* data = (perform_data_set_icon*)_data;
1108			return BColorControl::SetIcon(data->icon, data->flags);
1109		}
1110	}
1111
1112	return BControl::Perform(code, _data);
1113}
1114
1115
1116void BColorControl::_ReservedColorControl1() {}
1117void BColorControl::_ReservedColorControl2() {}
1118void BColorControl::_ReservedColorControl3() {}
1119void BColorControl::_ReservedColorControl4() {}
1120
1121
1122BColorControl &
1123BColorControl::operator=(const BColorControl &)
1124{
1125	return *this;
1126}
1127