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