xref: /haiku/src/kits/interface/ColorControl.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
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 <SystemCatalog.h>
27 #include <Window.h>
28 
29 using BPrivate::gSystemCatalog;
30 
31 #include <binary_compatibility/Interface.h>
32 
33 
34 #undef B_TRANSLATION_CONTEXT
35 #define B_TRANSLATION_CONTEXT "ColorControl"
36 
37 static const uint32 kMsgColorEntered = 'ccol';
38 static const uint32 kMinCellSize = 6;
39 static const float kSelectorPenSize = 2.0f;
40 static const float kSelectorSize = 4.0f;
41 static const float kSelectorHSpacing = 2.0f;
42 static const float kTextFieldsHSpacing = 6.0f;
43 
44 BColorControl::BColorControl(BPoint leftTop, color_control_layout layout,
45 	float cellSize, const char *name, BMessage *message,
46 	bool bufferedDrawing)
47 	: BControl(BRect(leftTop, leftTop), name, NULL, message,
48 			B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE)
49 {
50 	_InitData(layout, cellSize, bufferedDrawing, NULL);
51 }
52 
53 
54 BColorControl::BColorControl(BMessage* archive)
55 	: BControl(archive)
56 {
57 	int32 layout;
58 	float cellSize;
59 	bool useOffscreen;
60 
61 	archive->FindInt32("_layout", &layout);
62 	archive->FindFloat("_csize", &cellSize);
63 	archive->FindBool("_use_off", &useOffscreen);
64 
65 	_InitData((color_control_layout)layout, cellSize, useOffscreen, archive);
66 }
67 
68 
69 BColorControl::~BColorControl()
70 {
71 	delete fBitmap;
72 }
73 
74 
75 void
76 BColorControl::_InitData(color_control_layout layout, float size,
77 	bool useOffscreen, BMessage* archive)
78 {
79 	fPaletteMode = BScreen(B_MAIN_SCREEN_ID).ColorSpace() == B_CMAP8;
80 		//TODO: we don't support workspace and colorspace changing for now
81 		//		so we take the main_screen colorspace at startup
82 	fColumns = layout;
83 	fRows = 256 / fColumns;
84 	fCellSize = ceil(max_c(kMinCellSize, size));
85 
86 	fSelectedPaletteColorIndex = -1;
87 	fPreviousSelectedPaletteColorIndex = -1;
88 	fFocusedComponent = 0;
89 
90 	const char* red = B_TRANSLATE_MARK("Red:");
91 	const char* green = B_TRANSLATE_MARK("Green:");
92 	const char* blue = B_TRANSLATE_MARK("Blue:");
93 	red = gSystemCatalog.GetString(red, "ColorControl");
94 	green = gSystemCatalog.GetString(green, "ColorControl");
95 	blue = gSystemCatalog.GetString(blue, "ColorControl");
96 
97 	if (archive) {
98 		fRedText = (BTextControl*)FindView("_red");
99 		fGreenText = (BTextControl*)FindView("_green");
100 		fBlueText = (BTextControl*)FindView("_blue");
101 
102 		int32 value = 0;
103 		archive->FindInt32("_val", &value);
104 
105 		SetValue(value);
106 	} else {
107 		BRect rect(0.0f, 0.0f, 70.0f, 15.0f);
108 		float labelWidth = std::max(StringWidth(red),
109 			std::max(StringWidth(green), StringWidth(blue))) + 5;
110 		rect.right = labelWidth + StringWidth("999") + 20;
111 
112 		// red
113 
114 		fRedText = new BTextControl(rect, "_red", red, "0",
115 			new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP,
116 			B_WILL_DRAW | B_NAVIGABLE);
117 		fRedText->SetDivider(labelWidth);
118 
119 		float offset = fRedText->Bounds().Height() + 2;
120 
121 		for (int32 i = 0; i < 256; i++)
122 			fRedText->TextView()->DisallowChar(i);
123 		for (int32 i = '0'; i <= '9'; i++)
124 			fRedText->TextView()->AllowChar(i);
125 		fRedText->TextView()->SetMaxBytes(3);
126 
127 		// green
128 
129 		rect.OffsetBy(0.0f, offset);
130 		fGreenText = new BTextControl(rect, "_green", green, "0",
131 			new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP,
132 			B_WILL_DRAW | B_NAVIGABLE);
133 		fGreenText->SetDivider(labelWidth);
134 
135 		for (int32 i = 0; i < 256; i++)
136 			fGreenText->TextView()->DisallowChar(i);
137 		for (int32 i = '0'; i <= '9'; i++)
138 			fGreenText->TextView()->AllowChar(i);
139 		fGreenText->TextView()->SetMaxBytes(3);
140 
141 		// blue
142 
143 		rect.OffsetBy(0.0f, offset);
144 		fBlueText = new BTextControl(rect, "_blue", blue, "0",
145 			new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP,
146 			B_WILL_DRAW | B_NAVIGABLE);
147 		fBlueText->SetDivider(labelWidth);
148 
149 		for (int32 i = 0; i < 256; i++)
150 			fBlueText->TextView()->DisallowChar(i);
151 		for (int32 i = '0'; i <= '9'; i++)
152 			fBlueText->TextView()->AllowChar(i);
153 		fBlueText->TextView()->SetMaxBytes(3);
154 
155 		AddChild(fRedText);
156 		AddChild(fGreenText);
157 		AddChild(fBlueText);
158 	}
159 
160 	_LayoutView();
161 
162 	if (useOffscreen) {
163 		BRect bounds = fPaletteFrame;
164 		bounds.InsetBy(-2.0f, -2.0f);
165 
166 		fBitmap = new BBitmap(bounds, B_RGB32, true, false);
167 		fOffscreenView = new BView(bounds, "off_view", 0, 0);
168 
169 		fBitmap->Lock();
170 		fBitmap->AddChild(fOffscreenView);
171 		fBitmap->Unlock();
172 	} else {
173 		fBitmap = NULL;
174 		fOffscreenView = NULL;
175 	}
176 }
177 
178 
179 void
180 BColorControl::_LayoutView()
181 {
182 	if (fPaletteMode){
183 		fPaletteFrame.Set(2.0f, 2.0f,
184 			float(fColumns) * fCellSize + 2.0,
185 			float(fRows) * fCellSize + 2.0);
186 	} else {
187 		fPaletteFrame.Set(2.0f, 2.0f,
188 			float(fColumns) * fCellSize + 2.0,
189 			float(fRows) * fCellSize + 2.0 - 1.0);
190 			//1 pixel adjust so that the inner space
191 			//has exactly rows*cellsize pixels in height
192 	}
193 
194 	BRect rect = fPaletteFrame.InsetByCopy(-2.0,-2.0);	//bevel
195 
196 	if (rect.Height() < fBlueText->Frame().bottom) {
197 		// adjust the height to fit
198 		rect.bottom = fBlueText->Frame().bottom;
199 	}
200 
201 	float offset = floor(rect.bottom / 4);
202 	float y = offset;
203 	if (offset < fRedText->Bounds().Height() + 2) {
204 		offset = fRedText->Bounds().Height() + 2;
205 		y = 0;
206 	}
207 
208 	fRedText->MoveTo(rect.right + kTextFieldsHSpacing, y);
209 
210 	y += offset;
211 	fGreenText->MoveTo(rect.right + kTextFieldsHSpacing, y);
212 
213 	y += offset;
214 	fBlueText->MoveTo(rect.right + kTextFieldsHSpacing, y);
215 
216 	ResizeTo(rect.Width() + kTextFieldsHSpacing + fRedText->Bounds().Width(), rect.Height());
217 
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 		float invalidateRadius = kSelectorSize / 2 + kSelectorPenSize;
283 		BPoint p;
284 
285 		if (c1.red != c2.red) {
286 			p = _SelectorPosition(_RampFrame(1), c1.red);
287 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
288 				 p.x + invalidateRadius, p.y + invalidateRadius));
289 
290 			p = _SelectorPosition(_RampFrame(1), c2.red);
291 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
292 				 p.x + invalidateRadius, p.y + invalidateRadius));
293 		}
294 		if (c1.green != c2.green) {
295 			p = _SelectorPosition(_RampFrame(2), c1.green);
296 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
297 				 p.x + invalidateRadius, p.y + invalidateRadius));
298 
299 			p = _SelectorPosition(_RampFrame(2), c2.green);
300 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
301 				 p.x + invalidateRadius, p.y + invalidateRadius));
302 		}
303 		if (c1.blue != c2.blue) {
304 			p = _SelectorPosition(_RampFrame(3), c1.blue);
305 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
306 				 p.x + invalidateRadius, p.y + invalidateRadius));
307 
308 			p = _SelectorPosition(_RampFrame(3), c2.blue);
309 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
310 				 p.x + invalidateRadius, p.y + invalidateRadius));
311 		}
312 	}
313 
314 	// Set the value here, since BTextControl will trigger
315 	// Window()->UpdateIfNeeded() which will cause us to draw the indicators
316 	// at the old offset.
317 	if (Value() != value)
318 		BControl::SetValueNoUpdate(value);
319 
320 	// the textcontrols have to be updated even when the color
321 	// hasn't changed since the value is clamped upstream
322 	// and the textcontrols would still show the unclamped value
323 	char string[4];
324 	sprintf(string, "%d", c2.red);
325 	fRedText->SetText(string);
326 	sprintf(string, "%d", c2.green);
327 	fGreenText->SetText(string);
328 	sprintf(string, "%d", c2.blue);
329 	fBlueText->SetText(string);
330 }
331 
332 
333 rgb_color
334 BColorControl::ValueAsColor()
335 {
336 	int32 value = Value();
337 	rgb_color color;
338 
339 	color.red = (value & 0xFF000000) >> 24;
340 	color.green = (value & 0x00FF0000) >> 16;
341 	color.blue = (value & 0x0000FF00) >> 8;
342 	color.alpha = 255;
343 
344 	return color;
345 }
346 
347 
348 void
349 BColorControl::SetEnabled(bool enabled)
350 {
351 	BControl::SetEnabled(enabled);
352 
353 	fRedText->SetEnabled(enabled);
354 	fGreenText->SetEnabled(enabled);
355 	fBlueText->SetEnabled(enabled);
356 }
357 
358 
359 void
360 BColorControl::AttachedToWindow()
361 {
362 	if (Parent())
363 		SetViewColor(Parent()->ViewColor());
364 	else
365 		SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
366 
367 	BControl::AttachedToWindow();
368 
369 	fRedText->SetTarget(this);
370 	fGreenText->SetTarget(this);
371 	fBlueText->SetTarget(this);
372 
373 	if (fBitmap)
374 		_InitOffscreen();
375 }
376 
377 
378 void
379 BColorControl::MessageReceived(BMessage *message)
380 {
381 	switch (message->what) {
382 		case kMsgColorEntered:
383 		{
384 			rgb_color color;
385 			color.red = min_c(strtol(fRedText->Text(), NULL, 10), 255);
386 			color.green = min_c(strtol(fGreenText->Text(), NULL, 10), 255);
387 			color.blue = min_c(strtol(fBlueText->Text(), NULL, 10), 255);
388 			color.alpha = 255;
389 
390 			SetValue(color);
391 			Invoke();
392 			break;
393 		}
394 		default:
395 			BControl::MessageReceived(message);
396 	}
397 }
398 
399 
400 void
401 BColorControl::Draw(BRect updateRect)
402 {
403 	if (fBitmap)
404 		DrawBitmap(fBitmap, B_ORIGIN);
405 	else
406 		_DrawColorArea(this, updateRect);
407 	_DrawSelectors(this);
408 }
409 
410 
411 void
412 BColorControl::_DrawColorArea(BView* target, BRect update)
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 
514 
515 void
516 BColorControl::_DrawSelectors(BView* target)
517 {
518 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
519 	rgb_color lightenmax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
520 
521 	if (fPaletteMode) {
522 		if (fSelectedPaletteColorIndex != -1) {
523 	      	target->SetHighColor(lightenmax);
524 			target->StrokeRect(_PaletteSelectorFrame(fSelectedPaletteColorIndex));
525 		}
526 	} else {
527 		rgb_color color = ValueAsColor();
528 		target->SetPenSize(kSelectorPenSize);
529 		target->SetHighColor(255, 255, 255);
530 
531 		target->StrokeEllipse(_SelectorPosition(_RampFrame(1), color.red),
532 			 kSelectorSize / 2, kSelectorSize / 2);
533 		target->StrokeEllipse(_SelectorPosition(_RampFrame(2), color.green),
534 			 kSelectorSize / 2, kSelectorSize / 2);
535 		target->StrokeEllipse(_SelectorPosition(_RampFrame(3), color.blue),
536 			 kSelectorSize / 2, kSelectorSize / 2);
537 
538 		target->SetPenSize(1.0f);
539 	}
540 }
541 
542 
543 void
544 BColorControl::_ColorRamp(BRect rect, BView* target,
545 	rgb_color baseColor, rgb_color compColor, int16 flag, bool focused, BRect update)
546 {
547 	float width = rect.Width() + 1;
548 	rgb_color color;
549 	color.alpha = 255;
550 
551 	update = update & rect;
552 
553 	if (update.IsValid() && update.Width() >= 0){
554 		target->BeginLineArray((int32)update.Width() + 1);
555 
556 		for (float i = (update.left - rect.left); i <= (update.right - rect.left) + 1; i++) {
557 			color.red = (uint8)(i * baseColor.red / width) + compColor.red;
558 			color.green = (uint8)(i * baseColor.green / width) + compColor.green;
559 			color.blue = (uint8)(i * baseColor.blue / width) + compColor.blue;
560 			target->AddLine(BPoint(rect.left + i, rect.top),
561 				BPoint(rect.left + i, rect.bottom - 1), color);
562 		}
563 
564 		target->EndLineArray();
565 	}
566 }
567 
568 
569 BPoint
570 BColorControl::_SelectorPosition(const BRect& rampRect, uint8 shade) const
571 {
572 	float radius = kSelectorSize / 2 + kSelectorPenSize / 2;
573 
574 	return BPoint(rampRect.left + kSelectorHSpacing + radius +
575 		shade * (rampRect.Width() - 2 * (kSelectorHSpacing + radius)) / 255,
576 		rampRect.top + rampRect.Height() / 2);
577 }
578 
579 
580 BRect
581 BColorControl::_RampFrame(uint8 rampIndex) const
582 {
583 	float rampHeight = float(fRows) * fCellSize / 4.0f;
584 
585 	return BRect( fPaletteFrame.left,
586 		fPaletteFrame.top + float(rampIndex) * rampHeight,
587 		fPaletteFrame.right,
588 		fPaletteFrame.top + float(rampIndex + 1) * rampHeight);
589 }
590 
591 
592 BRect
593 BColorControl::_PaletteSelectorFrame(uint8 colorIndex) const
594 {
595 	uint32 row = colorIndex / fColumns;
596 	uint32 column = colorIndex % fColumns;
597 	float x = fPaletteFrame.left + column * fCellSize;
598 	float y = fPaletteFrame.top + row * fCellSize;
599 	return BRect(x, y, x + fCellSize, y + fCellSize);
600 }
601 
602 
603 void
604 BColorControl::_InitOffscreen()
605 {
606 	if (fBitmap->Lock()) {
607 		_DrawColorArea(fOffscreenView, fPaletteFrame.InsetByCopy(-2.0f,-2.0f));
608 		fOffscreenView->Sync();
609 		fBitmap->Unlock();
610 	}
611 }
612 
613 
614 void
615 BColorControl::SetCellSize(float cellSide)
616 {
617 	fCellSize = ceil(max_c(kMinCellSize, cellSide));
618 	_LayoutView();
619 	ResizeToPreferred();
620 }
621 
622 
623 float
624 BColorControl::CellSize() const
625 {
626 	return fCellSize;
627 }
628 
629 
630 void
631 BColorControl::SetLayout(color_control_layout layout)
632 {
633 	switch (layout) {
634 		case B_CELLS_4x64:
635 			fColumns = 4;
636 			fRows = 64;
637 			break;
638 		case B_CELLS_8x32:
639 			fColumns = 8;
640 			fRows = 32;
641 			break;
642 		case B_CELLS_16x16:
643 			fColumns = 16;
644 			fRows = 16;
645 			break;
646 		case B_CELLS_32x8:
647 			fColumns = 32;
648 			fRows = 8;
649 			break;
650 		case B_CELLS_64x4:
651 			fColumns = 64;
652 			fRows = 4;
653 			break;
654 	}
655 
656 	_LayoutView();
657 
658 	ResizeToPreferred();
659 	Invalidate();
660 }
661 
662 
663 color_control_layout
664 BColorControl::Layout() const
665 {
666 	if (fColumns == 4 && fRows == 64)
667 		return B_CELLS_4x64;
668 	if (fColumns == 8 && fRows == 32)
669 		return B_CELLS_8x32;
670 	if (fColumns == 16 && fRows == 16)
671 		return B_CELLS_16x16;
672 	if (fColumns == 32 && fRows == 8)
673 		return B_CELLS_32x8;
674 	if (fColumns == 64 && fRows == 4)
675 		return B_CELLS_64x4;
676 
677 	return B_CELLS_32x8;
678 }
679 
680 
681 void
682 BColorControl::WindowActivated(bool state)
683 {
684 	BControl::WindowActivated(state);
685 }
686 
687 
688 void
689 BColorControl::KeyDown(const char* bytes, int32 numBytes)
690 {
691 	// TODO: make this keyboard navigable!
692 	BControl::KeyDown(bytes, numBytes);
693 }
694 
695 
696 void
697 BColorControl::MouseUp(BPoint point)
698 {
699 	fFocusedComponent = 0;
700 	SetTracking(false);
701 }
702 
703 
704 void
705 BColorControl::MouseDown(BPoint point)
706 {
707 	if (!IsEnabled())
708 		return;
709 	if (!fPaletteFrame.Contains(point))
710 		return;
711 
712 	MakeFocus();
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(),
727 				255));
728 
729 		if (_RampFrame(0).Contains(point)) {
730 			color.red = color.green = color.blue = shade;
731 			fFocusedComponent = 1;
732 		} else if (_RampFrame(1).Contains(point)) {
733 			color.red = shade;
734 			fFocusedComponent = 2;
735 		} else if (_RampFrame(2).Contains(point)) {
736 			color.green = shade;
737 			fFocusedComponent = 3;
738 		} else if (_RampFrame(3).Contains(point)){
739 			color.blue = shade;
740 			fFocusedComponent = 4;
741 		}
742 
743 		SetValue(color);
744 
745 	}
746 
747 	Invoke();
748 
749 	SetTracking(true);
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_LAYOUT_INVALIDATED:
932 		{
933 			perform_data_layout_invalidated* data
934 				= (perform_data_layout_invalidated*)_data;
935 			BColorControl::LayoutInvalidated(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