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