#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Application.h>
#include <Bitmap.h>
#include <Beep.h>
#include <ControlLook.h>
#include <GroupLayout.h>
#include <LayoutBuilder.h>
#include <Message.h>
#include <MessageRunner.h>
#include <RadioButton.h>
#include <StringView.h>
#include <TextControl.h>
#include <Window.h>
#include "ColorField.h"
#include "ColorPreview.h"
#include "ColorSlider.h"
#include "rgb_hsv.h"
#include "ColorPickerView.h"
#define round(x) (int)(x+.5)
#define hexdec(str, offset) (int)(((str[offset]<60?str[offset]-48:(str[offset]|32)-87)<<4)|(str[offset+1]<60?str[offset+1]-48:(str[offset+1]|32)-87))
ColorPickerView::ColorPickerView(const char* name, rgb_color color,
SelectedColorMode mode)
:
BView(name, 0),
h(0.0),
s(1.0),
v(1.0),
r((float)color.red / 255.0),
g((float)color.green / 255.0),
b((float)color.blue / 255.0),
fRequiresUpdate(false)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
RGB_to_HSV(r, g, b, h, s, v);
SetColorMode(mode, false);
}
ColorPickerView::~ColorPickerView()
{
}
void
ColorPickerView::AttachedToWindow()
{
rgb_color color = { (uint8)(r * 255), (uint8)(g * 255), (uint8)(b * 255),
255 };
BView::AttachedToWindow();
fColorField = new ColorField(fSelectedColorMode, *p);
fColorField->SetMarkerToColor(color);
fColorField->SetTarget(this);
fColorSlider = new ColorSlider(fSelectedColorMode, *p1, *p2);
fColorSlider->SetMarkerToColor(color);
fColorSlider->SetTarget(this);
fColorPreview = new ColorPreview(color);
fColorPreview->SetTarget(this);
fColorField->SetExplicitMinSize(BSize(256, 256));
fColorField->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
fColorSlider->SetExplicitMaxSize(
BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED));
fColorPreview->SetExplicitMinSize(BSize(B_SIZE_UNSET, 70));
const char* title[] = { "H", "S", "V", "R", "G", "B" };
int32 selectedRadioButton = _NumForMode(fSelectedColorMode);
for (int i = 0; i < 6; i++) {
fRadioButton[i] = new BRadioButton(NULL, title[i],
new BMessage(MSG_RADIOBUTTON + i));
fRadioButton[i]->SetTarget(this);
if (i == selectedRadioButton)
fRadioButton[i]->SetValue(1);
fTextControl[i] = new BTextControl(NULL, NULL, NULL,
new BMessage(MSG_TEXTCONTROL + i));
fTextControl[i]->TextView()->SetMaxBytes(3);
for (int j = 32; j < 255; ++j) {
if (j < '0' || j > '9')
fTextControl[i]->TextView()->DisallowChar(j);
}
}
fHexTextControl = new BTextControl(NULL, "#", NULL,
new BMessage(MSG_HEXTEXTCONTROL));
fHexTextControl->TextView()->SetMaxBytes(6);
for (int j = 32; j < 255; ++j) {
if (!((j >= '0' && j <= '9') || (j >= 'a' && j <= 'f')
|| (j >= 'A' && j <= 'F'))) {
fHexTextControl->TextView()->DisallowChar(j);
}
}
const float inset = be_control_look->DefaultLabelSpacing();
BSize separatorSize(B_SIZE_UNSET, inset / 2);
BAlignment separatorAlignment(B_ALIGN_LEFT, B_ALIGN_TOP);
SetLayout(new BGroupLayout(B_HORIZONTAL));
BLayoutBuilder::Group<>(this, B_HORIZONTAL)
.AddGroup(B_HORIZONTAL, 0.0f)
.Add(fColorField)
.SetInsets(3, 3, 0, 3)
.End()
.Add(fColorSlider)
.AddGroup(B_VERTICAL)
.Add(fColorPreview)
.AddGrid(inset / 2, inset / 2)
.Add(fRadioButton[0], 0, 0)
.Add(fTextControl[0], 1, 0)
.Add(new BStringView(NULL, "°"), 2, 0)
.Add(fRadioButton[1], 0, 1)
.Add(fTextControl[1], 1, 1)
.Add(new BStringView(NULL, "%"), 2, 1)
.Add(fRadioButton[2], 0, 2)
.Add(fTextControl[2], 1, 2)
.Add(new BStringView(NULL, "%"), 2, 2)
.Add(new BSpaceLayoutItem(separatorSize, separatorSize,
separatorSize, separatorAlignment),
0, 3, 3)
.Add(fRadioButton[3], 0, 4)
.Add(fTextControl[3], 1, 4)
.Add(fRadioButton[4], 0, 5)
.Add(fTextControl[4], 1, 5)
.Add(fRadioButton[5], 0, 6)
.Add(fTextControl[5], 1, 6)
.Add(new BSpaceLayoutItem(separatorSize, separatorSize,
separatorSize, separatorAlignment),
0, 7, 3)
.AddGroup(B_HORIZONTAL, 0.0f, 0, 8, 2)
.Add(fHexTextControl->CreateLabelLayoutItem())
.Add(fHexTextControl->CreateTextViewLayoutItem())
.End()
.End()
.SetInsets(0, 3, 3, 3)
.End()
.SetInsets(inset, inset, inset, inset)
;
for (int i = 0; i < 6; i++) {
fRadioButton[i]->SetTarget(this);
fTextControl[i]->SetTarget(this);
}
fHexTextControl->SetTarget(this);
_UpdateTextControls();
}
void
ColorPickerView::MessageReceived(BMessage *message)
{
switch (message->what) {
case MSG_UPDATE_COLOR_PICKER_VIEW:
if (fRequiresUpdate)
_UpdateTextControls();
break;
case MSG_COLOR_FIELD:
{
float value1, value2;
value1 = message->FindFloat("value");
value2 = message->FindFloat("value", 1);
_UpdateColor(-1, value1, value2);
fRequiresUpdate = true;
break;
}
case MSG_COLOR_SLIDER:
{
float value;
message->FindFloat("value", &value);
_UpdateColor(value, -1, -1);
fRequiresUpdate = true;
break;
}
case MSG_COLOR_PREVIEW:
{
rgb_color* color;
ssize_t numBytes;
if (message->FindData("color", B_RGB_COLOR_TYPE,
(const void**)&color, &numBytes) == B_OK) {
color->alpha = 255;
SetColor(*color);
}
break;
}
case MSG_RADIOBUTTON:
SetColorMode(H_SELECTED);
break;
case MSG_RADIOBUTTON + 1:
SetColorMode(S_SELECTED);
break;
case MSG_RADIOBUTTON + 2:
SetColorMode(V_SELECTED);
break;
case MSG_RADIOBUTTON + 3:
SetColorMode(R_SELECTED);
break;
case MSG_RADIOBUTTON + 4:
SetColorMode(G_SELECTED);
break;
case MSG_RADIOBUTTON + 5:
SetColorMode(B_SELECTED);
break;
case MSG_TEXTCONTROL:
case MSG_TEXTCONTROL + 1:
case MSG_TEXTCONTROL + 2:
case MSG_TEXTCONTROL + 3:
case MSG_TEXTCONTROL + 4:
case MSG_TEXTCONTROL + 5:
{
int nr = message->what - MSG_TEXTCONTROL;
int value = atoi(fTextControl[nr]->Text());
switch (nr) {
case 0: {
value %= 360;
h = (float)value / 60;
} break;
case 1: {
value = min_c(value, 100);
s = (float)value / 100;
} break;
case 2: {
value = min_c(value, 100);
v = (float)value / 100;
} break;
case 3: {
value = min_c(value, 255);
r = (float)value / 255;
} break;
case 4: {
value = min_c(value, 255);
g = (float)value / 255;
} break;
case 5: {
value = min_c(value, 255);
b = (float)value / 255;
} break;
}
if (nr < 3) {
HSV_to_RGB(h, s, v, r, g, b);
}
rgb_color color = { (uint8)round(r * 255), (uint8)round(g * 255),
(uint8)round(b * 255), 255 };
SetColor(color);
break;
}
case MSG_HEXTEXTCONTROL:
{
BString string = _HexTextControlString();
if (string.Length() == 6) {
rgb_color color = {
(uint8)hexdec(string, 0),
(uint8)hexdec(string, 2),
(uint8)hexdec(string, 4), 255 };
SetColor(color);
}
break;
}
default:
BView::MessageReceived(message);
}
}
void
ColorPickerView::SetColorMode(SelectedColorMode mode, bool update)
{
fSelectedColorMode = mode;
switch (mode) {
case R_SELECTED:
p = &r;
p1 = &g;
p2 = &b;
break;
case G_SELECTED:
p = &g;
p1 = &r;
p2 = &b;
break;
case B_SELECTED:
p = &b;
p1 = &r;
p2 = &g;
break;
case H_SELECTED:
p = &h;
p1 = &s;
p2 = &v;
break;
case S_SELECTED:
p = &s;
p1 = &h;
p2 = &v;
break;
case V_SELECTED:
p = &v;
p1 = &h;
p2 = &s;
break;
}
if (!update)
return;
fColorSlider->SetModeAndValues(fSelectedColorMode, *p1, *p2);
fColorField->SetModeAndValue(fSelectedColorMode, *p);
}
void
ColorPickerView::SetColor(rgb_color color)
{
r = (float)color.red / 255;
g = (float)color.green / 255;
b = (float)color.blue / 255;
RGB_to_HSV(r, g, b, h, s, v);
fColorSlider->SetModeAndValues(fSelectedColorMode, *p1, *p2);
fColorSlider->SetMarkerToColor(color);
fColorField->SetModeAndValue(fSelectedColorMode, *p);
fColorField->SetMarkerToColor(color);
fColorPreview->SetColor(color);
_UpdateTextControls();
}
rgb_color
ColorPickerView::Color()
{
if (fSelectedColorMode & (R_SELECTED | G_SELECTED | B_SELECTED))
RGB_to_HSV(r, g, b, h, s, v);
else
HSV_to_RGB(h, s, v, r, g, b);
rgb_color color;
color.red = (uint8)round(r * 255.0);
color.green = (uint8)round(g * 255.0);
color.blue = (uint8)round(b * 255.0);
color.alpha = 255;
return color;
}
int32
ColorPickerView::_NumForMode(SelectedColorMode mode) const
{
int32 num = -1;
switch (mode) {
case H_SELECTED:
num = 0;
break;
case S_SELECTED:
num = 1;
break;
case V_SELECTED:
num = 2;
break;
case R_SELECTED:
num = 3;
break;
case G_SELECTED:
num = 4;
break;
case B_SELECTED:
num = 5;
break;
}
return num;
}
void
ColorPickerView::_UpdateColor(float value, float value1, float value2)
{
if (value != -1) {
fColorField->SetFixedValue(value);
*p = value;
} else if (value1 != -1 && value2 != -1) {
fColorSlider->SetOtherValues(value1, value2);
*p1 = value1; *p2 = value2;
}
if (fSelectedColorMode & (R_SELECTED|G_SELECTED|B_SELECTED))
RGB_to_HSV(r, g, b, h, s, v);
else
HSV_to_RGB(h, s, v, r, g, b);
rgb_color color = { (uint8)(r * 255), (uint8)(g * 255),
(uint8)(b * 255), 255 };
fColorPreview->SetColor(color);
}
void
ColorPickerView::_UpdateTextControls()
{
bool updateRequired = false;
updateRequired |= _SetTextControlValue(0, round(h * 60));
updateRequired |= _SetTextControlValue(1, round(s * 100));
updateRequired |= _SetTextControlValue(2, round(v * 100));
updateRequired |= _SetTextControlValue(3, round(r * 255));
updateRequired |= _SetTextControlValue(4, round(g * 255));
updateRequired |= _SetTextControlValue(5, round(b * 255));
BString hexString;
hexString.SetToFormat("%.6X",
(round(r * 255) << 16) | (round(g * 255) << 8) | round(b * 255));
updateRequired |= _SetHexTextControlString(hexString);
fRequiresUpdate = updateRequired;
if (fRequiresUpdate) {
BMessage message(MSG_UPDATE_COLOR_PICKER_VIEW);
BMessageRunner::StartSending(this, &message, 500000, 1);
}
}
int
ColorPickerView::_TextControlValue(int32 index)
{
return atoi(fTextControl[index]->Text());
}
bool
ColorPickerView::_SetTextControlValue(int32 index, int value)
{
BString text;
text << value;
return _SetText(fTextControl[index], text);
}
BString
ColorPickerView::_HexTextControlString() const
{
return fHexTextControl->TextView()->Text();
}
bool
ColorPickerView::_SetHexTextControlString(const BString& text)
{
return _SetText(fHexTextControl, text);
}
bool
ColorPickerView::_SetText(BTextControl* control, const BString& text)
{
if (text == control->Text())
return false;
if (control->TextView()->IsFocus())
return true;
control->SetText(text.String());
return false;
}