Began creating the listbox widget. It's still just a structure.
[xuni.git] / graphics.c
bloba25e09ea591175f8dbd0e82cd4e0855b68094f4b
1 #include <stdlib.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include "SDL_gfxPrimitives.h"
5 #include "graphics.h"
6 #include "xuni.h"
8 static int pick_font_point(struct smode_t *smode);
9 static SDL_Surface *set_graphics_mode(struct smode_t *smode);
10 static enum boxtype_t get_box_type(struct smode_t *smode,
11 struct widget_t *widget);
12 static enum boxtype_t get_inv_box_type(struct smode_t *smode,
13 struct widget_t *widget);
14 static enum boxtype_t get_perm_in_box_type(struct smode_t *smode,
15 struct widget_t *widget);
17 void default_smode(struct smode_t *smode) {
18 smode->width = 640;
19 smode->height = 480;
20 smode->depth = 0;
21 smode->fullscreen = 0;
22 smode->focus = 2; /* !!! might not be true in windowed mode, not that the main menu cares */
23 smode->restrictfocus = SDL_GRAB_OFF;
25 clear_smode(smode);
28 void clear_smode(struct smode_t *smode) {
29 clear_sel(&smode->sel);
31 smode->active.widget = 0;
32 smode->active.id = (size_t)-1;
34 /*smode->button.button = 0;
35 smode->button.xp = -1;
36 smode->button.yp = -1;*/
38 /*smode->lastclick.button = 0;
39 smode->lastclick.xp = -1;
40 smode->lastclick.yp = -1;*/
43 void clear_sel(struct widget_sel_t *sel) {
44 sel->wasin = 0;
45 sel->clickin = 0;
46 sel->p.widget = 0;
47 sel->p.id = (size_t)-1;
50 SDL_Surface *init_sdl(struct smode_t *smode) {
51 SDL_Surface *screen;
53 if(SDL_Init(SDL_INIT_VIDEO /*| SDL_INIT_AUDIO*/) < 0) {
54 fatal_error("Can't init SDL", __FILE__, __LINE__);
57 load_icon();
59 screen = set_graphics_mode(smode);
60 if(!screen) fatal_error("Can't set graphics mode", __FILE__, __LINE__);
62 set_caption("Loading");
64 smode->restrictfocus = set_focus(smode->restrictfocus);
66 SDL_EnableUNICODE(0);
68 if(TTF_Init() < 0) fatal_error("Can't init SDL_ttf", __FILE__, __LINE__);
70 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
71 SDL_DEFAULT_REPEAT_INTERVAL);
73 return screen;
76 SDL_GrabMode set_focus(SDL_GrabMode mode) {
77 if(SDL_WM_GrabInput(SDL_GRAB_QUERY) != mode) {
78 return SDL_WM_GrabInput(mode);
81 return mode;
84 void quit_sdl(void) {
85 TTF_Quit();
86 SDL_Quit();
89 void free_resize_image(struct resize_image_t *image) {
90 SDL_FreeSurface(image->original);
91 SDL_FreeSurface(image->image);
94 void free_fonts(struct font_t *font) {
95 if(font->mono == font->sans) {
96 TTF_CloseFont(font->mono);
98 else {
99 TTF_CloseFont(font->mono);
100 TTF_CloseFont(font->sans);
104 void free_theme(struct theme_t *theme) {
105 enum boxtype_t x;
107 for(x = 0; x < BOXTYPES; x ++) {
108 free_resize_image(&theme->box[x].corners);
111 free_resize_image(&theme->check);
114 void load_icon(void) {
115 SDL_Surface *icon = SDL_LoadBMP(ICON_FILE);
117 if(icon) {
118 SDL_WM_SetIcon(icon, NULL);
119 SDL_FreeSurface(icon);
121 else {
122 print_warning("Can't open icon \"" ICON_FILE "\"",
123 __FILE__, __LINE__);
127 void load_fonts(struct font_t *font) {
128 font->mono = TTF_OpenFont(MONOSPACE_FONT, font->point);
129 font->sans = TTF_OpenFont(SANS_SERIF_FONT, font->point);
131 if(!font->mono && !font->sans) {
132 fatal_error("Can't open either of these fonts: monospace, sans serif",
133 __FILE__, __LINE__);
136 if(!font->mono) {
137 font->mono = font->sans;
138 print_warning("Can't open monospace font, "
139 "using sans serif font as its replacement", __FILE__, __LINE__);
141 if(!font->sans) {
142 font->sans = font->mono;
143 print_warning("Can't open sans serif font, "
144 "using monospace font as its replacement", __FILE__, __LINE__);
147 TTF_SetFontStyle(font->mono, TTF_STYLE_NORMAL);
148 TTF_SetFontStyle(font->sans, TTF_STYLE_NORMAL);
151 void load_theme(struct theme_t *theme, const char *path) {
152 enum boxtype_t x;
153 size_t len = strlen(path) + GUI_PATH_LEN;
154 /* gui/path/boxtype/8.3\0 */
155 char *filename = malloc(len + 1 + 8+1 + 8+1+3 + 1);
156 /* !!! use global declaration? */
157 char *btstr[] = {"out", "out_hov", "out_act", "in", "in_hov", "in_act"};
158 size_t btlen[] = {3, 7, 7, 2, 6, 6};
160 strcpy(filename, GUI_PATH);
161 strcpy(filename + GUI_PATH_LEN, path);
162 filename[len++] = '/';
164 for(x = 0; x < BOXTYPES; x ++) {
165 strcpy(filename + len, btstr[x]);
166 filename[len + btlen[x]] = '/';
167 strcpy(filename + len + btlen[x] + 1, "corners.png");
169 load_resize_image(&theme->box[x].corners, filename);
172 strcpy(filename + len, "check.png");
174 load_resize_image(&theme->check, filename);
176 free(filename);
179 static int pick_font_point(struct smode_t *smode) {
180 int mindim = (smode->width / 1.2 < smode->height ? smode->width / 1.2
181 : smode->height);
183 return (int)(mindim / 30.0 + 0.5);
186 SDL_Surface *load_image(const char *filename) {
187 SDL_Surface *image = IMG_Load(filename);
189 if(!image) print_warning("Can't open image", __FILE__, __LINE__);
191 return image;
194 void load_resize_image(struct resize_image_t *image, const char *filename) {
195 image->original = load_image(filename);
196 image->image = 0;
199 SDL_Surface *resize_screen(struct smode_t *smode, SDL_ResizeEvent *resize) {
200 smode->width = resize->w;
201 smode->height = resize->h;
203 return set_graphics_mode(smode);
206 int toggle_fullscreen(SDL_Surface **screen, struct smode_t *smode) {
207 SDL_Surface *nscreen;
209 smode->fullscreen = !smode->fullscreen;
211 if(SDL_WM_ToggleFullScreen(*screen)) return 0;
213 nscreen = set_graphics_mode(smode);
214 if(nscreen) {
215 *screen = nscreen;
216 return 0;
219 smode->fullscreen = !smode->fullscreen; /* undo toggling above */
220 print_warning("Can't toggle fullscreen", __FILE__, __LINE__);
222 return 1;
225 void reload_fonts(struct font_t *font, struct smode_t *smode) {
226 int np = pick_font_point(smode);
228 if(font->point != np) {
229 if(font->point >= 0) free_fonts(font);
231 font->point = np;
232 load_fonts(font);
236 void set_caption(const char *s) {
237 char wt[BUFSIZ] = TITLE_PREFIX;
239 strcat(wt, s);
241 SDL_WM_SetCaption(wt, NULL);
244 void resize_theme(struct theme_t *theme, struct smode_t *smode) {
245 enum boxtype_t x;
247 theme->boxw = smode->width / 30.0 / 2;
248 theme->boxh = smode->height / 30.0 / 2;
250 for(x = 0; x < BOXTYPES; x ++) {
251 if(!theme->box[x].corners.original) continue;
253 if(theme->box[x].corners.image) {
254 SDL_FreeSurface(theme->box[x].corners.image);
257 theme->box[x].corners.image =
258 zoomSurface(theme->box[x].corners.original,
259 smode->width / 30.0 / theme->box[x].corners.original->w,
260 smode->height / 30.0 / theme->box[x].corners.original->h,
261 SMOOTHING_ON);
264 if(theme->check.original) {
265 if(theme->check.image) {
266 SDL_FreeSurface(theme->check.image);
269 theme->check.image = zoomSurface(theme->check.original,
270 smode->width / 30.0 / theme->check.original->w,
271 smode->height / 30.0 / theme->check.original->h,
272 SMOOTHING_ON);
276 static SDL_Surface *set_graphics_mode(struct smode_t *smode) {
277 Uint32 flags = SDL_SWSURFACE | SDL_RESIZABLE;
278 if(smode->fullscreen) flags |= SDL_FULLSCREEN;
280 return SDL_SetVideoMode(smode->width, smode->height, smode->depth, flags);
283 void lock_screen(SDL_Surface *screen) {
284 if(SDL_MUSTLOCK(screen)) {
285 if(SDL_LockSurface(screen) < 0) {
286 print_warning("Can't lock screen", __FILE__, __LINE__);
291 void unlock_screen(SDL_Surface *screen) {
292 if(SDL_MUSTLOCK(screen)) {
293 SDL_UnlockSurface(screen);
297 void blit_surface(SDL_Surface *screen, SDL_Surface *image, int xp, int yp) {
298 SDL_Rect rect;
300 rect.x = xp;
301 rect.y = yp;
303 if(SDL_BlitSurface(image, NULL, screen, &rect) < 0) {
304 print_warning("Can't blit surface", __FILE__, __LINE__);
308 void blit_surface_area(SDL_Surface *screen, SDL_Surface *image,
309 int tx, int ty, int fx, int fy, int fw, int fh) {
311 SDL_Rect from, to;
313 from.x = fx;
314 from.y = fy;
315 from.w = fw;
316 from.h = fh;
317 to.x = tx;
318 to.y = ty;
320 if(SDL_BlitSurface(image, &from, screen, &to) < 0) {
321 print_warning("Can't blit surface", __FILE__, __LINE__);
325 void blit_surface_repeat(SDL_Surface *screen, SDL_Surface *image,
326 int xp, int yp, int w, int h) {
328 int x, y;
329 SDL_Rect srect, drect;
331 srect.x = 0;
332 srect.y = 0;
334 for(x = 0; x < w; x += image->w) {
335 for(y = 0; y < h; y += image->h) {
336 drect.x = xp + x;
337 drect.y = yp + y;
339 if(x + image->w > w) srect.w = w - x;
340 else srect.w = image->w;
341 if(y + image->h > h) srect.h = h - y;
342 else srect.h = image->h;
344 if(SDL_BlitSurface(image, &srect, screen, &drect) < 0) {
345 print_warning("Can't blit surface", __FILE__, __LINE__);
351 void blit_surface_repeat_area(SDL_Surface *screen, SDL_Surface *image,
352 int tx, int ty, int tw, int th, int fx, int fy, int fw, int fh) {
354 int x, y;
355 SDL_Rect srect, drect;
357 srect.x = fx;
358 srect.y = fy;
360 for(x = 0; x < tw; x += fw) {
361 for(y = 0; y < th; y += fh) {
362 drect.x = tx + x;
363 drect.y = ty + y;
365 if(x + fw > tw) srect.w = tw - x;
366 else srect.w = fw;
367 if(y + fh > th) srect.h = th - y;
368 else srect.h = fh;
370 if(SDL_BlitSurface(image, &srect, screen, &drect) < 0) {
371 print_warning("Can't blit surface", __FILE__, __LINE__);
377 void blit_surface_fill_from(SDL_Surface *screen, SDL_Surface *image,
378 int tx, int ty, int tw, int th, int fx1, int fy1) {
380 int x, y;
381 SDL_Rect srect, drect;
383 /*x = image->w - fx1 < tw ? image->w - fx1 : tw;
384 y = image->h - fy1 < th ? image->h - fy1 : th;
385 blit_surface_area(screen, image,
386 tx, ty, fx1, fy1, x, y);*/
388 for(x = fx1 - image->w; x < tw; x += image->w) {
389 for(y = fy1 - image->h; y < th; y += image->h) {
390 if(x < 0) {
391 drect.x = tx;
392 srect.x = -x;
393 srect.w = image->w - -x;
395 else {
396 drect.x = tx + x;
398 srect.x = 0;
399 if(x + image->w > tw) srect.w = tw - x;
400 else srect.w = image->w;
403 if(y < 0) {
404 drect.y = ty;
405 srect.y = -y;
406 srect.h = image->h - -y;
408 else {
409 drect.y = ty + y;
411 srect.y = 0;
412 if(y + image->h > th) srect.h = th - y;
413 else srect.h = image->h;
416 if(SDL_BlitSurface(image, &srect, screen, &drect) < 0) {
417 print_warning("Can't blit surface", __FILE__, __LINE__);
423 /*void fill_area(SDL_Surface *screen, int x, int y, int w, int h, Uint32 col) {
424 SDL_Rect r;
426 r.x = x;
427 r.y = y;
428 r.w = w;
429 r.h = h;
431 SDL_FillRect(screen, &r, col);
434 int in_rect(int xp, int yp, int x, int y, int w, int h) {
435 return xp >= x && xp < x + w && yp >= y && yp < y + h;
438 int in_sdl_rect(int xp, int yp, SDL_Rect *r) {
439 return xp >= r->x && xp < r->x + r->w && yp >= r->y && yp < r->y + r->h;
442 /*int two_in_rect(int x1, int y1, int x2, int y2, int x, int y, int w, int h) {
443 return in_rect(x1, y1, x, y, w, h) && in_rect(x2, y2, x, y, w, h);
446 static enum boxtype_t get_box_type(struct smode_t *smode,
447 struct widget_t *widget) {
449 int xp, yp;
450 Uint8 button = SDL_GetMouseState(&xp, &yp);
452 if(widget == smode->sel.p.widget) {
453 if(in_sdl_rect(xp, yp, &widget->pos)) {
454 if(smode->sel.clickin) return BOX_IN_ACTIVE;
456 return BOX_OUT_HOVER;
459 if(smode->sel.clickin) return BOX_OUT_ACTIVE;
462 return BOX_OUT;
465 static enum boxtype_t get_inv_box_type(struct smode_t *smode,
466 struct widget_t *widget) {
468 int xp, yp;
469 Uint8 button = SDL_GetMouseState(&xp, &yp);
471 if(widget == smode->sel.p.widget) {
472 if(in_sdl_rect(xp, yp, &widget->pos)) {
473 if(smode->sel.clickin) return BOX_OUT_ACTIVE;
475 return BOX_IN_HOVER;
478 if(smode->sel.clickin) return BOX_IN_ACTIVE;
481 return BOX_IN;
484 static enum boxtype_t get_perm_in_box_type(struct smode_t *smode,
485 struct widget_t *widget) {
487 int xp, yp;
488 Uint8 button = SDL_GetMouseState(&xp, &yp);
490 if(widget == smode->sel.p.widget) {
491 if(smode->sel.clickin) return BOX_IN_ACTIVE;
493 if(in_sdl_rect(xp, yp, &widget->pos)) {
494 return BOX_IN_HOVER;
498 if(widget == smode->active.widget) {
499 return BOX_IN_ACTIVE;
502 return BOX_IN;
505 int is_box_in(enum boxtype_t bt) {
506 return bt == BOX_IN || bt == BOX_IN_HOVER || bt == BOX_IN_ACTIVE;
509 int is_box_out(enum boxtype_t bt) {
510 return bt == BOX_OUT || bt == BOX_OUT_HOVER || bt == BOX_OUT_ACTIVE;
513 int is_box_normal(enum boxtype_t bt) {
514 return bt == BOX_IN || bt == BOX_OUT;
517 int is_box_hover(enum boxtype_t bt) {
518 return bt == BOX_IN_HOVER || bt == BOX_OUT_HOVER;
521 int is_box_active(enum boxtype_t bt) {
522 return bt == BOX_IN_ACTIVE || bt == BOX_OUT_ACTIVE;
525 enum boxtype_t paint_box(SDL_Surface *screen, struct smode_t *smode,
526 struct theme_t *theme, SDL_Surface *text, SDL_Rect *b,
527 enum boxtype_t bt, int centre) {
529 int x, y, cw, ch, a;
531 /*if(!b->text) return BOX_ERROR;*/
533 cw = b->w - theme->boxw * 2;
534 ch = b->h - theme->boxh * 2;
536 if(theme->box[bt].corners.image) {
537 if(theme->box[bt].corners.image->w % 2) cw --;
538 if(theme->box[bt].corners.image->h % 2) ch --;
540 /* upper left */
541 blit_surface_area(screen, theme->box[bt].corners.image, b->x, b->y,
542 0, 0, theme->boxw, theme->boxh);
544 /* lower left */
545 blit_surface_area(screen, theme->box[bt].corners.image,
546 b->x, b->y + theme->boxh + ch, 0, theme->boxh,
547 theme->boxw, theme->boxh);
549 /* upper right */
550 blit_surface_area(screen, theme->box[bt].corners.image,
551 b->x + theme->boxw + cw, b->y,
552 theme->boxw, 0, theme->boxw, theme->boxh);
554 /* lower right */
555 blit_surface_area(screen, theme->box[bt].corners.image,
556 b->x + theme->boxw + cw, b->y + theme->boxh + ch,
557 theme->boxw, theme->boxh, theme->boxw, theme->boxh);
559 /* top */
560 blit_surface_repeat_area(screen, theme->box[bt].corners.image,
561 b->x + theme->boxw, b->y, cw, theme->boxh,
562 theme->boxw, 0, 1, theme->boxh);
564 /* bottom */
565 blit_surface_repeat_area(screen, theme->box[bt].corners.image,
566 b->x + theme->boxw, b->y + theme->boxh + ch,
567 cw, theme->boxh, theme->boxw, theme->boxh,
568 1, theme->boxh);
570 /* left */
571 blit_surface_repeat_area(screen, theme->box[bt].corners.image,
572 b->x, b->y + theme->boxh, theme->boxw, ch,
573 0, theme->boxh, theme->boxw, 1);
575 /* right */
576 blit_surface_repeat_area(screen, theme->box[bt].corners.image,
577 b->x + theme->boxw + cw, b->y + theme->boxh,
578 theme->boxw, ch, theme->boxw, theme->boxh,
579 theme->boxw, 1);
581 /* middle */
582 blit_surface_repeat_area(screen, theme->box[bt].corners.image,
583 b->x + theme->boxw, b->y + theme->boxh,
584 cw, ch,
585 theme->boxw, theme->boxh, 1, 1);
587 if(text) {
588 if(text->w <= b->w && text->h <= b->h) {
589 /* text */
590 if(centre) {
591 x = b->x + theme->boxw + (cw - text->w) / 2;
592 y = b->y + theme->boxh + (ch - text->h) / 2;
594 if(is_box_in(bt)) x ++, y ++;
596 blit_surface(screen, text, x, y);
598 else {
599 x = b->x + theme->boxh + (ch - text->h) / 2;
600 y = b->y + theme->boxh + (ch - text->h) / 2;
602 a = text->w > cw ? text->w - cw : 0;
604 if(is_box_in(bt)) x ++, y ++;
606 blit_surface_area(screen, text, x, y, a, 0, text->w - a,
607 text->h);
608 y = /*TTF_FontHeight(font->sans)*/ 16;
609 vlineRGBA(screen, x + (text->w - a) + 1,
610 b->y + (b->h - y) / 2, b->y + (b->h + y) / 2,
611 255, 255, 255, 255);
614 else {
615 /* !!! shrink text and then blit it? Or maybe:
616 print_warning("Text too small for button", __FILE__, __LINE__); */
620 /*SDL_SaveBMP(theme->box[bt].corners.image, "temp/corners.bmp");*/
623 /* middle */
624 /*fill_area(screen, xp + theme->box[bt].corners.image->w / 2,
625 yp + theme->box[bt].corners.image->h / 2,
626 theme->box[bt].corners.image->w + text->w,
627 theme->box[bt].corners.image->h + text->h,
628 gp(theme->box[bt].corners.image,
629 theme->box[bt].corners.image->w / 2,
630 theme->box[bt].corners.image->h / 2));*/
632 return bt;
635 void init_button(struct widget_t *widget, TTF_Font *font, const char *text,
636 int x, int y, int w, int h) {
638 static const SDL_Color white = {255, 255, 255, 0};
640 widget->type = WIDGET_BUTTON;
641 widget->p.button = malloc(sizeof(*widget->p.button));
642 widget->p.button->text = TTF_RenderText_Blended(font, text, white);
643 widget->pos.x = x;
644 widget->pos.y = y;
645 widget->pos.w = w;
646 widget->pos.h = h;
649 void init_checkbox(struct widget_t *widget, TTF_Font *font, const char *text,
650 struct theme_t *theme, int checked, int x, int y, int w, int h, int bw) {
652 static const SDL_Color white = {255, 255, 255, 0};
654 widget->type = WIDGET_CHECKBOX;
655 widget->p.checkbox = malloc(sizeof(*widget->p.checkbox));
656 widget->p.checkbox->text = TTF_RenderText_Blended(font, text, white);
657 widget->p.checkbox->checked = checked;
658 widget->p.checkbox->bw = bw;
659 widget->pos.x = x;
660 widget->pos.y = y;
661 widget->pos.w = w;
662 widget->pos.h = h;
665 void init_textbox(struct widget_t *widget, TTF_Font *font, const char *data,
666 size_t len, struct theme_t *theme, int x, int y, int w, int h) {
668 static const SDL_Color white = {255, 255, 255, 0};
670 widget->type = WIDGET_TEXTBOX;
671 widget->p.textbox = malloc(sizeof(*widget->p.textbox));
672 widget->p.textbox->data = duplicate_string(data, len);
673 widget->p.textbox->len = len;
674 widget->p.textbox->text = TTF_RenderText_Blended(font, data, white);
675 widget->p.textbox->ra = 0;
676 widget->pos.x = x;
677 widget->pos.y = y;
678 widget->pos.w = w;
679 widget->pos.h = h;
682 size_t in_widget_array(int xp, int yp, struct widget_t *widget, size_t n) {
683 size_t x;
685 for(x = 0; x < n; x ++) {
686 if(in_sdl_rect(xp, yp, &widget[x].pos)) {
687 return x;
691 return (size_t)-1;
694 int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click,
695 size_t offset, struct widget_t *widget, size_t n) {
697 size_t x;
698 int r, v = 0;
700 if(sel->p.widget) {
701 r = in_sdl_rect(xp, yp, &sel->p.widget->pos);
703 if(sel->wasin != r) {
704 sel->wasin = r;
706 v = 1;
709 if(sel->clickin != click) {
710 sel->clickin = click;
712 v = 1;
715 if(v && !sel->clickin && !sel->wasin) clear_sel(sel);
717 if(!sel->p.widget) {
718 x = in_widget_array(xp, yp, widget, n);
720 if(x != (size_t)-1) {
721 sel->wasin = 1;
722 sel->clickin = click;
723 sel->p.widget = widget + x;
724 sel->p.id = x + offset;
726 return 1;
730 return v;
732 /*if(sel->wasin && x != (size_t)-1 && x + offset == sel->id) return 0;
733 if(!sel->wasin && x == (size_t)-1) return 0;*/
735 /*if(state == STATE_NORMAL_NORMAL || state == STATE_CLICK_NORMAL
736 || state == STATE_ACTIVE_NORMAL)) {
738 if(x != (size_t)-1) return 0;
740 else {
741 if(x == (size_t)-1) return 0;
744 sel->state = state;
745 sel->widget = widget + x;
746 sel->id = x + offset;
748 return 1;*/
751 enum boxtype_t paint_widget(SDL_Surface *screen, struct smode_t *smode,
752 struct theme_t *theme, struct widget_t *widget) {
754 enum boxtype_t bt = BOX_ERROR, t;
755 SDL_Rect r;
757 switch(widget->type) {
758 case WIDGET_BUTTON:
759 t = get_box_type(smode, widget);
760 bt = paint_box(screen, smode, theme,
761 widget->p.button->text, &widget->pos, t, 1);
762 break;
763 case WIDGET_CHECKBOX:
764 r = widget->pos;
765 r.w = widget->p.checkbox->bw;
767 if(!widget->p.checkbox->checked) {
768 t = get_box_type(smode, widget);
769 bt = paint_box(screen, smode, theme,
770 t == BOX_IN_ACTIVE ? theme->check.image : 0, &r, t, 1);
772 else {
773 t = get_inv_box_type(smode, widget);
774 bt = paint_box(screen, smode, theme,
775 t != BOX_OUT_ACTIVE ? theme->check.image : 0, &r, t, 1);
778 blit_surface(screen, widget->p.checkbox->text, r.x + r.w * 1.3,
779 r.y + (r.h - widget->p.checkbox->text->h) / 2);
780 break;
781 case WIDGET_TEXTBOX:
782 t = get_perm_in_box_type(smode, widget);
783 bt = paint_box(screen, smode, theme,
784 widget->p.textbox->text, &widget->pos, t, 0);
785 break;
786 default:
787 break;
790 return bt;
793 void perform_widget_click(struct widget_t *widget) {
794 switch(widget->type) {
795 case WIDGET_CHECKBOX:
796 widget->p.checkbox->checked = !widget->p.checkbox->checked;
797 break;
798 default:
799 break;
803 int render_restricted_text(SDL_Surface **text, const char *data,
804 TTF_Font *font, int w) {
806 static const SDL_Color white = {255, 255, 255, 0};
807 const char *p = data;
809 int tw;
811 if(*p) {
812 do {
813 TTF_SizeText(font, p ++, &tw, NULL);
814 } while(tw > w && *p);
816 p --;
817 if(p != data) {
818 *text = TTF_RenderText_Blended(font, p - 1, white);
820 return p - data;
824 *text = TTF_RenderText_Blended(font, p, white);
826 return 0;
829 int widget_process_character(struct widget_t *widget, SDL_keysym *sym,
830 struct font_t *font, struct theme_t *theme) {
832 /*if(sym->mod & KMOD_SHIFT) {
833 if(isalpha(c)) c = toupper(c);
834 else if(c) {
835 if((p = strchr(from, c))) {
836 c = to[p - from];
841 if(!(sym->unicode >> 7)) {
842 if(isprint(sym->unicode)) {
843 switch(widget->type) {
844 case WIDGET_TEXTBOX:
845 widget->p.textbox->data = realloc(widget->p.textbox->data,
846 widget->p.textbox->len + 2);
847 widget->p.textbox->data[widget->p.textbox->len]
848 = sym->unicode;
849 widget->p.textbox->data[++widget->p.textbox->len] = 0;
850 widget->p.textbox->ra = render_restricted_text(
851 &widget->p.textbox->text, widget->p.textbox->data,
852 font->sans, widget->pos.w - theme->boxw * 2);
853 break;
854 default:
855 break;
858 return 1;
860 else if(sym->unicode == '\b') {
861 switch(widget->type) {
862 case WIDGET_TEXTBOX:
863 if(widget->p.textbox->len) {
864 widget->p.textbox->data = realloc(widget->p.textbox->data,
865 widget->p.textbox->len + 1);
866 widget->p.textbox->data[--widget->p.textbox->len] = 0;
867 widget->p.textbox->ra = render_restricted_text(
868 &widget->p.textbox->text, widget->p.textbox->data,
869 font->sans, widget->pos.w - theme->boxw * 2);
871 else return 0;
873 break;
874 default:
875 break;
878 return 1;
882 return 0;
885 int widget_can_be_active(enum widget_type_t wt) {
886 return wt == WIDGET_TEXTBOX;
889 void enable_unicode(enum widget_type_t wt) {
890 int v = 0;
892 if(wt == WIDGET_TEXTBOX) {
893 v = 1;
896 if(SDL_EnableUNICODE(-1) != v) SDL_EnableUNICODE(v);
899 void paint_widget_array(SDL_Surface *screen, struct smode_t *smode,
900 struct theme_t *theme, struct widget_t *widget, size_t n) {
902 size_t x;
904 for(x = 0; x < n; x ++) {
905 paint_widget(screen, smode, theme, widget + x);
909 void free_widget_array(struct widget_t *widget, size_t n) {
910 size_t x;
912 for(x = 0; x < n; x ++) {
913 switch(widget[x].type) {
914 case WIDGET_BUTTON:
915 SDL_FreeSurface(widget[x].p.button->text);
916 break;
917 case WIDGET_CHECKBOX:
918 SDL_FreeSurface(widget[x].p.checkbox->text);
919 break;
920 case WIDGET_TEXTBOX:
921 free(widget[x].p.textbox->data);
922 SDL_FreeSurface(widget[x].p.textbox->text);
923 default:
924 break;
927 free(widget[x].p.all);