/* * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "gfx_common.h" #include "widgets.h" #include "libs/strlib.h" WIDGET *widget_focus = NULL; /* Some basic color defines */ #define WIDGET_ACTIVE_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x0E) #define WIDGET_INACTIVE_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x18, 0x18, 0x1F), 0x00) #define WIDGET_INACTIVE_SELECTED_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F) #define WIDGET_CURSOR_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00) #define WIDGET_DIALOG_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x07) #define WIDGET_DIALOG_TEXT_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00) static Color win_bg_clr = BUILD_COLOR (MAKE_RGB15_INIT (0x18, 0x18, 0x1F), 0x00); static Color win_medium_clr = BUILD_COLOR (MAKE_RGB15_INIT (0x10, 0x10, 0x18), 0x00); static Color win_dark_clr = BUILD_COLOR (MAKE_RGB15_INIT (0x08, 0x08, 0x10), 0x00); static FONT cur_font; void DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium) { RECT t; Color oldcolor; BatchGraphics (); t.corner.x = r->corner.x - 2; t.corner.y = r->corner.y - 2; t.extent.width = r->extent.width + 4; t.extent.height = r->extent.height + 4; oldcolor = SetContextForeGroundColor (dark); DrawFilledRectangle (&t); t.corner.x += 2; t.corner.y += 2; t.extent.width -= 2; t.extent.height -= 2; SetContextForeGroundColor (medium); DrawFilledRectangle (&t); t.corner.x -= 1; t.corner.y += r->extent.height + 1; t.extent.height = 1; DrawFilledRectangle (&t); t.corner.x += r->extent.width + 2; t.corner.y -= r->extent.height + 2; t.extent.width = 1; DrawFilledRectangle (&t); SetContextForeGroundColor (bg); DrawFilledRectangle (r); SetContextForeGroundColor (oldcolor); UnbatchGraphics (); } // windowRect, if not NULL, will be filled with the dimensions of the // window drawn. void DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect) { Color oldfg = SetContextForeGroundColor (WIDGET_DIALOG_TEXT_COLOR); FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); RECT r; TEXT t; int i, win_w, win_h; if (cur_font) oldfont = SetContextFont (cur_font); /* Compute the dimensions of the label */ win_h = label->height ((WIDGET *)label) + 16; win_w = 0; for (i = 0; i < label->line_count; i++) { int len = utf8StringCount (label->lines[i]); if (len > win_w) { win_w = len; } } win_w = (win_w * 6) + 16; BatchGraphics (); r.corner.x = (ScreenWidth - win_w) >> 1; r.corner.y = (ScreenHeight - win_h) >> 1; r.extent.width = win_w; r.extent.height = win_h; DrawShadowedBox (&r, win_bg_clr, win_dark_clr, win_medium_clr); t.baseline.x = r.corner.x + (r.extent.width >> 1); t.baseline.y = r.corner.y + 16; for (i = 0; i < label->line_count; i++) { t.pStr = label->lines[i]; t.align = ALIGN_CENTER; t.CharCount = (COUNT)~0; font_DrawText (&t); t.baseline.y += 8; } UnbatchGraphics (); SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldfg); if (windowRect != NULL) { // Add the outer border added by DrawShadowedBox. // XXX: It may be nicer to add a border size parameter to // DrawShadowedBox, instead of assuming 2 here. windowRect->corner.x = r.corner.x - 2; windowRect->corner.y = r.corner.y - 2; windowRect->extent.width = r.extent.width + 4; windowRect->extent.height = r.extent.height + 4; } } void Widget_SetWindowColors (Color bg, Color dark, Color medium) { win_bg_clr = bg; win_dark_clr = dark; win_medium_clr = medium; } FONT Widget_SetFont (FONT newFont) { FONT oldFont = cur_font; cur_font = newFont; return oldFont; } static void Widget_DrawToolTips (int numlines, const char **tips) { RECT r; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR); TEXT t; int i; if (cur_font) oldfont = SetContextFont (cur_font); r.corner.x = 2; r.corner.y = 2; r.extent.width = ScreenWidth - 4; r.extent.height = ScreenHeight - 4; t.align = ALIGN_CENTER; t.CharCount = ~0; t.baseline.x = r.corner.x + (r.extent.width >> 1); t.baseline.y = r.corner.y + (r.extent.height - 8 - 8 * numlines); for (i = 0; i < numlines; i++) { t.pStr = tips[i]; font_DrawText(&t); t.baseline.y += 8; } SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); } void Widget_DrawMenuScreen (WIDGET *_self, int x, int y) { RECT r; Color title, oldtext; Color inactive, default_color, selected; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; int widget_index, height, widget_y; WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; if (cur_font) oldfont = SetContextFont (cur_font); r.corner.x = 2; r.corner.y = 2; r.extent.width = ScreenWidth - 4; r.extent.height = ScreenHeight - 4; title = WIDGET_INACTIVE_SELECTED_COLOR; selected = WIDGET_ACTIVE_COLOR; inactive = WIDGET_INACTIVE_COLOR; default_color = title; DrawStamp (&self->bgStamp); oldtext = SetContextForeGroundColor (title); t.baseline.x = r.corner.x + (r.extent.width >> 1); t.baseline.y = r.corner.y + 8; t.pStr = self->title; t.align = ALIGN_CENTER; t.CharCount = ~0; font_DrawText (&t); t.baseline.y += 8; t.pStr = self->subtitle; font_DrawText (&t); height = 0; for (widget_index = 0; widget_index < self->num_children; widget_index++) { WIDGET *child = self->child[widget_index]; height += (*child->height)(child); height += 8; /* spacing */ } height -= 8; widget_y = (ScreenHeight - height) >> 1; for (widget_index = 0; widget_index < self->num_children; widget_index++) { WIDGET *c = self->child[widget_index]; (*c->draw)(c, 0, widget_y); widget_y += (*c->height)(c) + 8; } SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); (void) x; (void) y; } void Widget_DrawChoice (WIDGET *_self, int x, int y) { WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self; Color oldtext; Color inactive, default_color, selected; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; int i, home_x, home_y; if (cur_font) oldfont = SetContextFont (cur_font); default_color = WIDGET_INACTIVE_SELECTED_COLOR; selected = WIDGET_ACTIVE_COLOR; inactive = WIDGET_INACTIVE_COLOR; t.baseline.x = x; t.baseline.y = y; t.align = ALIGN_LEFT; t.CharCount = ~0; t.pStr = self->category; if (widget_focus == _self) { oldtext = SetContextForeGroundColor (selected); } else { oldtext = SetContextForeGroundColor (default_color); } font_DrawText (&t); home_x = t.baseline.x + 3 * (ScreenWidth / ((self->maxcolumns + 1) * 2)); home_y = t.baseline.y; t.align = ALIGN_CENTER; for (i = 0; i < self->numopts; i++) { t.baseline.x = home_x + ((i % 3) * (ScreenWidth / (self->maxcolumns + 1))); t.baseline.y = home_y + (8 * (i / 3)); t.pStr = self->options[i].optname; if ((widget_focus == _self) && (self->highlighted == i)) { SetContextForeGroundColor (selected); Widget_DrawToolTips (3, self->options[i].tooltip); } else if (i == self->selected) { SetContextForeGroundColor (default_color); } else { SetContextForeGroundColor (inactive); } font_DrawText (&t); } SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); } void Widget_DrawButton (WIDGET *_self, int x, int y) { WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self; Color oldtext; Color inactive, selected; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; if (cur_font) oldfont = SetContextFont (cur_font); selected = WIDGET_ACTIVE_COLOR; inactive = WIDGET_INACTIVE_COLOR; t.baseline.x = 160; t.baseline.y = y; t.align = ALIGN_CENTER; t.CharCount = ~0; t.pStr = self->name; if (widget_focus == _self) { Widget_DrawToolTips (3, self->tooltip); oldtext = SetContextForeGroundColor (selected); } else { oldtext = SetContextForeGroundColor (inactive); } font_DrawText (&t); SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); (void) x; } void Widget_DrawLabel (WIDGET *_self, int x, int y) { WIDGET_LABEL *self = (WIDGET_LABEL *)_self; Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR); FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; int i; if (cur_font) oldfont = SetContextFont (cur_font); t.baseline.x = 160; t.baseline.y = y; t.align = ALIGN_CENTER; t.CharCount = ~0; for (i = 0; i < self->line_count; i++) { t.pStr = self->lines[i]; font_DrawText (&t); t.baseline.y += 8; } SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); (void) x; } void Widget_DrawSlider(WIDGET *_self, int x, int y) { WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self; Color oldtext; Color inactive, default_color, selected; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; RECT r; int tick = (ScreenWidth - x) / 8; if (cur_font) oldfont = SetContextFont (cur_font); default_color = WIDGET_INACTIVE_SELECTED_COLOR; selected = WIDGET_ACTIVE_COLOR; inactive = WIDGET_INACTIVE_COLOR; t.baseline.x = x; t.baseline.y = y; t.align = ALIGN_LEFT; t.CharCount = ~0; t.pStr = self->category; if (widget_focus == _self) { Widget_DrawToolTips (3, self->tooltip); oldtext = SetContextForeGroundColor (selected); } else { oldtext = SetContextForeGroundColor (default_color); } font_DrawText (&t); r.corner.x = t.baseline.x + 3 * tick; r.corner.y = t.baseline.y - 4; r.extent.height = 2; r.extent.width = 3 * tick; DrawFilledRectangle (&r); r.extent.width = 3; r.extent.height = 8; r.corner.y = t.baseline.y - 7; r.corner.x = t.baseline.x + 3 * tick + (3 * tick * (self->value - self->min) / (self->max - self->min)) - 1; DrawFilledRectangle (&r); (*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y); SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); } void Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y) { TEXT t; char buffer[16]; sprintf (buffer, "%d", self->value); t.baseline.x = x; t.baseline.y = y; t.align = ALIGN_CENTER; t.CharCount = ~0; t.pStr = buffer; font_DrawText (&t); } void Widget_DrawTextEntry (WIDGET *_self, int x, int y) { WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self; Color oldtext; Color inactive, default_color, selected; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; if (cur_font) oldfont = SetContextFont (cur_font); default_color = WIDGET_INACTIVE_SELECTED_COLOR; selected = WIDGET_ACTIVE_COLOR; inactive = WIDGET_INACTIVE_COLOR; BatchGraphics (); t.baseline.x = x; t.baseline.y = y; t.align = ALIGN_LEFT; t.CharCount = ~0; t.pStr = self->category; if (widget_focus == _self) { oldtext = SetContextForeGroundColor (selected); } else { oldtext = SetContextForeGroundColor (default_color); } font_DrawText (&t); /* Force string termination */ self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0; t.baseline.y = y; t.CharCount = utf8StringCount (self->value); t.pStr = self->value; if (!(self->state & WTE_EDITING)) { // normal or selected state t.baseline.x = 160; t.align = ALIGN_CENTER; if (widget_focus == _self) { oldtext = SetContextForeGroundColor (selected); } else { oldtext = SetContextForeGroundColor (inactive); } font_DrawText (&t); } else { // editing state COUNT i; RECT text_r; BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH]; BYTE *pchar_deltas; RECT r; SIZE leading; t.baseline.x = 90; t.align = ALIGN_LEFT; // calc background box dimensions // XXX: this may need some tuning, especially if a // different font is used. The font 'leading' values // are not what they should be. #define BOX_VERT_OFFSET 2 GetContextFontLeading (&leading); r.corner.x = t.baseline.x - 1; r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET; r.extent.width = ScreenWidth - r.corner.x - 10; r.extent.height = leading + 2; TextRect (&t, &text_r, char_deltas); #if 0 // XXX: this should potentially be used in ChangeCallback if ((text_r.extent.width + 2) >= r.extent.width) { // the text does not fit the input box size and so // will not fit when displayed later UnbatchGraphics (); // disallow the change return (FALSE); } #endif oldtext = SetContextForeGroundColor (selected); DrawFilledRectangle (&r); // calculate the cursor position and draw it pchar_deltas = char_deltas; for (i = self->cursor_pos; i > 0; --i) r.corner.x += (SIZE)*pchar_deltas++; if (self->cursor_pos < t.CharCount) /* cursor mid-line */ --r.corner.x; if (self->state & WTE_BLOCKCUR) { // Use block cursor for keyboardless systems if (self->cursor_pos == t.CharCount) { // cursor at end-line -- use insertion point r.extent.width = 1; } else if (self->cursor_pos + 1 == t.CharCount) { // extra pixel for last char margin r.extent.width = (SIZE)*pchar_deltas + 2; } else { // normal mid-line char r.extent.width = (SIZE)*pchar_deltas + 1; } } else { // Insertion point cursor r.extent.width = 1; } // position cursor within input field rect ++r.corner.x; ++r.corner.y; r.extent.height -= 2; SetContextForeGroundColor (WIDGET_CURSOR_COLOR); DrawFilledRectangle (&r); SetContextForeGroundColor (inactive); font_DrawText (&t); } UnbatchGraphics (); SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); } void Widget_DrawControlEntry (WIDGET *_self, int x, int y) { WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self; Color oldtext; Color inactive, default_color, selected; FONT oldfont = 0; FRAME oldFontEffect = SetContextFontEffect (NULL); TEXT t; int i, home_x, home_y; if (cur_font) oldfont = SetContextFont (cur_font); default_color = WIDGET_INACTIVE_SELECTED_COLOR; selected = WIDGET_ACTIVE_COLOR; inactive = WIDGET_INACTIVE_COLOR; t.baseline.x = x; t.baseline.y = y; t.align = ALIGN_LEFT; t.CharCount = ~0; t.pStr = self->category; if (widget_focus == _self) { oldtext = SetContextForeGroundColor (selected); } else { oldtext = SetContextForeGroundColor (default_color); } font_DrawText (&t); // 3 * ScreenWidth / ((self->maxcolumns + 1) * 2)) as per CHOICE, but only two options. home_x = t.baseline.x + (ScreenWidth / 2); home_y = t.baseline.y; t.align = ALIGN_CENTER; for (i = 0; i < 2; i++) { t.baseline.x = home_x + ((i % 3) * (ScreenWidth / 3)); // self->maxcolumns + 1 as per CHOICE. t.baseline.y = home_y + (8 * (i / 3)); t.pStr = self->controlname[i]; if (!t.pStr[0]) { t.pStr = "---"; } if ((widget_focus == _self) && (self->highlighted == i)) { SetContextForeGroundColor (selected); } else { SetContextForeGroundColor (default_color); } font_DrawText (&t); } SetContextFontEffect (oldFontEffect); if (oldfont) SetContextFont (oldfont); SetContextForeGroundColor (oldtext); } int Widget_HeightChoice (WIDGET *_self) { return ((((WIDGET_CHOICE *)_self)->numopts + 2) / 3) * 8; } int Widget_HeightFullScreen (WIDGET *_self) { (void)_self; return ScreenHeight; } int Widget_HeightOneLine (WIDGET *_self) { (void)_self; return 8; } int Widget_HeightLabel (WIDGET *_self) { WIDGET_LABEL *self = (WIDGET_LABEL *)_self; return self->line_count * 8; } int Widget_WidthFullScreen (WIDGET *_self) { (void)_self; return ScreenWidth; } int Widget_ReceiveFocusSimple (WIDGET *_self, int event) { widget_focus = _self; (void)event; return TRUE; } int Widget_ReceiveFocusChoice (WIDGET *_self, int event) { WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self; widget_focus = _self; self->highlighted = self->selected; (void)event; return TRUE; } int Widget_ReceiveFocusControlEntry (WIDGET *_self, int event) { WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self; int oldval = 0; if (widget_focus->tag == WIDGET_TYPE_CONTROLENTRY) { oldval = ((WIDGET_CONTROLENTRY *)widget_focus)->highlighted; } widget_focus = _self; self->highlighted = oldval; (void)event; return TRUE; } int Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event) { WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; int x, last_x, dx; for (x = 0; x < self->num_children; x++) { self->child[x]->parent = _self; } if (event == WIDGET_EVENT_UP) { x = self->num_children - 1; dx = -1; last_x = -1; } else if (event == WIDGET_EVENT_DOWN) { x = 0; dx = 1; last_x = self->num_children; } else { /* Leave highlighted value the same */ WIDGET *child = self->child[self->highlighted]; child->receiveFocus (child, event); return TRUE; } for ( ; x != last_x; x += dx) { WIDGET *child = self->child[x]; if ((*child->receiveFocus)(child, event)) { self->highlighted = x; return TRUE; } } return FALSE; } int Widget_ReceiveFocusRefuseFocus (WIDGET *self, int event) { (void)self; (void)event; return FALSE; } int Widget_HandleEventIgnoreAll (WIDGET *self, int event) { (void)event; (void)self; return FALSE; } int Widget_HandleEventChoice (WIDGET *_self, int event) { WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self; switch (event) { case WIDGET_EVENT_LEFT: self->highlighted -= 1; if (self->highlighted < 0) self->highlighted = self->numopts - 1; return TRUE; case WIDGET_EVENT_RIGHT: self->highlighted += 1; if (self->highlighted >= self->numopts) self->highlighted = 0; return TRUE; case WIDGET_EVENT_SELECT: { int oldval = self->selected; self->selected = self->highlighted; if (self->onChange) { (*(self->onChange))(self, oldval); } return TRUE; } default: return FALSE; } } int Widget_HandleEventSlider (WIDGET *_self, int event) { WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self; switch (event) { case WIDGET_EVENT_LEFT: self->value -= self->step; if (self->value < self->min) self->value = self->min; return TRUE; case WIDGET_EVENT_RIGHT: self->value += self->step; if (self->value > self->max) self->value = self->max; return TRUE; default: return FALSE; } } int Widget_HandleEventMenuScreen (WIDGET *_self, int event) { WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; int x, last_x, dx; switch (event) { case WIDGET_EVENT_UP: dx = -1; break; case WIDGET_EVENT_DOWN: dx = 1; break; case WIDGET_EVENT_CANCEL: /* On cancel, shift focus to last element and send a SELECT. */ self->highlighted = self->num_children - 1; widget_focus = self->child[self->highlighted]; return (widget_focus->handleEvent)(widget_focus, WIDGET_EVENT_SELECT); default: return FALSE; } last_x = self->highlighted; x = self->highlighted + dx; while (x != last_x) { WIDGET *child; if (x == -1) x = self->num_children - 1; if (x == self->num_children) x = 0; child = self->child[x]; if ((*child->receiveFocus)(child, event)) { self->highlighted = x; return TRUE; } x += dx; } return FALSE; } int Widget_HandleEventTextEntry (WIDGET *_self, int event) { WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self; if (event == WIDGET_EVENT_SELECT) { if (!self->handleEventSelect) return FALSE; return (*self->handleEventSelect)(self); } return FALSE; } int Widget_HandleEventControlEntry (WIDGET *_self, int event) { WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self; if (event == WIDGET_EVENT_SELECT) { if (self->onChange) { (self->onChange)(self); return TRUE; } } if (event == WIDGET_EVENT_DELETE) { if (self->onDelete) { (self->onDelete)(self); return TRUE; } } if ((event == WIDGET_EVENT_RIGHT) || (event == WIDGET_EVENT_LEFT)) { self->highlighted = 1-self->highlighted; return TRUE; } return FALSE; } int Widget_Event (int event) { WIDGET *widget = widget_focus; while (widget != NULL) { if ((*widget->handleEvent)(widget, event)) return TRUE; widget = widget->parent; } return FALSE; }