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