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