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