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