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