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