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