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