xref: /haiku/src/kits/interface/ColorControl.cpp (revision a1163de83ea633463a79de234b8742ee106531b2)
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 
250 	if (fPaletteMode) {
251 		//workaround when two indexes have the same color
252 		rgb_color c = BScreen(Window()).ColorForIndex(fSelectedPaletteColorIndex);
253 		c.alpha = 255;
254 		if (fSelectedPaletteColorIndex == -1 || c != c2) {
255 				//here SetValue hasn't been called by mouse tracking
256 			fSelectedPaletteColorIndex = BScreen(Window()).IndexForColor(c2);
257 		}
258 
259 		c2 = BScreen(Window()).ColorForIndex(fSelectedPaletteColorIndex);
260 
261 		Invalidate(_PaletteSelectorFrame(fPreviousSelectedPaletteColorIndex));
262 		Invalidate(_PaletteSelectorFrame(fSelectedPaletteColorIndex));
263 
264 		fPreviousSelectedPaletteColorIndex = fSelectedPaletteColorIndex;
265 	} else {
266 		float invalidateRadius = kSelectorSize / 2 + kSelectorPenSize;
267 		BPoint p;
268 
269 		if (c1.red != c2.red) {
270 			p = _SelectorPosition(_RampFrame(1), c1.red);
271 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
272 				 p.x + invalidateRadius, p.y + invalidateRadius));
273 
274 			p = _SelectorPosition(_RampFrame(1), c2.red);
275 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
276 				 p.x + invalidateRadius, p.y + invalidateRadius));
277 		}
278 		if (c1.green != c2.green) {
279 			p = _SelectorPosition(_RampFrame(2), c1.green);
280 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
281 				 p.x + invalidateRadius, p.y + invalidateRadius));
282 
283 			p = _SelectorPosition(_RampFrame(2), c2.green);
284 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
285 				 p.x + invalidateRadius, p.y + invalidateRadius));
286 		}
287 		if (c1.blue != c2.blue) {
288 			p = _SelectorPosition(_RampFrame(3), c1.blue);
289 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
290 				 p.x + invalidateRadius, p.y + invalidateRadius));
291 
292 			p = _SelectorPosition(_RampFrame(3), c2.blue);
293 			Invalidate(BRect(p.x - invalidateRadius, p.y - invalidateRadius,
294 				 p.x + invalidateRadius, p.y + invalidateRadius));
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)
388 		DrawBitmap(fBitmap, B_ORIGIN);
389 	else
390 		_DrawColorArea(this, updateRect);
391 	_DrawSelectors(this);
392 }
393 
394 
395 void
396 BColorControl::_DrawColorArea(BView* target, BRect update)
397 {
398 	BRect bevelRect = fPaletteFrame.InsetByCopy(-2.0,-2.0);	//bevel
399 	bool enabled = IsEnabled();
400 
401 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
402 	rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
403 
404 	if (be_control_look != NULL) {
405 		uint32 flags = 0;
406 		if (!enabled)
407 			flags |= BControlLook::B_DISABLED;
408 		be_control_look->DrawTextControlBorder(target, bevelRect, update,
409 			noTint, flags);
410 	} else {
411 		rgb_color lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT);
412 		rgb_color lightenmax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
413 		rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
414 		rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
415 
416 		// First bevel
417 		if (enabled)
418 			target->SetHighColor(darken1);
419 		else
420 			target->SetHighColor(noTint);
421 		target->StrokeLine(bevelRect.LeftBottom(), bevelRect.LeftTop());
422 		target->StrokeLine(bevelRect.LeftTop(), bevelRect.RightTop());
423 		if (enabled)
424 			target->SetHighColor(lightenmax);
425 		else
426 			target->SetHighColor(lighten1);
427 		target->StrokeLine(BPoint(bevelRect.left + 1.0f, bevelRect.bottom),
428 			bevelRect.RightBottom());
429 		target->StrokeLine(bevelRect.RightBottom(),
430 			BPoint(bevelRect.right, bevelRect.top + 1.0f));
431 
432 		bevelRect.InsetBy(1.0f, 1.0f);
433 
434 		// Second bevel
435 		if (enabled)
436 			target->SetHighColor(darken4);
437 		else
438 			target->SetHighColor(darken2);
439 		target->StrokeLine(bevelRect.LeftBottom(), bevelRect.LeftTop());
440 		target->StrokeLine(bevelRect.LeftTop(), bevelRect.RightTop());
441 		target->SetHighColor(noTint);
442 		target->StrokeLine(BPoint(bevelRect.left + 1.0f, bevelRect.bottom),
443 			bevelRect.RightBottom());
444 		target->StrokeLine(bevelRect.RightBottom(),
445 			BPoint(bevelRect.right, bevelRect.top + 1.0f));
446 	}
447 
448 	if (fPaletteMode) {
449 		int colBegin = max_c(0, -1 + int(update.left) / int(fCellSize));
450 		int colEnd = min_c(fColumns, 2 + int(update.right) / int(fCellSize));
451 		int rowBegin = max_c(0, -1 + int(update.top) / int(fCellSize));
452 		int rowEnd = min_c(fRows, 2 + int(update.bottom) / int(fCellSize));
453 
454 		//grid
455 		if (enabled)
456 			target->SetHighColor(darken1);
457 		else
458 			target->SetHighColor(noTint);
459 		for (int xi = 0; xi < fColumns+1; xi++) {
460 			float x = fPaletteFrame.left + float(xi) * fCellSize;
461 			target->StrokeLine(BPoint(x, fPaletteFrame.top), BPoint(x, fPaletteFrame.bottom));
462 		}
463 		for (int yi = 0; yi < fRows+1; yi++) {
464 			float y = fPaletteFrame.top + float(yi) * fCellSize;
465 			target->StrokeLine(BPoint(fPaletteFrame.left, y), BPoint(fPaletteFrame.right, y));
466 		}
467 
468 		//colors
469 		for (int col = colBegin; col < colEnd; col++) {
470 			for (int row = rowBegin; row < rowEnd; row++) {
471 				uint8 colorIndex = row * fColumns + col;
472 				float x = fPaletteFrame.left + col * fCellSize;
473 				float y = fPaletteFrame.top + row * fCellSize;
474 
475 				target->SetHighColor(system_colors()->color_list[colorIndex]);
476 				target->FillRect(BRect(x+1, y+1, x + fCellSize - 1, y + fCellSize - 1));
477 			}
478 		}
479 	} else {
480 		rgb_color white = {255, 255, 255, 255};
481 		rgb_color red = {255, 0, 0, 255};
482 		rgb_color green = {0, 255, 0, 255};
483 		rgb_color blue = {0, 0, 255, 255};
484 
485 		rgb_color compColor = {0, 0, 0, 255};
486 		if (!enabled) {
487 			compColor.red = compColor.green = compColor.blue = 156;
488 			red.red = green.green = blue.blue = 70;
489 			white.red = white.green = white.blue = 70;
490 		}
491 		_ColorRamp(_RampFrame(0), target, white, compColor, 0, false, update);
492 		_ColorRamp(_RampFrame(1), target, red, compColor, 0, false, update);
493 		_ColorRamp(_RampFrame(2), target, green, compColor, 0, false, update);
494 		_ColorRamp(_RampFrame(3), target, blue, compColor, 0, false, update);
495 	}
496 }
497 
498 
499 void
500 BColorControl::_DrawSelectors(BView* target)
501 {
502 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
503 	rgb_color lightenmax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
504 
505 	if (fPaletteMode) {
506 		if (fSelectedPaletteColorIndex != -1) {
507 	      	target->SetHighColor(lightenmax);
508 			target->StrokeRect(_PaletteSelectorFrame(fSelectedPaletteColorIndex));
509 		}
510 	} else {
511 		rgb_color color = ValueAsColor();
512 		target->SetPenSize(kSelectorPenSize);
513 		target->SetHighColor(255, 255, 255);
514 
515 		target->StrokeEllipse(_SelectorPosition(_RampFrame(1), color.red),
516 			 kSelectorSize / 2, kSelectorSize / 2);
517 		target->StrokeEllipse(_SelectorPosition(_RampFrame(2), color.green),
518 			 kSelectorSize / 2, kSelectorSize / 2);
519 		target->StrokeEllipse(_SelectorPosition(_RampFrame(3), color.blue),
520 			 kSelectorSize / 2, kSelectorSize / 2);
521 
522 		target->SetPenSize(1.0f);
523 	}
524 }
525 
526 
527 void
528 BColorControl::_ColorRamp(BRect rect, BView* target,
529 	rgb_color baseColor, rgb_color compColor, int16 flag, bool focused, BRect update)
530 {
531 	float width = rect.Width() + 1;
532 	rgb_color color;
533 	color.alpha = 255;
534 
535 	update = update & rect;
536 
537 	if (update.IsValid() && update.Width() >= 0){
538 		target->BeginLineArray((int32)update.Width() + 1);
539 
540 		for (float i = (update.left - rect.left); i <= (update.right - rect.left) + 1; i++) {
541 			color.red = (uint8)(i * baseColor.red / width) + compColor.red;
542 			color.green = (uint8)(i * baseColor.green / width) + compColor.green;
543 			color.blue = (uint8)(i * baseColor.blue / width) + compColor.blue;
544 			target->AddLine(BPoint(rect.left + i, rect.top),
545 				BPoint(rect.left + i, rect.bottom - 1), color);
546 		}
547 
548 		target->EndLineArray();
549 	}
550 }
551 
552 
553 BPoint
554 BColorControl::_SelectorPosition(const BRect& rampRect, uint8 shade) const
555 {
556 	float radius = kSelectorSize / 2 + kSelectorPenSize / 2;
557 
558 	return BPoint(rampRect.left + kSelectorHSpacing + radius +
559 		shade * (rampRect.Width() - 2 * (kSelectorHSpacing + radius)) / 255,
560 		rampRect.top + rampRect.Height() / 2);
561 }
562 
563 
564 BRect
565 BColorControl::_RampFrame(uint8 rampIndex) const
566 {
567 	float rampHeight = float(fRows) * fCellSize / 4.0f;
568 
569 	return BRect( fPaletteFrame.left,
570 		fPaletteFrame.top + float(rampIndex) * rampHeight,
571 		fPaletteFrame.right,
572 		fPaletteFrame.top + float(rampIndex + 1) * rampHeight);
573 }
574 
575 
576 BRect
577 BColorControl::_PaletteSelectorFrame(uint8 colorIndex) const
578 {
579 	uint32 row = colorIndex / fColumns;
580 	uint32 column = colorIndex % fColumns;
581 	float x = fPaletteFrame.left + column * fCellSize;
582 	float y = fPaletteFrame.top + row * fCellSize;
583 	return BRect(x, y, x + fCellSize, y + fCellSize);
584 }
585 
586 
587 void
588 BColorControl::_InitOffscreen()
589 {
590 	if (fBitmap->Lock()) {
591 		_DrawColorArea(fOffscreenView, fPaletteFrame.InsetByCopy(-2.0f,-2.0f));
592 		fOffscreenView->Sync();
593 		fBitmap->Unlock();
594 	}
595 }
596 
597 
598 void
599 BColorControl::SetCellSize(float cellSide)
600 {
601 	fCellSize = ceil(max_c(kMinCellSize, cellSide));
602 	_LayoutView();
603 	ResizeToPreferred();
604 }
605 
606 
607 float
608 BColorControl::CellSize() const
609 {
610 	return fCellSize;
611 }
612 
613 
614 void
615 BColorControl::SetLayout(color_control_layout layout)
616 {
617 	switch (layout) {
618 		case B_CELLS_4x64:
619 			fColumns = 4;
620 			fRows = 64;
621 			break;
622 		case B_CELLS_8x32:
623 			fColumns = 8;
624 			fRows = 32;
625 			break;
626 		case B_CELLS_16x16:
627 			fColumns = 16;
628 			fRows = 16;
629 			break;
630 		case B_CELLS_32x8:
631 			fColumns = 32;
632 			fRows = 8;
633 			break;
634 		case B_CELLS_64x4:
635 			fColumns = 64;
636 			fRows = 4;
637 			break;
638 	}
639 
640 	_LayoutView();
641 
642 	ResizeToPreferred();
643 	Invalidate();
644 }
645 
646 
647 color_control_layout
648 BColorControl::Layout() const
649 {
650 	if (fColumns == 4 && fRows == 64)
651 		return B_CELLS_4x64;
652 	if (fColumns == 8 && fRows == 32)
653 		return B_CELLS_8x32;
654 	if (fColumns == 16 && fRows == 16)
655 		return B_CELLS_16x16;
656 	if (fColumns == 32 && fRows == 8)
657 		return B_CELLS_32x8;
658 	if (fColumns == 64 && fRows == 4)
659 		return B_CELLS_64x4;
660 
661 	return B_CELLS_32x8;
662 }
663 
664 
665 void
666 BColorControl::WindowActivated(bool state)
667 {
668 	BControl::WindowActivated(state);
669 }
670 
671 
672 void
673 BColorControl::KeyDown(const char* bytes, int32 numBytes)
674 {
675 	// TODO: make this keyboard navigable!
676 	BControl::KeyDown(bytes, numBytes);
677 }
678 
679 
680 void
681 BColorControl::MouseUp(BPoint point)
682 {
683 	fFocusedComponent = 0;
684 	SetTracking(false);
685 }
686 
687 
688 void
689 BColorControl::MouseDown(BPoint point)
690 {
691 	if (!IsEnabled())
692 		return;
693 	if (!fPaletteFrame.Contains(point))
694 		return;
695 
696 	MakeFocus();
697 
698 	if (fPaletteMode) {
699 		int column = (int) ( (point.x - fPaletteFrame.left) / fCellSize );
700 		int row = (int) ( (point.y - fPaletteFrame.top) / fCellSize );
701 		int colorIndex = row * fColumns + column;
702 		if (colorIndex >= 0 && colorIndex < 256) {
703 			fSelectedPaletteColorIndex = colorIndex;
704 			SetValue(system_colors()->color_list[colorIndex]);
705 		}
706 	} else {
707 		rgb_color color = ValueAsColor();
708 
709 		uint8 shade = (unsigned char)max_c(0,
710 			min_c((point.x - _RampFrame(0).left) * 255 / _RampFrame(0).Width(),
711 				255));
712 
713 		if (_RampFrame(0).Contains(point)) {
714 			color.red = color.green = color.blue = shade;
715 			fFocusedComponent = 1;
716 		} else if (_RampFrame(1).Contains(point)) {
717 			color.red = shade;
718 			fFocusedComponent = 2;
719 		} else if (_RampFrame(2).Contains(point)) {
720 			color.green = shade;
721 			fFocusedComponent = 3;
722 		} else if (_RampFrame(3).Contains(point)){
723 			color.blue = shade;
724 			fFocusedComponent = 4;
725 		}
726 
727 		SetValue(color);
728 
729 	}
730 
731 	Invoke();
732 
733 	SetTracking(true);
734 	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY|B_LOCK_WINDOW_FOCUS);
735 }
736 
737 
738 void
739 BColorControl::MouseMoved(BPoint point, uint32 transit,
740 	const BMessage *message)
741 {
742 	if (!IsTracking())
743 		return;
744 
745 	if (fPaletteMode && fPaletteFrame.Contains(point)) {
746 		int column = (int) ( (point.x - fPaletteFrame.left) / fCellSize );
747 		int row = (int) ( (point.y - fPaletteFrame.top) / fCellSize );
748 		int colorIndex = row * fColumns + column;
749 		if (colorIndex >= 0 && colorIndex < 256) {
750 			fSelectedPaletteColorIndex = colorIndex;
751 			SetValue(system_colors()->color_list[colorIndex]);
752 		}
753 	} else {
754 		if (fFocusedComponent == 0)
755 			return;
756 
757 		rgb_color color = ValueAsColor();
758 
759 		uint8 shade = (unsigned char)max_c(0,
760 			min_c((point.x - _RampFrame(0).left) * 255 / _RampFrame(0).Width(), 255));
761 
762 		switch (fFocusedComponent) {
763 			case 1:
764 				color.red = color.green = color.blue = shade;
765 				break;
766 			case 2:
767 				color.red = shade;
768 				break;
769 			case 3:
770 				color.green = shade;
771 				break;
772 			case 4:
773 				color.blue = shade;
774 				break;
775 			default:
776 				break;
777 		}
778 
779 		SetValue(color);
780 	}
781 
782 	Invoke();
783 }
784 
785 
786 void
787 BColorControl::DetachedFromWindow()
788 {
789 	BControl::DetachedFromWindow();
790 }
791 
792 
793 void
794 BColorControl::GetPreferredSize(float *_width, float *_height)
795 {
796 	BRect rect = fPaletteFrame.InsetByCopy(-2.0,-2.0);	//bevel
797 
798 	if (rect.Height() < fBlueText->Frame().bottom) {
799 		// adjust the height to fit
800 		rect.bottom = fBlueText->Frame().bottom;
801 	}
802 
803 	if (_width)
804 		*_width = rect.Width() + kTextFieldsHSpacing + fRedText->Bounds().Width();
805 
806 	if (_height)
807 		*_height = rect.Height();
808 }
809 
810 
811 void
812 BColorControl::ResizeToPreferred()
813 {
814 	BControl::ResizeToPreferred();
815 
816 	_LayoutView();
817 }
818 
819 
820 status_t
821 BColorControl::Invoke(BMessage *msg)
822 {
823 	return BControl::Invoke(msg);
824 }
825 
826 
827 void
828 BColorControl::FrameMoved(BPoint new_position)
829 {
830 	BControl::FrameMoved(new_position);
831 }
832 
833 
834 void
835 BColorControl::FrameResized(float new_width, float new_height)
836 {
837 	BControl::FrameResized(new_width, new_height);
838 }
839 
840 
841 BHandler *
842 BColorControl::ResolveSpecifier(BMessage *msg, int32 index,
843 	BMessage *specifier, int32 form, const char *property)
844 {
845 	return BControl::ResolveSpecifier(msg, index, specifier, form, property);
846 }
847 
848 
849 status_t
850 BColorControl::GetSupportedSuites(BMessage *data)
851 {
852 	return BControl::GetSupportedSuites(data);
853 }
854 
855 
856 void
857 BColorControl::MakeFocus(bool state)
858 {
859 	BControl::MakeFocus(state);
860 }
861 
862 
863 void
864 BColorControl::AllAttached()
865 {
866 	BControl::AllAttached();
867 }
868 
869 
870 void
871 BColorControl::AllDetached()
872 {
873 	BControl::AllDetached();
874 }
875 
876 
877 status_t
878 BColorControl::Perform(perform_code code, void* _data)
879 {
880 	switch (code) {
881 		case PERFORM_CODE_MIN_SIZE:
882 			((perform_data_min_size*)_data)->return_value
883 				= BColorControl::MinSize();
884 			return B_OK;
885 		case PERFORM_CODE_MAX_SIZE:
886 			((perform_data_max_size*)_data)->return_value
887 				= BColorControl::MaxSize();
888 			return B_OK;
889 		case PERFORM_CODE_PREFERRED_SIZE:
890 			((perform_data_preferred_size*)_data)->return_value
891 				= BColorControl::PreferredSize();
892 			return B_OK;
893 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
894 			((perform_data_layout_alignment*)_data)->return_value
895 				= BColorControl::LayoutAlignment();
896 			return B_OK;
897 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
898 			((perform_data_has_height_for_width*)_data)->return_value
899 				= BColorControl::HasHeightForWidth();
900 			return B_OK;
901 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
902 		{
903 			perform_data_get_height_for_width* data
904 				= (perform_data_get_height_for_width*)_data;
905 			BColorControl::GetHeightForWidth(data->width, &data->min, &data->max,
906 				&data->preferred);
907 			return B_OK;
908 }
909 		case PERFORM_CODE_SET_LAYOUT:
910 		{
911 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
912 			BColorControl::SetLayout(data->layout);
913 			return B_OK;
914 		}
915 		case PERFORM_CODE_INVALIDATE_LAYOUT:
916 		{
917 			perform_data_invalidate_layout* data
918 				= (perform_data_invalidate_layout*)_data;
919 			BColorControl::InvalidateLayout(data->descendants);
920 			return B_OK;
921 		}
922 		case PERFORM_CODE_DO_LAYOUT:
923 		{
924 			BColorControl::DoLayout();
925 			return B_OK;
926 		}
927 	}
928 
929 	return BControl::Perform(code, _data);
930 }
931 
932 
933 void BColorControl::_ReservedColorControl1() {}
934 void BColorControl::_ReservedColorControl2() {}
935 void BColorControl::_ReservedColorControl3() {}
936 void BColorControl::_ReservedColorControl4() {}
937 
938 
939 BColorControl &
940 BColorControl::operator=(const BColorControl &)
941 {
942 	return *this;
943 }
944