498 lines
17 KiB
C
498 lines
17 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) Jonathan Gordon (2006)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "config.h"
|
|
#include "stdarg.h"
|
|
#include "string.h"
|
|
#include "stdio.h"
|
|
#include "kernel.h"
|
|
#include "system.h"
|
|
#include "screen_access.h"
|
|
#include "font.h"
|
|
#include "debug.h"
|
|
#include "misc.h"
|
|
#include "settings.h"
|
|
#include "scrollbar.h"
|
|
#include "lang.h"
|
|
#include "splash.h"
|
|
#include "action.h"
|
|
#include "icon.h"
|
|
#include "color_picker.h"
|
|
#include "viewport.h"
|
|
|
|
/* structure for color info */
|
|
struct rgb_pick
|
|
{
|
|
unsigned color; /* native color value */
|
|
union
|
|
{
|
|
unsigned char rgb_val[6]; /* access to components as array */
|
|
struct
|
|
{
|
|
unsigned char r; /* native red value */
|
|
unsigned char g; /* native green value */
|
|
unsigned char b; /* native blue value */
|
|
unsigned char red; /* 8 bit red value */
|
|
unsigned char green; /* 8 bit green value */
|
|
unsigned char blue; /* 8 bit blue value */
|
|
} __attribute__ ((__packed__)); /* assume byte packing */
|
|
};
|
|
};
|
|
|
|
|
|
/* list of primary colors */
|
|
#define SB_PRIM 0
|
|
#define SB_FILL 1
|
|
static const unsigned prim_rgb[][3] =
|
|
{
|
|
/* Foreground colors for sliders */
|
|
{
|
|
LCD_RGBPACK(255, 0, 0),
|
|
LCD_RGBPACK( 0, 255, 0),
|
|
LCD_RGBPACK( 0, 0, 255),
|
|
},
|
|
/* Fill colors for sliders */
|
|
{
|
|
LCD_RGBPACK( 85, 0, 0),
|
|
LCD_RGBPACK( 0, 85, 0),
|
|
LCD_RGBPACK( 0, 0, 85),
|
|
},
|
|
};
|
|
|
|
/* maximum values for components */
|
|
static const unsigned char rgb_max[3] =
|
|
{
|
|
LCD_MAX_RED,
|
|
LCD_MAX_GREEN,
|
|
LCD_MAX_BLUE
|
|
};
|
|
|
|
/* Unpacks the color value into native rgb values and 24 bit rgb values */
|
|
static void unpack_rgb(struct rgb_pick *rgb)
|
|
{
|
|
unsigned color = rgb->color;
|
|
rgb->red = RGB_UNPACK_RED(color);
|
|
rgb->green = RGB_UNPACK_GREEN(color);
|
|
rgb->blue = RGB_UNPACK_BLUE(color);
|
|
rgb->r = RGB_UNPACK_RED_LCD(color);
|
|
rgb->g = RGB_UNPACK_GREEN_LCD(color);
|
|
rgb->b = RGB_UNPACK_BLUE_LCD(color);
|
|
}
|
|
|
|
/* Packs the native rgb colors into a color value */
|
|
static inline void pack_rgb(struct rgb_pick *rgb)
|
|
{
|
|
rgb->color = LCD_RGBPACK_LCD(rgb->r, rgb->g, rgb->b);
|
|
}
|
|
|
|
/* Returns LCD_BLACK if the color is above a threshold brightness
|
|
else return LCD_WHITE */
|
|
static inline unsigned get_black_or_white(const struct rgb_pick *rgb)
|
|
{
|
|
return (2*rgb->red + 5*rgb->green + rgb->blue) >= 1024 ?
|
|
LCD_BLACK : LCD_WHITE;
|
|
}
|
|
|
|
#define MARGIN_TOP 2 /* Top margin of screen */
|
|
#define MARGIN_BOTTOM 6 /* Bottom margin of screen */
|
|
#define SLIDER_TEXT_MARGIN 2 /* Gap between text and sliders */
|
|
#define TITLE_MARGIN_BOTTOM 4 /* Space below title bar */
|
|
#define SELECTOR_TB_MARGIN 1 /* Margin on top and bottom of selector */
|
|
#define SWATCH_TOP_MARGIN 4 /* Space between last slider and swatch */
|
|
#define SELECTOR_WIDTH get_icon_width(display->screen_type)
|
|
#define SELECTOR_HEIGHT 8 /* Height of > and < bitmaps */
|
|
|
|
/* dunno why lcd_set_drawinfo should be left out of struct screen */
|
|
static void set_drawinfo(struct screen *display, int mode,
|
|
unsigned foreground, unsigned background)
|
|
{
|
|
display->set_drawmode(mode);
|
|
if (display->depth > 1)
|
|
{
|
|
display->set_foreground(foreground);
|
|
display->set_background(background);
|
|
}
|
|
}
|
|
|
|
/* Figure out widest label character in case they vary -
|
|
this function assumes labels are one character */
|
|
static int label_get_max_width(struct screen *display)
|
|
{
|
|
int max_width, i;
|
|
char buf[4];
|
|
for (i = 0, max_width = 0; i < 3; i++)
|
|
{
|
|
int width;
|
|
buf[0] = str(LANG_COLOR_RGB_LABELS)[i];
|
|
buf[1] = '\0';
|
|
width = display->getstringsize(buf, NULL, NULL);
|
|
if (width > max_width)
|
|
max_width = width;
|
|
}
|
|
return max_width;
|
|
}
|
|
|
|
static void draw_screen(struct screen *display, char *title,
|
|
struct rgb_pick *rgb, int row)
|
|
{
|
|
unsigned text_color = LCD_BLACK;
|
|
unsigned background_color = LCD_WHITE;
|
|
char buf[32];
|
|
int i, char_height, line_height;
|
|
int max_label_width;
|
|
int text_x, text_top;
|
|
int slider_x, slider_width;
|
|
int value_width;
|
|
bool display_three_rows;
|
|
struct viewport vp;
|
|
|
|
viewport_set_defaults(&vp, display->screen_type);
|
|
struct viewport * last_vp = display->set_viewport(&vp);
|
|
|
|
display->clear_viewport();
|
|
|
|
if (display->depth > 1)
|
|
{
|
|
text_color = display->get_foreground();
|
|
background_color = display->get_background();
|
|
}
|
|
|
|
/* Draw title string */
|
|
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
|
|
vp.flags |= VP_FLAG_ALIGN_CENTER;
|
|
display->putsxy(0, MARGIN_TOP, title);
|
|
|
|
/* Get slider positions and top starting position */
|
|
max_label_width = label_get_max_width(display);
|
|
char_height = display->getcharheight();
|
|
text_top = MARGIN_TOP + char_height +
|
|
TITLE_MARGIN_BOTTOM + SELECTOR_TB_MARGIN;
|
|
text_x = SELECTOR_WIDTH;
|
|
slider_x = text_x + max_label_width + SLIDER_TEXT_MARGIN;
|
|
slider_width = vp.width - text_x - slider_x - SLIDER_TEXT_MARGIN;
|
|
if (display->depth >= 24)
|
|
display->getstringsize("255", &value_width, NULL);
|
|
else
|
|
display->getstringsize("63", &value_width, NULL);
|
|
slider_width -= value_width;
|
|
line_height = char_height + 2*SELECTOR_TB_MARGIN;
|
|
|
|
/* Find out if there's enough room for three sliders or just
|
|
enough to display the selected slider - calculate total height
|
|
of display with three sliders present */
|
|
display_three_rows =
|
|
vp.height >=
|
|
text_top + line_height*3 + /* Title + 3 sliders */
|
|
SWATCH_TOP_MARGIN + /* at least 2 lines */
|
|
char_height*2 + /* + margins for bottom */
|
|
MARGIN_BOTTOM; /* colored rectangle */
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
unsigned sb_flags = HORIZONTAL;
|
|
int mode = DRMODE_SOLID;
|
|
unsigned fg = text_color;
|
|
unsigned bg = background_color;
|
|
|
|
if (i == row)
|
|
{
|
|
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
|
|
|
|
if (global_settings.cursor_style != 0)
|
|
{
|
|
/* Draw solid bar selection bar */
|
|
display->fillrect(0,
|
|
text_top - SELECTOR_TB_MARGIN,
|
|
vp.width,
|
|
char_height + SELECTOR_TB_MARGIN*2);
|
|
|
|
if (display->depth < 16)
|
|
{
|
|
sb_flags |= FOREGROUND | INNER_FILL;
|
|
mode |= DRMODE_INVERSEVID;
|
|
}
|
|
}
|
|
else if (display_three_rows)
|
|
{
|
|
/* Draw "> <" around sliders */
|
|
int top = text_top + (char_height - SELECTOR_HEIGHT) / 2;
|
|
screen_put_iconxy(display, 0, top, Icon_Cursor);
|
|
screen_put_iconxy(display,
|
|
vp.width - SELECTOR_WIDTH,
|
|
top, Icon_Cursor);
|
|
}
|
|
|
|
if (display->depth >= 16)
|
|
{
|
|
sb_flags |= FOREGROUND | INNER_BGFILL;
|
|
mode = DRMODE_FG;
|
|
fg = prim_rgb[SB_PRIM][i];
|
|
bg = prim_rgb[SB_FILL][i];
|
|
}
|
|
}
|
|
else if (!display_three_rows)
|
|
continue;
|
|
|
|
set_drawinfo(display, mode, fg, bg);
|
|
|
|
/* Draw label */
|
|
buf[0] = str(LANG_COLOR_RGB_LABELS)[i];
|
|
buf[1] = '\0';
|
|
vp.flags &= ~VP_FLAG_ALIGNMENT_MASK;
|
|
display->putsxy(text_x, text_top, buf);
|
|
/* Draw color value */
|
|
if (display->depth >= 24)
|
|
snprintf(buf, 4, "%03d", rgb->rgb_val[i] & 0xFF);
|
|
else
|
|
snprintf(buf, 3, "%02d", rgb->rgb_val[i] & 0x3F);
|
|
vp.flags |= VP_FLAG_ALIGN_RIGHT;
|
|
display->putsxy(text_x, text_top, buf);
|
|
|
|
/* Draw scrollbar */
|
|
gui_scrollbar_draw(display, /* screen */
|
|
slider_x, /* x */
|
|
text_top + char_height / 4, /* y */
|
|
slider_width, /* width */
|
|
char_height / 2, /* height */
|
|
rgb_max[i], /* items */
|
|
0, /* min_shown */
|
|
rgb->rgb_val[i], /* max_shown */
|
|
sb_flags); /* flags */
|
|
|
|
/* Advance to next line */
|
|
text_top += line_height;
|
|
} /* end for */
|
|
|
|
/* Format RGB: #rrggbb */
|
|
snprintf(buf, sizeof(buf), str(LANG_COLOR_RGB_VALUE),
|
|
rgb->red, rgb->green, rgb->blue);
|
|
vp.flags |= VP_FLAG_ALIGN_CENTER;
|
|
if (display->depth >= 16)
|
|
{
|
|
/* Display color swatch on color screens only */
|
|
int top = text_top + SWATCH_TOP_MARGIN;
|
|
int width = vp.width - text_x*2;
|
|
int height = vp.height - top - MARGIN_BOTTOM;
|
|
|
|
/* Only draw if room */
|
|
if (height >= char_height + 2)
|
|
{
|
|
/* draw the big rectangle */
|
|
display->set_foreground(rgb->color);
|
|
display->fillrect(text_x, top, width, height);
|
|
|
|
/* Draw RGB: #rrggbb in middle of swatch */
|
|
set_drawinfo(display, DRMODE_FG, get_black_or_white(rgb),
|
|
background_color);
|
|
|
|
display->putsxy(0, top + (height - char_height) / 2, buf);
|
|
|
|
/* Draw border around the rect */
|
|
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
|
|
display->drawrect(text_x, top, width, height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Display RGB value only centered on remaining display if room */
|
|
int top = text_top + SWATCH_TOP_MARGIN;
|
|
int height = vp.height - top - MARGIN_BOTTOM;
|
|
|
|
if (height >= char_height)
|
|
{
|
|
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
|
|
display->putsxy(0, top + (height - char_height) / 2, buf);
|
|
}
|
|
}
|
|
|
|
display->update_viewport();
|
|
display->set_viewport(last_vp);
|
|
}
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
static int touchscreen_slider(struct screen *display,
|
|
struct rgb_pick *rgb,
|
|
int *selected_slider)
|
|
{
|
|
short x, y;
|
|
int char_height, line_height;
|
|
int max_label_width, value_width;
|
|
int text_top, slider_x, slider_width;
|
|
bool display_three_rows;
|
|
int button;
|
|
int pressed_slider;
|
|
struct viewport vp;
|
|
|
|
viewport_set_defaults(&vp, display->screen_type);
|
|
struct viewport *last_vp = display->set_viewport(&vp);
|
|
|
|
button = action_get_touchscreen_press_in_vp(&x, &y, &vp);
|
|
if (button == ACTION_UNKNOWN || button == BUTTON_NONE)
|
|
return ACTION_NONE;
|
|
/* Get slider positions and top starting position
|
|
* need vp.y here, because of the statusbar, since touchscreen
|
|
* coordinates are absolute */
|
|
max_label_width = label_get_max_width(display);
|
|
char_height = display->getcharheight();
|
|
text_top = MARGIN_TOP + char_height +
|
|
TITLE_MARGIN_BOTTOM + SELECTOR_TB_MARGIN;
|
|
slider_x = SELECTOR_WIDTH + max_label_width + SLIDER_TEXT_MARGIN;
|
|
slider_width = vp.width - SELECTOR_WIDTH - slider_x - SLIDER_TEXT_MARGIN;
|
|
if (display->depth >= 24)
|
|
display->getstringsize("255", &value_width, NULL);
|
|
else
|
|
display->getstringsize("63", &value_width, NULL);
|
|
slider_width -= value_width;
|
|
line_height = char_height + 2*SELECTOR_TB_MARGIN;
|
|
|
|
/* same logic as in draw_screen */
|
|
/* Find out if there's enough room for three sliders or just
|
|
enough to display the selected slider - calculate total height
|
|
of display with three sliders present */
|
|
display_three_rows =
|
|
vp.height >=
|
|
text_top + line_height*3 + /* Title + 3 sliders */
|
|
SWATCH_TOP_MARGIN + /* at least 2 lines */
|
|
char_height*2 + /* + margins for bottom */
|
|
MARGIN_BOTTOM; /* colored rectangle */
|
|
|
|
display->set_viewport(last_vp);
|
|
|
|
if (y < text_top)
|
|
{
|
|
if (button == BUTTON_REL)
|
|
return ACTION_STD_CANCEL;
|
|
else
|
|
return ACTION_NONE;
|
|
}
|
|
|
|
if (y >= text_top + line_height * (display_three_rows ? 3:1))
|
|
{ /* touching the color area means accept */
|
|
if (button == BUTTON_REL)
|
|
return ACTION_STD_OK;
|
|
else
|
|
return ACTION_NONE;
|
|
}
|
|
/* y is relative to the original viewport */
|
|
pressed_slider = (y - text_top)/line_height;
|
|
if (pressed_slider != *selected_slider)
|
|
*selected_slider = pressed_slider;
|
|
/* add max_label_width to overcome integer division limits,
|
|
* cap value later since that may lead to an overflow */
|
|
if (x < slider_x + (slider_width+max_label_width) && x > slider_x)
|
|
{
|
|
char computed_val;
|
|
x -= slider_x;
|
|
computed_val = (x*rgb_max[pressed_slider]/(slider_width));
|
|
rgb->rgb_val[pressed_slider] =
|
|
MIN(computed_val,rgb_max[pressed_slider]);
|
|
pack_rgb(rgb);
|
|
}
|
|
return ACTION_NONE;
|
|
}
|
|
#endif
|
|
/***********
|
|
set_color
|
|
returns true if USB was inserted, false otherwise
|
|
color is a pointer to the colour (in native format) to modify
|
|
set banned_color to -1 to allow all
|
|
***********/
|
|
bool set_color(struct screen *display, char *title,
|
|
unsigned *color, unsigned banned_color)
|
|
{
|
|
int exit = 0, slider = 0;
|
|
struct rgb_pick rgb;
|
|
|
|
rgb.color = *color;
|
|
|
|
while (!exit)
|
|
{
|
|
int button;
|
|
|
|
unpack_rgb(&rgb);
|
|
|
|
if (display != NULL)
|
|
{
|
|
draw_screen(display, title, &rgb, slider);
|
|
}
|
|
else
|
|
{
|
|
FOR_NB_SCREENS(i)
|
|
draw_screen(&screens[i], title, &rgb, slider);
|
|
}
|
|
|
|
button = get_action(CONTEXT_SETTINGS_COLOURCHOOSER, TIMEOUT_BLOCK);
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
if (button == ACTION_TOUCHSCREEN
|
|
&& display->screen_type == SCREEN_MAIN)
|
|
button = touchscreen_slider(display, &rgb, &slider);
|
|
#endif
|
|
|
|
switch (button)
|
|
{
|
|
case ACTION_STD_PREV:
|
|
case ACTION_STD_PREVREPEAT:
|
|
slider = (slider + 2) % 3;
|
|
break;
|
|
|
|
case ACTION_STD_NEXT:
|
|
case ACTION_STD_NEXTREPEAT:
|
|
slider = (slider + 1) % 3;
|
|
break;
|
|
|
|
case ACTION_SETTINGS_INC:
|
|
case ACTION_SETTINGS_INCREPEAT:
|
|
if (rgb.rgb_val[slider] < rgb_max[slider])
|
|
rgb.rgb_val[slider]++;
|
|
pack_rgb(&rgb);
|
|
break;
|
|
|
|
case ACTION_SETTINGS_DEC:
|
|
case ACTION_SETTINGS_DECREPEAT:
|
|
if (rgb.rgb_val[slider] > 0)
|
|
rgb.rgb_val[slider]--;
|
|
pack_rgb(&rgb);
|
|
break;
|
|
|
|
case ACTION_STD_OK:
|
|
if (banned_color != (unsigned)-1 &&
|
|
banned_color == rgb.color)
|
|
{
|
|
splash(HZ*2, ID2P(LANG_COLOR_UNACCEPTABLE));
|
|
break;
|
|
}
|
|
*color = rgb.color;
|
|
exit = 1;
|
|
break;
|
|
|
|
case ACTION_STD_CANCEL:
|
|
exit = 1;
|
|
break;
|
|
|
|
default:
|
|
if (default_event_handler(button) == SYS_USB_CONNECTED)
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|