Atrinik Client  4.0
widget.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
44 #include <global.h>
45 #include <toolkit/string.h>
46 #include <network_graph.h>
47 #include <toolkit/path.h>
48 
49 static widgetdata def_widget[TOTAL_SUBWIDGETS];
50 static const char *const widget_names[TOTAL_SUBWIDGETS] = {
51  "map", "stat", "menu_buttons", "quickslots", "textwin", "playerdoll",
52  "playerinfo", "mapname", "input", "fps", "mplayer", "spells", "skills",
53  "party", "notification", "container", "label", "texture", "buddy",
54  "active_effects", "protections", "minimap", "target", "inventory",
55  "network_graph",
56 
57  "container_strip", "menu", "menuitem"
58 };
59 static void (*widget_initializers[TOTAL_SUBWIDGETS]) (widgetdata *);
60 
61 /* Default overall priority tree. Will change during runtime.
62  * Widget at the top (head) of the tree has highest priority.
63  * Events go to the top (head) of the tree first.
64  * Displaying goes to the right (foot) of the tree first. */
69 
74 /* TODO: change cur_widget to type_list_head */
76 static widgetdata *type_list_foot[TOTAL_SUBWIDGETS];
77 
83  NULL, 0, 0
84 };
85 
88  0, NULL, 0, 0
89 };
90 
93  0, NULL, 0, 0
94 };
95 
101 static int IsMouseExclusive = 0;
102 
106 static int widget_render_debug = 0;
107 
108 int widget_id_from_name(const char *name)
109 {
110  int i;
111 
112  for (i = 0; i < TOTAL_SUBWIDGETS; i++) {
113  if (strcmp(widget_names[i], name) == 0) {
114  return i;
115  }
116  }
117 
118  return -1;
119 }
120 
121 static int widget_load(const char *path, uint8_t defaults, widgetdata *widgets[])
122 {
123  FILE *fp;
124  char buf[HUGE_BUF], *end, *line, *cp;
125  widgetdata *widget;
126  int depth, old_depth;
127 
128  fp = path_fopen(path, "r");
129 
130  if (!fp) {
131  return 0;
132  }
133 
134  widget = NULL;
135  depth = old_depth = 0;
136 
137  while (fgets(buf, sizeof(buf), fp)) {
138  if (*buf == '#' || *buf == '\n') {
139  continue;
140  }
141 
142  end = strchr(buf, '\n');
143 
144  if (end) {
145  *end = '\0';
146  }
147 
148  old_depth = depth;
149  depth = 0;
150  line = buf;
151 
152  while (*line == '\t') {
153  depth++;
154  line++;
155  }
156 
157  if (string_startswith(line, "[") && string_endswith(line, "]")) {
158  int id;
159 
160  if (old_depth != 0) {
161  insert_widget_in_container(widgets[old_depth - 1], widget, 1);
162  }
163 
164  cp = string_sub(line, 1, -1);
165  id = widget_id_from_name(cp);
166  efree(cp);
167 
168  if (id == -1) {
169  /* Reset to NULL in case there was a valid widget previously,
170  * so that we don't load into it from this invalid one. */
171  widget = NULL;
172  LOG(DEBUG, "Invalid widget: %s", line);
173  continue;
174  }
175 
176  widget = defaults ? &def_widget[id] : create_widget_object(id);
177  widgets[depth] = widget;
178 
179  if (defaults) {
180  widget->required = 1;
181  widget->save = 1;
182  }
183  } else if (widget) {
184  char *cps[2];
185 
186  if (string_split(line, cps, arraysize(cps), '=') != arraysize(cps)) {
187  LOG(BUG, "Invalid line: %s", line);
188  continue;
189  }
190 
191  string_whitespace_trim(cps[0]);
192  string_whitespace_trim(cps[1]);
193 
194  if (strcmp(cps[0], "id") == 0) {
195  widget->id = estrdup(cps[1]);
196  } else if (strcmp(cps[0], "texture_type") == 0) {
197  widget->texture_type = atoi(cps[1]);
198  } else if (strcmp(cps[0], "bg") == 0) {
199  strncpy(widget->bg, cps[1], sizeof(widget->bg) - 1);
200  widget->bg[sizeof(widget->bg) - 1] = '\0';
201  } else if (strcmp(cps[0], "moveable") == 0) {
202  KEYWORD_TO_BOOLEAN(cps[1], widget->moveable);
203  } else if (strcmp(cps[0], "shown") == 0) {
204  KEYWORD_TO_BOOLEAN(cps[1], widget->show);
205  } else if (strcmp(cps[0], "resizeable") == 0) {
206  KEYWORD_TO_BOOLEAN(cps[1], widget->resizeable);
207  } else if (strcmp(cps[0], "required") == 0) {
208  KEYWORD_TO_BOOLEAN(cps[1], widget->required);
209  } else if (strcmp(cps[0], "save") == 0) {
210  KEYWORD_TO_BOOLEAN(cps[1], widget->save);
211  } else if (strcmp(cps[0], "x") == 0) {
212  widget->x = atoi(cps[1]);
213  } else if (strcmp(cps[0], "y") == 0) {
214  widget->y = atoi(cps[1]);
215  } else if (strcmp(cps[0], "w") == 0) {
216  resize_widget(widget, RESIZE_RIGHT, atoi(cps[1]));
217  } else if (strcmp(cps[0], "h") == 0) {
218  resize_widget(widget, RESIZE_BOTTOM, atoi(cps[1]));
219  } else if (strcmp(cps[0], "min_w") == 0) {
220  widget->min_w = atoi(cps[1]);
221  } else if (strcmp(cps[0], "min_h") == 0) {
222  widget->min_h = atoi(cps[1]);
223  } else if (widget->load_func && widget->load_func(widget, cps[0], cps[1])) {
224  } else {
225  LOG(BUG, "Invalid line: %s = %s", cps[0], cps[1]);
226  }
227  }
228  }
229 
230  fclose(fp);
231 
232  if (old_depth != 0) {
233  insert_widget_in_container(widgets[old_depth - 1], widget, 1);
234  }
235 
236  return 1;
237 }
238 
244 {
245  widgetdata * widgets[100];
246 
247  widget_initializers[ACTIVE_EFFECTS_ID] = widget_active_effects_init;
248  widget_initializers[BUDDY_ID] = widget_buddy_init;
249  widget_initializers[CONTAINER_ID] = widget_container_init;
250  widget_initializers[FPS_ID] = widget_fps_init;
251  widget_initializers[INPUT_ID] = widget_input_init;
252  widget_initializers[INVENTORY_ID] = widget_inventory_init;
253  widget_initializers[LABEL_ID] = widget_label_init;
254  widget_initializers[MAP_ID] = widget_map_init;
255  widget_initializers[MAPNAME_ID] = widget_mapname_init;
256  widget_initializers[MENU_B_ID] = widget_menu_buttons_init;
257  widget_initializers[MINIMAP_ID] = widget_minimap_init;
258  widget_initializers[MPLAYER_ID] = widget_mplayer_init;
259  widget_initializers[NETWORK_GRAPH_ID] = widget_network_graph_init;
260  widget_initializers[NOTIFICATION_ID] = widget_notification_init;
261  widget_initializers[PARTY_ID] = widget_party_init;
262  widget_initializers[PDOLL_ID] = widget_playerdoll_init;
263  widget_initializers[PLAYER_INFO_ID] = widget_playerinfo_init;
264  widget_initializers[PROTECTIONS_ID] = widget_protections_init;
265  widget_initializers[QUICKSLOT_ID] = widget_quickslots_init;
266  widget_initializers[SKILLS_ID] = widget_skills_init;
267  widget_initializers[SPELLS_ID] = widget_spells_init;
268  widget_initializers[STAT_ID] = widget_stat_init;
269  widget_initializers[TARGET_ID] = widget_target_init;
270  widget_initializers[TEXTURE_ID] = widget_texture_init;
271  widget_initializers[CHATWIN_ID] = widget_textwin_init;
272 
273  if (!widget_load("data/interface.cfg", 1, widgets)) {
274  LOG(ERROR, "Could not load widget defaults from data/interface.cfg.");
275  exit(1);
276  }
277 
278  widget_load("settings/interface.cfg", 0, widgets);
280 }
281 
283 static int widget_menu_handle(widgetdata *widget, SDL_Event *event)
284 {
285  widgetdata *menu = create_menu(event->motion.x, event->motion.y, widget);
286  widget_menu_standard_items(widget, menu);
287  menu_finalize(menu);
288  return 1;
289 }
290 
291 void menu_container_move(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
292 {
293  widget_event_start_move(widget->env ? widget->env : widget);
294 }
295 
296 void menu_container_detach(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
297 {
298  widgetdata *widget_container;
299 
300  widget_container = widget->env;
301  detach_widget(widget);
302 
303  if (!widget_container->inv) {
304  remove_widget_object(widget_container);
305  }
306 }
307 
308 void menu_container_attach(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
309 {
310  widgetdata *widget_container;
311 
312  widget_container = create_widget_object(CONTAINER_ID);
313  SOFT_ASSERT(widget_container != NULL,
314  "Failed to create a widget container");
315  widget_container->x = widget->x;
316  widget_container->y = widget->y;
317  insert_widget_in_container(widget_container, widget, 0);
318 }
319 
320 void menu_container_background_change(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
321 {
322  widgetdata *tmp, *container;
323  _widget_label *label;
324 
325  container = get_innermost_container(widget);
326 
327  for (tmp = menuitem->inv; tmp; tmp = tmp->next) {
328  if (tmp->type == LABEL_ID) {
329  label = LABEL(tmp);
330  LOG(INFO, "%s, %s", label->text, container->texture ? container->texture->name : "NONE");
331 
332  SDL_FreeSurface(container->surface);
333  container->surface = NULL;
334 
335  container->texture_type = WIDGET_TEXTURE_TYPE_NORMAL;
336  container->texture = NULL;
337 
338  if (strcmp(label->text, "Blank") == 0) {
339  strncpy(container->bg, "#000000", sizeof(container->bg) - 1);
340  container->bg[sizeof(container->bg) - 1] = '\0';
341  } else if (strcmp(label->text, "Texturised") == 0) {
342  strncpy(container->bg, "widget_bg", sizeof(container->bg) - 1);
343  container->bg[sizeof(container->bg) - 1] = '\0';
344  } else if (strcmp(label->text, "Transparent") == 0) {
345  container->texture_type = WIDGET_TEXTURE_TYPE_NONE;
346  }
347 
348  WIDGET_REDRAW(container);
349 
350  break;
351  }
352  }
353 }
354 
355 void menu_container_background(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
356 {
357  widgetdata *submenu, *container;
358  int is_widget_bg;
359 
360  submenu = MENU(menuitem->env)->submenu;
361  container = get_innermost_container(widget);
362  SOFT_ASSERT(container != NULL, "Failed to get innermost container.");
363  is_widget_bg = container->texture != NULL && strstr(container->texture->name, "widget_bg") != NULL;
364 
365  add_menuitem(submenu, "Blank", &menu_container_background_change, MENU_RADIO, container->texture != NULL && !is_widget_bg);
366  add_menuitem(submenu, "Texturized", &menu_container_background_change, MENU_RADIO, container->texture != NULL && is_widget_bg);
367  add_menuitem(submenu, "Transparent", &menu_container_background_change, MENU_RADIO, container->texture == NULL);
368 }
369 
370 static void menu_container(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
371 {
372  widgetdata *submenu, *outermost;
373 
374  submenu = MENU(menuitem->env)->submenu;
375  outermost = get_outermost_container(widget);
376  SOFT_ASSERT(outermost != NULL, "Failed to get outermost container.");
377 
378  if (outermost->type == CONTAINER_ID) {
379  add_menuitem(submenu, "Move", &menu_container_move, MENU_NORMAL, 0);
380  add_menuitem(submenu, "Background >", &menu_container_background, MENU_SUBMENU, 0);
381  }
382 
383  if (widget != outermost) {
384  add_menuitem(submenu, "Detach", &menu_container_detach, MENU_NORMAL, 0);
385  } else if (widget->type != CONTAINER_ID) {
386  add_menuitem(submenu, "Attach", &menu_container_attach, MENU_NORMAL, 0);
387  }
388 }
389 
390 void widget_menu_standard_items(widgetdata *widget, widgetdata *menu)
391 {
392  if (widget->type != CONTAINER_ID) {
393  add_menuitem(menu, "Move Widget", &menu_move_widget, MENU_NORMAL, 0);
394  }
395 
396  add_menuitem(menu, "Container >", &menu_container, MENU_SUBMENU, 0);
397 }
398 
399 static void widget_texture_create(widgetdata *widget)
400 {
401  char buf[HUGE_BUF];
402  snprintf(VS(buf), "rectangle:%d,%d;", widget->w, widget->h);
403 
404  if (widget->texture_type == WIDGET_TEXTURE_TYPE_NORMAL) {
405  snprintfcat(VS(buf), "[bar=%s]",
406  widget->bg[0] != '\0' ? widget->bg : "widget_bg");
407  }
408 
409  texture_delete(widget->texture);
410  widget->texture = texture_get(TEXTURE_TYPE_SOFTWARE, buf);
411 }
412 
414 widgetdata *create_widget_object(int widget_subtype_id)
415 {
416  widgetdata *widget;
417  int widget_type_id = widget_subtype_id;
418 
419  /* map the widget subtype to widget type */
420  if (widget_subtype_id >= TOTAL_WIDGETS) {
421  switch (widget_subtype_id) {
422  case CONTAINER_STRIP_ID:
423  case MENU_ID:
424  case MENUITEM_ID:
425  widget_type_id = CONTAINER_ID;
426  break;
427 
428  /* no subtype was found, so get out of here */
429  default:
430  return NULL;
431  }
432  }
433 
434  /* sanity check */
435  if (widget_subtype_id < 0 || widget_subtype_id >= TOTAL_SUBWIDGETS) {
436  return NULL;
437  }
438 
439  /* don't create more than one widget if it is a unique widget */
440  if (cur_widget[widget_subtype_id] && cur_widget[widget_subtype_id]->unique) {
441  return NULL;
442  }
443 
444  /* allocate the widget node, this should always be the first function called
445  * in here */
446  widget = create_widget(widget_subtype_id);
447  widget->type = widget_type_id;
448  widget->name = estrdup(widget_names[widget_subtype_id]);
449  widget->redraw = 1;
450  widget->menu_handle_func = widget_menu_handle;
451 
452  if (widget_initializers[widget->type]) {
453  widget_initializers[widget->type](widget);
454  }
455 
456  return widget;
457 }
458 
461 {
462  /* don't delete the last widget if there needs to be at least one of this
463  * widget type */
464  if (widget->required && cur_widget[widget->sub_type] == type_list_foot[widget->sub_type]) {
465  return;
466  }
467 
469 }
470 
479 {
480  widgetdata *tmp;
481 
482  remove_widget_inv(widget);
483 
484  /* If this widget happens to be the owner of an event, keeping them pointed
485  * to it is a bad idea. */
486  if (widget_mouse_event.owner == widget) {
487  widget_mouse_event.owner = NULL;
488  }
489 
490  if (widget_event_move.owner == widget) {
491  widget_event_move.owner = NULL;
492  }
493 
494  if (widget_event_resize.owner == widget) {
495  widget_event_resize.owner = NULL;
496  }
497 
498  /* If any menu is open and this widget is the owner, bad things could happen
499  * here too. Clear the pointers. */
500  if (cur_widget[MENU_ID] && (MENU(cur_widget[MENU_ID]))->owner == widget) {
501  for (tmp = cur_widget[MENU_ID]; tmp; tmp = tmp->type_next) {
502  (MENU(cur_widget[MENU_ID]))->owner = NULL;
503  }
504  }
505 
506  /* Get the environment if it exists, this is used to make containers
507  * auto-resize when the widget is deleted. */
508  tmp = widget->env;
509 
510  if (widget->deinit_func) {
511  widget->deinit_func(widget);
512  }
513 
514  efree(widget->name);
515 
516  if (widget->id) {
517  efree(widget->id);
518  }
519 
520  /* remove the custom attribute nodes if they exist */
521  if (widget->subwidget) {
522  efree(widget->subwidget);
523  widget->subwidget = NULL;
524  }
525 
526  /* finally de-allocate the widget node, this should always be the last node
527  * removed in here */
528  remove_widget(widget);
529 
530  /* resize the container that used to contain this widget, if it exists */
531  if (tmp) {
532  /* if something else exists in its inventory, make it auto-resize to fit
533  * the widgets inside */
534  if (tmp->inv) {
535  resize_widget(tmp->inv, RESIZE_RIGHT, tmp->inv->w);
536  resize_widget(tmp->inv, RESIZE_BOTTOM, tmp->inv->h);
537  } else {
538  /* otherwise if its inventory is empty, resize it to its default size */
539  resize_widget(tmp, RESIZE_RIGHT, cur_widget[tmp->sub_type]->w);
540  resize_widget(tmp, RESIZE_BOTTOM, cur_widget[tmp->sub_type]->h);
541  }
542  }
543 }
544 
554 {
555  widgetdata *tmp;
556 
557  for (widget = widget->inv; widget; widget = tmp) {
558  /* call this function recursively to get to the first child node deep
559  * down inside the widget */
560  remove_widget_inv(widget);
561  /* we need a temp pointer for the next node, as the current node is
562  * about to be no more */
563  tmp = widget->next;
564  /* then remove the widget, and slowly work our way up the tree deleting
565  * widgets until we get to the original widget again */
566  remove_widget_object(widget);
567  }
568 }
569 
573 void kill_widgets(void)
574 {
575  /* get rid of the pointer to the widgets first */
576  widget_mouse_event.owner = NULL;
577  widget_event_move.owner = NULL;
578  widget_event_resize.owner = NULL;
579 
580  /* kick off the chain reaction, there's no turning back now :) */
581  if (widget_list_head) {
582  kill_widget_tree(widget_list_head);
583  widget_list_head = NULL;
584  }
585 }
586 
590 void widgets_reset(void)
591 {
592  char *path;
593 
594  toolkit_widget_deinit();
595  path = file_path("settings/interface.cfg", "w");
596 
597  if (path_exists(path)) {
598  unlink(path);
599  }
600 
601  efree(path);
602 
604 }
605 
614 int
615 widget_x (const widgetdata *widget)
616 {
617  if (!DBL_EQUAL(widget->zoom, 1.0)) {
618  return widget->zoom_x;
619  }
620 
621  return widget->x;
622 }
623 
632 int
633 widget_y (const widgetdata *widget)
634 {
635  if (!DBL_EQUAL(widget->zoom, 1.0)) {
636  return widget->zoom_y;
637  }
638 
639  return widget->y;
640 }
641 
650 int
651 widget_w (const widgetdata *widget)
652 {
653  return widget->w * widget->zoom;
654 }
655 
664 int
665 widget_h (const widgetdata *widget)
666 {
667  return widget->h * widget->zoom;
668 }
669 
680 bool
681 widget_set_zoom (widgetdata *widget, double zoom)
682 {
683  if (DBL_EQUAL(widget->zoom, zoom)) {
684  /* Zoom values are the same; nothing to do. */
685  return false;
686  }
687 
688  widget->zoom = zoom;
689 
690  int w, h;
691  zoomSurfaceSize(widget->w, widget->h, zoom, zoom, &w, &h);
692 
693  widget->zoom_x = widget->x - (w - widget->w) / 2;
694  widget->zoom_y = widget->y - (h - widget->h) / 2;
695 
696  return true;
697 }
698 
707 static bool
709 {
710  switch (widget->type) {
711  case MINIMAP_ID:
712  return true;
713 
714  default:
715  return false;
716  }
717 }
718 
731 static bool
732 widget_mouse_over (widgetdata *widget, int x, int y)
733 {
734  if (x < widget_x(widget) ||
735  y < widget_y(widget) ||
736  x > widget_x(widget) + widget_w(widget) ||
737  y > widget_y(widget) + widget_h(widget)) {
738  return false;
739  }
740 
741  if (widget_is_ellipse(widget)) {
742  int xpad = 0, ypad = 0;
743  if (widget->padding_func != NULL) {
744  widget->padding_func(widget, &xpad, &ypad);
745  }
746 
747  if (!math_point_in_ellipse(x - widget_x(widget) - xpad,
748  y - widget_y(widget) - ypad,
749  (widget_w(widget) - xpad * 2) / 2.0,
750  (widget_h(widget) - ypad * 2) / 2.0,
751  widget_w(widget) - xpad * 2,
752  widget_h(widget) - ypad * 2,
753  0.0)) {
754  return false;
755  }
756  }
757 
758  return true;
759 }
760 
766 static void widget_ensure_onscreen(widgetdata *widget)
767 {
768  int dx = 0, dy = 0;
769 
771  if (widget->x < 0) {
772  dx = -widget->x;
773  } else if (widget->x + widget->w > setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_X)) {
774  dx = setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_X) - widget->w - widget->x;
775  }
776 
777  if (widget->y < 0) {
778  dy = -widget->y;
779  } else if (widget->y + widget->h > setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_Y)) {
780  dy = setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_Y) - widget->h - widget->y;
781  }
782  }
783 
784  if (widget->env) {
785  if (widget->x < widget->env->x) {
786  dx = widget->env->x - widget->x;
787  } else if (widget->x + widget->w > widget->env->x + widget->env->w) {
788  dx = (widget->env->x + widget->env->w) - (widget->w + widget->x);
789  }
790 
791  if (widget->y < widget->env->y) {
792  dy = widget->env->y - widget->y;
793  } else if (widget->y + widget->h > widget->env->y + widget->env->h) {
794  dy = (widget->env->y + widget->env->h) - (widget->h + widget->y);
795  }
796  }
797 
798  move_widget_rec(widget, dx, dy);
799 }
800 
805 {
806  widgetdata *tmp;
807 
808  for (tmp = widget_list_head; tmp; tmp = tmp->next) {
810  }
811 }
812 
815 {
816  widgetdata *tmp;
817 
818  do {
819  /* we want to process the widgets starting from the left hand side of
820  * the tree first */
821  if (widget->inv) {
822  kill_widget_tree(widget->inv);
823  }
824 
825  /* store the next sibling in a tmp variable, as widget is about to be
826  * zapped from existence */
827  tmp = widget->next;
828 
829  /* here we call our widget kill function, and force removal by using the
830  * internal one */
832 
833  /* get the next sibling for our next loop */
834  widget = tmp;
835  } while (widget);
836 }
837 
845 widgetdata *create_widget(int widget_id)
846 {
847  widgetdata *node;
848 
849 #ifdef DEBUG_WIDGET
850  LOG(INFO, "Entering create_widget()..");
851 #endif
852 
853  /* allocate it */
854  node = ecalloc(1, sizeof(widgetdata));
855 
856  /* set the members */
857  memcpy(node, &def_widget[widget_id], sizeof(*node));
858  node->sub_type = widget_id;
859  node->zoom = 1.0;
860 
861  /* link it up to the tree if the root exists */
862  if (widget_list_head) {
863  node->next = widget_list_head;
864  widget_list_head->prev = node;
865  }
866 
867  /* set the foot if it doesn't exist */
868  if (!widget_list_foot) {
869  widget_list_foot = node;
870  }
871 
872  /* the new node becomes the new root node, which also automatically brings
873  * it to the front */
874  widget_list_head = node;
875 
876  /* if head of widget type linked list doesn't exist, set the head and foot
877  * */
878  if (!cur_widget[widget_id]) {
879  cur_widget[widget_id] = type_list_foot[widget_id] = node;
880  } else {
881  /* otherwise, link the node in to the existing type list */
882  type_list_foot[widget_id]->type_next = node;
883  node->type_prev = type_list_foot[widget_id];
884  type_list_foot[widget_id] = node;
885  }
886 
887 #ifdef DEBUG_WIDGET
888  LOG(DEBUG, "..ALLOCATED: %s", node->name);
889  debug_count_nodes(1);
890 
891  LOG(INFO, "..create_widget(): Done.");
892 #endif
893 
894  return node;
895 }
896 
900 {
901  widgetdata *tmp = NULL;
902 
903 #ifdef DEBUG_WIDGET
904  LOG(INFO, "Entering remove_widget()..");
905 #endif
906 
907  /* node to delete is the only node in the tree, bye-bye binary tree :) */
908  if (!widget_list_head->next && !widget_list_head->inv) {
909  widget_list_head = NULL;
910  widget_list_foot = NULL;
911  cur_widget[widget->sub_type] = NULL;
912  type_list_foot[widget->sub_type] = NULL;
913  } else {
914  /* node to delete is the head, move the pointer to next node */
915  if (widget == widget_list_head) {
916  widget_list_head = widget_list_head->next;
917  widget_list_head->prev = NULL;
918  } else if (widget == widget_list_foot) {
919  /* node to delete is the foot, move the pointer to the previous node */
920 
921  widget_list_foot = widget_list_foot->prev;
922  widget_list_foot->next = NULL;
923  } else if (!widget->prev) {
924  /* node is first sibling, and should have a parent since it is not the
925  * root node */
926 
927  /* node is also the last sibling, so NULL the parent's inventory */
928  if (!widget->next) {
929  widget->env->inv = NULL;
930  widget->env->inv_rev = NULL;
931  } else {
932  /* or else make it the parent's first child */
933  widget->env->inv = widget->next;
934  widget->next->prev = NULL;
935  }
936  } else if (!widget->next) {
937  /* node is last sibling and should have a parent, move the inv_rev
938  * pointer to the previous sibling */
939  widget->env->inv_rev = widget->prev;
940  widget->prev->next = NULL;
941  } else {
942  /* node to delete is in the middle of the tree somewhere */
943  widget->next->prev = widget->prev;
944  widget->prev->next = widget->next;
945  }
946 
947  /* move the children to the top level of the list, starting from the end
948  * child */
949  for (tmp = widget->inv_rev; tmp; tmp = tmp->prev) {
950  /* tmp is no longer in a container */
951  tmp->env = NULL;
952  widget_list_head->prev = tmp;
953  tmp->next = widget_list_head;
954  widget_list_head = tmp;
955  }
956 
957  /* if widget type list has only one node, kill it */
958  if (cur_widget[widget->sub_type] == type_list_foot[widget->sub_type]) {
959  cur_widget[widget->sub_type] = type_list_foot[widget->sub_type] = NULL;
960  } else if (widget == cur_widget[widget->sub_type]) {
961  /* widget is head node */
962  cur_widget[widget->sub_type] = cur_widget[widget->sub_type]->type_next;
963  cur_widget[widget->sub_type]->type_prev = NULL;
964  } else if (widget == type_list_foot[widget->sub_type]) {
965  /* widget is foot node */
966  type_list_foot[widget->sub_type] = type_list_foot[widget->sub_type]->type_prev;
967  type_list_foot[widget->sub_type]->type_next = NULL;
968  } else {
969  /* widget is in middle of type list */
970  widget->type_prev->type_next = widget->type_next;
971  widget->type_next->type_prev = widget->type_prev;
972  }
973  }
974 
975 #ifdef DEBUG_WIDGET
976  LOG(DEBUG, "..REMOVED: %s", widget->name);
977 #endif
978 
979  /* free the surface */
980  if (widget->surface) {
981  SDL_FreeSurface(widget->surface);
982  widget->surface = NULL;
983  }
984 
985  efree(widget);
986 
987 #ifdef DEBUG_WIDGET
988  debug_count_nodes(1);
989  LOG(INFO, "..remove_widget(): Done.");
990 #endif
991 }
992 
996 {
997  /* sanity check */
998  if (!widget->env) {
999  return;
1000  }
1001 
1002  /* first unlink the widget from the container and siblings */
1003 
1004  /* if widget is only one in the container's inventory, clear both pointers
1005  * */
1006  if (!widget->prev && !widget->next) {
1007  widget->env->inv = NULL;
1008  widget->env->inv_rev = NULL;
1009  } else if (!widget->prev) {
1010  /* widget is first sibling */
1011  widget->env->inv = widget->next;
1012  widget->next->prev = NULL;
1013  } else if (!widget->next) {
1014  /* widget is last sibling */
1015  widget->env->inv_rev = widget->prev;
1016  widget->prev->next = NULL;
1017  } else {
1018  /* widget is a middle sibling */
1019  widget->prev->next = widget->next;
1020  widget->next->prev = widget->prev;
1021  }
1022 
1023  /* if something else exists in the container's inventory, make it
1024  * auto-resize to fit the widgets inside */
1025  if (widget->env->inv) {
1026  resize_widget(widget->env->inv, RESIZE_RIGHT, widget->env->inv->w);
1027  resize_widget(widget->env->inv, RESIZE_BOTTOM, widget->env->inv->h);
1028  } else {
1029  /* otherwise if its inventory is empty, resize it to its default size */
1030  resize_widget(widget->env, RESIZE_RIGHT, cur_widget[widget->env->sub_type]->w);
1031  resize_widget(widget->env, RESIZE_BOTTOM, cur_widget[widget->env->sub_type]->h);
1032  }
1033 
1034  /* widget is no longer in a container */
1035  widget->env = NULL;
1036  /* move the widget to the top of the priority tree */
1037  widget->prev = NULL;
1038  widget_list_head->prev = widget;
1039  widget->next = widget_list_head;
1040  widget_list_head = widget;
1041 }
1042 
1043 #ifdef DEBUG_WIDGET
1044 
1046 int debug_count_nodes_rec(widgetdata *widget, int i, int j, int output)
1047 {
1048  int tmp = 0;
1049 
1050  do {
1051  /* we print out the top node, and then go down a level, rather than go
1052  * down first */
1053  if (output) {
1054  /* a way of representing graphically how many levels down we are */
1055  for (tmp = 0; tmp < j; ++tmp) {
1056  printf("..");
1057  }
1058 
1059  LOG(INFO, "..%s", widget->name);
1060  }
1061 
1062  i++;
1063 
1064  /* we want to process the widgets starting from the left hand side of
1065  * the tree first */
1066  if (widget->inv) {
1067  i = debug_count_nodes_rec(widget->inv, i, j + 1, output);
1068  }
1069 
1070  /* get the next sibling for our next loop */
1071  widget = widget->next;
1072  } while (widget);
1073 
1074  return i;
1075 }
1076 
1077 void debug_count_nodes(int output)
1078 {
1079  int i = 0;
1080 
1081  LOG(INFO, "Output of widget nodes:");
1082  LOG(INFO, "========================================");
1083 
1084  if (widget_list_head) {
1085  i = debug_count_nodes_rec(widget_list_head, 0, 0, output);
1086  }
1087 
1088  LOG(INFO, "========================================");
1089  LOG(INFO, "..Total widget nodes: %d", i);
1090 }
1091 #endif
1092 
1093 static void widget_save_rec(FILE *fp, widgetdata *widget, int depth)
1094 {
1095  char *padding;
1096 
1097  for (; widget; widget = widget->prev) {
1098  if (!widget->save) {
1099  continue;
1100  }
1101 
1102  padding = string_repeat("\t", depth);
1103 
1104  fprintf(fp, "%s[%s]\n", padding, widget->name);
1105 
1106  if (widget->id) {
1107  fprintf(fp, "%sid = %s\n", padding, widget->id);
1108  }
1109 
1110  if (widget->moveable != def_widget[widget->sub_type].moveable) {
1111  fprintf(fp, "%smoveable = %s\n", padding, widget->moveable ? "yes" : "no");
1112  }
1113 
1114  if (widget->show != def_widget[widget->sub_type].show) {
1115  fprintf(fp, "%sshown = %s\n", padding, widget->show ? "yes" : "no");
1116  }
1117 
1118  if (widget->resizeable != def_widget[widget->sub_type].resizeable) {
1119  fprintf(fp, "%sresizeable = %s\n", padding, widget->resizeable ? "yes" : "no");
1120  }
1121 
1122  if (widget->required != def_widget[widget->sub_type].required) {
1123  fprintf(fp, "%srequired = %s\n", padding, widget->required ? "yes" : "no");
1124  }
1125 
1126  if (widget->save != def_widget[widget->sub_type].save) {
1127  fprintf(fp, "%ssave = %s\n", padding, widget->save ? "yes" : "no");
1128  }
1129 
1130  if (widget->x != def_widget[widget->sub_type].x) {
1131  fprintf(fp, "%sx = %d\n", padding, widget->x);
1132  }
1133 
1134  if (widget->y != def_widget[widget->sub_type].y) {
1135  fprintf(fp, "%sy = %d\n", padding, widget->y);
1136  }
1137 
1138  if (widget->w != def_widget[widget->sub_type].w) {
1139  fprintf(fp, "%sw = %d\n", padding, widget->w);
1140  }
1141 
1142  if (widget->h != def_widget[widget->sub_type].h) {
1143  fprintf(fp, "%sh = %d\n", padding, widget->h);
1144  }
1145 
1146  if (widget->min_w != def_widget[widget->sub_type].min_w) {
1147  fprintf(fp, "%smin_w = %d\n", padding, widget->min_w);
1148  }
1149 
1150  if (widget->min_h != def_widget[widget->sub_type].min_h) {
1151  fprintf(fp, "%smin_h = %d\n", padding, widget->min_h);
1152  }
1153 
1154  if (strcmp(widget->bg, def_widget[widget->sub_type].bg) != 0) {
1155  fprintf(fp, "%sbg = %s\n", padding, widget->bg);
1156  }
1157 
1158  if (widget->texture_type != def_widget[widget->sub_type].texture_type) {
1159  fprintf(fp, "%stexture_type = %d\n", padding, widget->texture_type);
1160  }
1161 
1162  if (widget->save_func) {
1163  widget->save_func(widget, fp, padding);
1164  }
1165 
1166  if (widget->inv_rev) {
1167  widget_save_rec(fp, widget->inv_rev, depth + 1);
1168  }
1169 
1170  if (depth == 0) {
1171  fprintf(fp, "\n");
1172  }
1173 
1174  efree(padding);
1175  }
1176 }
1177 
1178 static void widget_save(void)
1179 {
1180  FILE *fp;
1181 
1182  fp = path_fopen("settings/interface.cfg", "w");
1183 
1184  if (!fp) {
1185  return;
1186  }
1187 
1188  widget_save_rec(fp, widget_list_foot, 0);
1189  fclose(fp);
1190 }
1191 
1192 void toolkit_widget_deinit(void)
1193 {
1194  widget_save();
1195  kill_widgets();
1196 }
1197 
1205 int widgets_event(SDL_Event *event)
1206 {
1207  widgetdata *widget;
1208  int ret;
1209 
1210  /* Widget is being moved. */
1211  if (widget_event_move.active) {
1212  widget = widget_event_move.owner;
1213 
1214  if (event->type == SDL_MOUSEMOTION) {
1215  int x, y, nx, ny;
1216 
1217  x = event->motion.x - widget_event_move.xOffset;
1218  y = event->motion.y - widget_event_move.yOffset;
1219  nx = x;
1220  ny = y;
1221 
1222  /* Widget snapping logic courtesy of OpenTTD (GPLv2). */
1224  widgetdata *tmp;
1225  int delta, hsnap, vsnap;
1226 
1227  delta = 0;
1229 
1230  for (tmp = widget_list_head; tmp; tmp = tmp->next) {
1231  if (tmp == widget || tmp->disable_snapping || !tmp->show) {
1232  continue;
1233  }
1234 
1235  if (y + widget->h > tmp->y && y < tmp->y + tmp->h) {
1236  /* Your left border <-> other right border */
1237  delta = abs(tmp->x + tmp->w - x);
1238 
1239  if (delta <= hsnap) {
1240  nx = tmp->x + tmp->w;
1241  hsnap = delta;
1242  }
1243 
1244  /* Your right border <-> other left border */
1245  delta = abs(tmp->x - x - widget->w);
1246 
1247  if (delta <= hsnap) {
1248  nx = tmp->x - widget->w;
1249  hsnap = delta;
1250  }
1251  }
1252 
1253  if (widget->y + widget->h >= tmp->y && widget->y <= tmp->y + tmp->h) {
1254  /* Your left border <-> other left border */
1255  delta = abs(tmp->x - x);
1256 
1257  if (delta <= hsnap) {
1258  nx = tmp->x;
1259  hsnap = delta;
1260  }
1261 
1262  /* Your right border <-> other right border */
1263  delta = abs(tmp->x + tmp->w - x - widget->w);
1264 
1265  if (delta <= hsnap) {
1266  nx = tmp->x + tmp->w - widget->w;
1267  hsnap = delta;
1268  }
1269  }
1270 
1271  if (x + widget->w > tmp->x && x < tmp->x + tmp->w) {
1272  /* Your top border <-> other bottom border */
1273  delta = abs(tmp->y + tmp->h - y);
1274 
1275  if (delta <= vsnap) {
1276  ny = tmp->y + tmp->h;
1277  vsnap = delta;
1278  }
1279 
1280  /* Your bottom border <-> other top border */
1281  delta = abs(tmp->y - y - widget->h);
1282 
1283  if (delta <= vsnap) {
1284  ny = tmp->y - widget->h;
1285  vsnap = delta;
1286  }
1287  }
1288 
1289  if (widget->x + widget->w >= tmp->x && widget->x <= tmp->x + tmp->w) {
1290  /* Your top border <-> other top border */
1291  delta = abs(tmp->y - y);
1292 
1293  if (delta <= vsnap) {
1294  ny = tmp->y;
1295  vsnap = delta;
1296  }
1297 
1298  /* Your bottom border <-> other bottom border */
1299  delta = abs(tmp->y + tmp->h - y - widget->h);
1300 
1301  if (delta <= vsnap) {
1302  ny = tmp->y + tmp->h - widget->h;
1303  vsnap = delta;
1304  }
1305  }
1306  }
1307  }
1308 
1309  move_widget_rec(widget, nx - widget->x, ny - widget->y);
1310  widget_ensure_onscreen(widget);
1311  } else if (event->type == SDL_MOUSEBUTTONDOWN) {
1312  return widget_event_move_stop(event->motion.x, event->motion.y);
1313  }
1314 
1315  return 1;
1316  } else if (widget_event_resize.active) {
1317  /* Widget is being resized. */
1318 
1319  widget = widget_event_resize.owner;
1320 
1321  if (event->type == SDL_MOUSEBUTTONUP) {
1322  widget_event_resize.active = 0;
1323  widget_event_resize.owner = NULL;
1324  } else if (event->type == SDL_MOUSEMOTION) {
1325  if (widget->resize_flags & (RESIZE_LEFT | RESIZE_RIGHT)) {
1326  resize_widget(widget, widget->resize_flags & (RESIZE_LEFT | RESIZE_RIGHT), MAX(MAX(5, widget->min_w), widget->w + (event->motion.x - widget_event_resize.xoff) * (widget->resize_flags & RESIZE_LEFT ? -1 : 1)));
1327  }
1328 
1329  if (widget->resize_flags & (RESIZE_TOP | RESIZE_BOTTOM)) {
1330  resize_widget(widget, widget->resize_flags & (RESIZE_TOP | RESIZE_BOTTOM), MAX(MAX(5, widget->min_h), widget->h + (event->motion.y - widget_event_resize.yoff) * (widget->resize_flags & RESIZE_TOP ? -1 : 1)));
1331  }
1332 
1333  widget_event_resize.xoff = event->motion.x;
1334  widget_event_resize.yoff = event->motion.y;
1335  }
1336 
1337  return 1;
1338  }
1339 
1340  if (EVENT_IS_MOUSE(event)) {
1341  if (!widget_event_respond(event->motion.x, event->motion.y)) {
1342  return 0;
1343  }
1344 
1345  widget = widget_mouse_event.owner;
1346 
1347  if (event->type == SDL_MOUSEMOTION) {
1348  if (widget->resizeable) {
1349  widget->resize_flags = 0;
1350  }
1351  } else if (event->type == SDL_MOUSEBUTTONDOWN) {
1352  /* Set the priority to this widget. */
1353  SetPriorityWidget(widget);
1354 
1355  /* Right mouse button was clicked, try to create menu. */
1356  if (event->button.button == SDL_BUTTON_RIGHT && !cur_widget[MENU_ID] && widget->menu_handle_func && widget->menu_handle_func(widget, event)) {
1357  return 1;
1358  } else if (widget->resize_flags && event->button.button == SDL_BUTTON_LEFT) {
1359  /* Start resizing. */
1360  widget_event_resize.active = 1;
1361  widget_event_resize.owner = widget;
1362  widget_event_resize.xoff = event->motion.x;
1363  widget_event_resize.yoff = event->motion.y;
1364  return 1;
1365  }
1366 
1367  if (cur_widget[MENU_ID] && widget->sub_type != MENUITEM_ID && widget->env && widget->env->sub_type == MENUITEM_ID) {
1368  widget = widget->env;
1369  }
1370  }
1371 
1372  ret = 0;
1373 
1374  if (widget->event_func) {
1375  ret = widget->event_func(widget, event);
1376  }
1377 
1378  if (ret == 0 && event->type == SDL_MOUSEMOTION && widget->resizeable) {
1379 #define WIDGET_RESIZE_CHECK(coord, upper_adj, lower_adj) \
1380  (event->motion.coord >= widget_##coord(widget) + (upper_adj) && \
1381  event->motion.coord <= widget_##coord(widget) + (lower_adj))
1382  if (widget_is_ellipse(widget)) {
1383  int xpad = 0, ypad = 0;
1384  if (widget->padding_func != NULL) {
1385  widget->padding_func(widget, &xpad, &ypad);
1386  }
1387  int x = event->motion.x - widget_x(widget) - xpad;
1388  int y = event->motion.y - widget_y(widget) - ypad;
1389  int deg;
1390  if (math_point_edge_ellipse(x,
1391  y,
1392  (widget_w(widget) - xpad * 2) / 2.0,
1393  (widget_h(widget) - ypad * 2) / 2.0,
1394  widget_w(widget) - xpad * 2,
1395  widget_h(widget) - ypad * 2,
1396  0.0,
1397  &deg)) {
1398  if (deg >= 5 && deg < 175) {
1399  widget->resize_flags |= RESIZE_RIGHT;
1400  } else if (deg >= 185 && deg < 355) {
1401  widget->resize_flags |= RESIZE_LEFT;
1402  }
1403 
1404  if (deg >= 95 && deg < 265) {
1405  widget->resize_flags |= RESIZE_BOTTOM;
1406  } else if (deg >= 275 || deg < 85) {
1407  widget->resize_flags |= RESIZE_TOP;
1408  }
1409  }
1410  } else {
1411  if (WIDGET_RESIZE_CHECK(y,
1412  0,
1413  2)) {
1414  widget->resize_flags = RESIZE_TOP;
1415  } else if (WIDGET_RESIZE_CHECK(y,
1416  widget_h(widget) - 2,
1417  widget_h(widget))) {
1418  widget->resize_flags = RESIZE_BOTTOM;
1419  } else if (WIDGET_RESIZE_CHECK(x,
1420  0,
1421  2)) {
1422  widget->resize_flags = RESIZE_LEFT;
1423  } else if (WIDGET_RESIZE_CHECK(x,
1424  widget_w(widget) - 2,
1425  widget_w(widget))) {
1426  widget->resize_flags = RESIZE_RIGHT;
1427  }
1428 
1429  if (widget->resize_flags & (RESIZE_TOP |
1430  RESIZE_BOTTOM)) {
1431  if (WIDGET_RESIZE_CHECK(x,
1432  0,
1433  widget_w(widget) * 0.05)) {
1434  widget->resize_flags |= RESIZE_LEFT;
1435  } else if (WIDGET_RESIZE_CHECK(x,
1436  widget_w(widget) -
1437  widget_w(widget) * 0.05,
1438  widget_w(widget))) {
1439  widget->resize_flags |= RESIZE_RIGHT;
1440  }
1441  } else if (widget->resize_flags & (RESIZE_LEFT |
1442  RESIZE_RIGHT)) {
1443  if (WIDGET_RESIZE_CHECK(y,
1444  0,
1445  widget_h(widget) * 0.05)) {
1446  widget->resize_flags |= RESIZE_TOP;
1447  } else if (WIDGET_RESIZE_CHECK(y,
1448  widget_h(widget) -
1449  widget_h(widget) * 0.05,
1450  widget_h(widget))) {
1451  widget->resize_flags |= RESIZE_BOTTOM;
1452  }
1453  }
1454  }
1455 #undef WIDGET_RESIZE_CHECK
1456 
1457  if (widget->resize_flags) {
1458  return 1;
1459  }
1460  }
1461 
1462  if (event->type == SDL_MOUSEBUTTONDOWN) {
1463  widgetdata *tmp, *next;
1464 
1465  for (tmp = cur_widget[MENU_ID]; tmp; tmp = next) {
1466  next = tmp->type_next;
1467  remove_widget_object(tmp);
1468  }
1469  }
1470 
1471  return ret;
1472  } else {
1473  for (widget = widget_list_head; widget; widget = widget->next) {
1474  if (widget->event_func && widget->event_func(widget, event)) {
1475  return 1;
1476  }
1477  }
1478  }
1479 
1480  return 0;
1481 }
1482 
1485 {
1486  int x, y;
1487 
1488  /* if its moveable, start moving it when the conditions warrant it, or else
1489  * run away */
1490  if (!widget->moveable) {
1491  return 0;
1492  }
1493 
1494  SDL_GetMouseState(&x, &y);
1495  if (!widget_mouse_over(widget, x, y)) {
1496  x = widget_x(widget) + widget_w(widget) / 2;
1497  y = widget_y(widget) + widget_h(widget) / 2;
1498  SDL_WarpMouse(x, y);
1499  }
1500 
1501  /* we know this widget owns the mouse.. */
1502  widget_event_move.active = 1;
1503 
1504  /* start the movement procedures */
1505  widget_event_move.owner = widget;
1506  widget_event_move.xOffset = x - widget->x;
1507  widget_event_move.yOffset = y - widget->y;
1508 
1509  return 1;
1510 }
1511 
1512 int widget_event_move_stop(int x, int y)
1513 {
1514  widgetdata *widget, *widget_container;
1515 
1516  if (!widget_event_move.active) {
1517  return 0;
1518  }
1519 
1520  widget = widget_mouse_event.owner;
1521 
1522  widget_event_move.active = 0;
1523  widget_mouse_event.x = x;
1524  widget_mouse_event.y = y;
1525  /* No widgets are being moved now. */
1526  widget_event_move.owner = NULL;
1527 
1528  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_default");
1529 
1530  /* Somehow the owner before the widget dragging is gone now. Not a
1531  * good idea to carry on... */
1532  if (!widget) {
1533  return 0;
1534  }
1535 
1536  /* Check to see if it's on top of a widget container. */
1537  widget_container = get_outermost_container(get_widget_owner(x, y, get_outermost_container(widget)->next, NULL));
1538 
1539  /* Attempt to insert it into the widget container if it exists. */
1540  insert_widget_in_container(widget_container, get_outermost_container(widget), 0);
1541 
1542  return 1;
1543 }
1544 
1546 int widget_event_respond(int x, int y)
1547 {
1548  widget_mouse_event.owner = get_widget_owner(x, y, NULL, NULL);
1549 
1550  /* sanity check.. return if mouse is not in a widget */
1551  if (!widget_mouse_event.owner) {
1552  return 0;
1553  }
1554 
1555  /* setup the event structure in response */
1556  widget_mouse_event.x = x;
1557  widget_mouse_event.y = y;
1558 
1559  return 1;
1560 }
1561 
1563 widgetdata *get_widget_owner(int x, int y, widgetdata *start, widgetdata *end)
1564 {
1565  widgetdata *success;
1566 
1567  /* mouse cannot be used by widgets */
1568  if (IsMouseExclusive) {
1569  return NULL;
1570  }
1571 
1572  /* no widgets exist */
1573  if (!widget_list_head) {
1574  return NULL;
1575  }
1576 
1577  /* sometimes we want a fast way to get the widget behind the widget at the
1578  * front.
1579  * this is what start is for, and we will only start walking the list
1580  * beginning with start.
1581  * if start is NULL, we just do a regular search */
1582  if (!start) {
1583  start = widget_list_head;
1584  }
1585 
1586  /* ok, let's kick off the recursion. if we find our widget, we get a widget
1587  * back. if not, we get a big fat NULL */
1588  success = get_widget_owner_rec(x, y, start, end);
1589 
1590  /*LOG(DEBUG, "WIDGET OWNER: %s", success? success->name:
1591  * "NULL");*/
1592 
1593  return success;
1594 }
1595 
1596 /* traverse through the tree & perform custom or default hit-test */
1597 widgetdata *get_widget_owner_rec(int x, int y, widgetdata *widget, widgetdata *end)
1598 {
1599  widgetdata *success = NULL;
1600 
1601  do {
1602  /* skip if widget is hidden */
1603  if (!widget->show) {
1604  widget = widget->next;
1605  continue;
1606  }
1607 
1608  /* we want to get the first widget starting from the left hand side of
1609  * the tree first */
1610  if (widget->inv) {
1611  success = get_widget_owner_rec(x, y, widget->inv, end);
1612 
1613  /* we found a widget in the hit test? if so, get out of this
1614  * recursive mess with our prize */
1615  if (success) {
1616  return success;
1617  }
1618  }
1619 
1620  if (widget_mouse_over(widget, x, y)) {
1621  return widget;
1622  }
1623 
1624  /* get the next sibling for our next loop */
1625  widget = widget->next;
1626  } while (widget || widget != end);
1627 
1628  return NULL;
1629 }
1630 
1639 {
1640  for ( ; widget != NULL; widget = widget->next) {
1641  if (widget->show && !widget->hidden && widget->draw_func &&
1642  widget->redraw) {
1643  return 1;
1644  }
1645 
1646  if (widget->inv) {
1647  if (widgets_need_redraw_rec(widget->inv)) {
1648  return 1;
1649  }
1650  }
1651  }
1652 
1653  return 0;
1654 }
1655 
1662 {
1663  return widgets_need_redraw_rec(widget_list_head);
1664 }
1665 
1673 static void process_widgets_rec(int draw, widgetdata *widget)
1674 {
1675  uint8_t redraw;
1676 
1677  for (; widget; widget = widget->prev) {
1678  if (widget->background_func) {
1679  widget->background_func(widget, draw);
1680  }
1681 
1682  if (draw && widget->show && !widget->hidden && widget->draw_func) {
1683  if (widget->resize_flags) {
1684  if (!widget_event_resize.active &&
1685  !widget_mouse_over(widget, cursor_x, cursor_y)) {
1686  widget->resize_flags = 0;
1687  }
1688 
1689  if (widget->resize_flags == (RESIZE_TOP | RESIZE_LEFT) || widget->resize_flags == (RESIZE_BOTTOM | RESIZE_RIGHT)) {
1690  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_resize_tl2br");
1691  } else if (widget->resize_flags == (RESIZE_TOP | RESIZE_RIGHT) || widget->resize_flags == (RESIZE_BOTTOM | RESIZE_LEFT)) {
1692  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_resize_tr2bl");
1693  } else if (widget->resize_flags & (RESIZE_LEFT | RESIZE_RIGHT)) {
1694  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_resize_hor");
1695  } else if (widget->resize_flags & (RESIZE_TOP | RESIZE_BOTTOM)) {
1696  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_resize_ver");
1697  }
1698  }
1699 
1700  if (!widget->texture && widget->texture_type != WIDGET_TEXTURE_TYPE_NONE) {
1701  widget_texture_create(widget);
1702  }
1703 
1704  if (widget->texture) {
1705  if (!widget->surface || widget->surface->w != widget->w || widget->surface->h != widget->h) {
1706  SDL_Surface *texture;
1707 
1708  if (widget->surface) {
1709  widget_texture_create(widget);
1710  SDL_FreeSurface(widget->surface);
1711  }
1712 
1713  texture = texture_surface(widget->texture);
1714  widget->surface = SDL_ConvertSurface(texture, texture->format, texture->flags);
1715  }
1716 
1717  if (widget->redraw) {
1718  surface_show(widget->surface, 0, 0, NULL, texture_surface(widget->texture));
1719  }
1720  }
1721 
1722  redraw = widget->redraw;
1723  widget->draw_func(widget);
1724 
1725  if (widget->texture) {
1726  SDL_Rect box;
1727 
1728  if (redraw && widget->texture_type == WIDGET_TEXTURE_TYPE_NORMAL) {
1729  box.w = widget->w;
1730  box.h = widget->h;
1731  text_show_format(widget->surface, FONT_ARIAL10, 0, 0, COLOR_BLACK, TEXT_MARKUP, &box, "[border=widget_border -1 -1 %d]", WIDGET_BORDER_SIZE);
1732  }
1733 
1734  box.x = widget->x;
1735  box.y = widget->y;
1736  box.w = 0;
1737  box.h = 0;
1738  SDL_BlitSurface(widget->surface, NULL, ScreenSurface, &box);
1739  }
1740 
1741  if (redraw != 0 && widget_render_debug) {
1742  texture_struct *texture_debug;
1743  SDL_Rect box;
1744 
1745  box.w = widget->w;
1746  box.h = widget->h;
1747 
1748  texture_debug = texture_get(TEXTURE_TYPE_SOFTWARE,
1749  "rectangle:50,50,127;[bar=#ff66ff]");
1750  surface_show_fill(ScreenSurface, widget->x, widget->y, NULL,
1751  texture_surface(texture_debug), &box);
1752  }
1753 
1754  widget->redraw -= redraw;
1755  }
1756 
1757  /* we want to process the widgets starting from the right hand side of
1758  * the tree first */
1759  if (widget->inv_rev) {
1760  process_widgets_rec(widget->show ? draw : 0, widget->inv_rev);
1761  }
1762  }
1763 }
1764 
1770 void process_widgets(int draw)
1771 {
1772  if (draw && widget_event_move.active) {
1773  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_move");
1774  }
1775 
1776  process_widgets_rec(draw, widget_list_foot);
1777 }
1778 
1789 {
1790 #ifdef DEBUG_WIDGET
1791  LOG(DEBUG, "Entering SetPriorityWidget..");
1792 #endif
1793 
1794  /* widget doesn't exist, means parent node has no children, so nothing to do
1795  * here */
1796  if (!node) {
1797 #ifdef DEBUG_WIDGET
1798  LOG(DEBUG, "..SetPriorityWidget(): Done (Node does not exist).");
1799 #endif
1800  return;
1801  }
1802 
1803  if (node->type == MAP_ID) {
1804  return;
1805  }
1806 
1807  /* TODO: add callback function? could also block the above if with it */
1808  if (node->type == INVENTORY_ID) {
1809  cpl.inventory_focus = node;
1810  WIDGET_REDRAW_ALL(INVENTORY_ID);
1811  }
1812 
1813 #ifdef DEBUG_WIDGET
1814  LOG(DEBUG, "..BEFORE:");
1815  LOG(DEBUG, "....node: %p - %s", node, node->name);
1816  LOG(DEBUG, "....node->env: %p - %s", node->env, node->env ? node->env->name : "NULL");
1817  LOG(DEBUG, "....node->prev: %p - %s, node->next: %p - %s", node->prev, node->prev ? node->prev->name : "NULL", node->next, node->next ? node->next->name : "NULL");
1818  LOG(DEBUG, "....node->inv: %p - %s, node->inv_rev: %p - %s", node->inv, node->inv ? node->inv->name : "NULL", node->inv_rev, node->inv_rev ? node->inv_rev->name : "NULL");
1819 #endif
1820 
1821  /* see if the node has a parent before continuing */
1822  if (node->env) {
1823  SetPriorityWidget(node->env);
1824 
1825  /* Strip containers are sorted in a fixed order, and no part of any
1826  * widget inside should be covered by a sibling.
1827  * This means we don't need to bother moving the node to the front
1828  * inside the container. */
1829  switch (node->env->sub_type) {
1830  case CONTAINER_STRIP_ID:
1831  case MENU_ID:
1832  case MENUITEM_ID:
1833  return;
1834  }
1835  }
1836 
1837  /* now we need to move our other node in front of the first sibling */
1838  if (!node->prev) {
1839 #ifdef DEBUG_WIDGET
1840  LOG(DEBUG, "..SetPriorityWidget(): Done (Node already at front).");
1841 #endif
1842  /* no point continuing, node is already at the front */
1843  return;
1844  }
1845 
1846  /* Unlink node from its current position in the priority tree. */
1847 
1848  /* node is last sibling, clear the pointer of the previous sibling */
1849  if (!node->next) {
1850  /* node also has a parent pointing to it, hand the inv_rev pointer to
1851  * the previous sibling */
1852  if (node->env) {
1853  node->env->inv_rev = node->prev;
1854  } else {
1855  /* no parent, this must be the foot then, so move it to the previous
1856  * node */
1857  widget_list_foot = node->prev;
1858  }
1859 
1860  node->prev->next = NULL;
1861  } else {
1862  /* link up the adjacent nodes */
1863  node->prev->next = node->next;
1864  node->next->prev = node->prev;
1865  }
1866 
1867  /* Insert node at the head of its container, or make it the root node if it
1868  * is not in a container. */
1869 
1870  /* Node is now the first sibling so the parent should point to it. */
1871  if (node->env) {
1872  node->next = node->env->inv;
1873  node->env->inv = node;
1874  } else {
1875  /* We are out of containers and this node is about to become the first
1876  * sibling, which means it's taking the place of the root node. */
1877  node->next = widget_list_head;
1878  widget_list_head = node;
1879  }
1880 
1881  /* Point the former head node to this node. */
1882  node->next->prev = node;
1883  /* There's no siblings in front of node now. */
1884  node->prev = NULL;
1885 
1886 #ifdef DEBUG_WIDGET
1887  LOG(DEBUG, "..AFTER:");
1888  LOG(DEBUG, "....node: %p - %s", node, node->name);
1889  LOG(DEBUG, "....node->env: %p - %s", node->env, node->env ? node->env->name : "NULL");
1890  LOG(DEBUG, "....node->prev: %p - %s, node->next: %p - %s", node->prev, node->prev ? node->prev->name : "NULL", node->next, node->next ? node->next->name : "NULL");
1891  LOG(DEBUG, "....node->inv: %p - %s, node->inv_rev: %p - %s", node->inv, node->inv ? node->inv->name : "NULL", node->inv_rev, node->inv_rev ? node->inv_rev->name : "NULL");
1892 
1893  LOG(DEBUG, "..SetPriorityWidget(): Done.");
1894 #endif
1895 }
1896 
1903 {
1904  if (!node) {
1905  return;
1906  }
1907 
1908  if (!node->next) {
1909  return;
1910  }
1911 
1912  if (!node->prev) {
1913  if (node->env) {
1914  node->env->inv_rev = node->next;
1915  } else {
1916  widget_list_head = node->next;
1917  }
1918 
1919  node->next->prev = NULL;
1920  } else {
1921  node->next->prev = node->prev;
1922  node->prev->next = node->next;
1923  }
1924 
1925  if (node->env) {
1926  node->prev = node->env->inv;
1927  node->env->inv = node;
1928  } else {
1929  node->prev = widget_list_foot;
1930  widget_list_foot = node;
1931  }
1932 
1933  node->prev->next = node;
1934  node->next = NULL;
1935 }
1936 
1937 void insert_widget_in_container(widgetdata *widget_container, widgetdata *widget, int absolute)
1938 {
1939  _widget_container *container;
1940  _widget_container_strip *container_strip;
1941 
1942  /* sanity checks */
1943  if (!widget_container || !widget) {
1944  return;
1945  }
1946 
1947  /* no, we don't want to end the universe just yet... */
1948  if (widget_container == widget) {
1949  return;
1950  }
1951 
1952  /* is the widget already in a container? */
1953  if (widget->env) {
1954  return;
1955  }
1956 
1957  /* if the widget isn't a container, get out of here */
1958  if (widget_container->type != CONTAINER_ID) {
1959  return;
1960  }
1961 
1962  /* we have our container, now we attempt to place the widget inside it */
1963  container = CONTAINER(widget_container);
1964 
1965  /* check to see if the widget is compatible with it */
1966  if (container->widget_type != -1 && container->widget_type != widget->type) {
1967  return;
1968  }
1969 
1970  /* if we get here, we now proceed to insert the widget into the container */
1971 
1972  /* snap the widget into the widget container if it is a strip container */
1973  if (widget_container->inv) {
1974  switch (widget_container->sub_type) {
1975  case CONTAINER_STRIP_ID:
1976  case MENU_ID:
1977  case MENUITEM_ID:
1978  container_strip = CONTAINER_STRIP(widget_container);
1979 
1980  /* container is horizontal, insert the widget to the right of
1981  * the first widget in its inventory */
1982  if (container_strip->horizontal) {
1983  move_widget_rec(widget, widget_container->inv->x + widget_container->inv->w + container_strip->inner_padding - widget->x, widget_container->y + container->outer_padding_top - widget->y);
1984  } else {
1985  /* otherwise the container is vertical, so insert the widget
1986  * below the first child widget */
1987  move_widget_rec(widget, widget_container->x + container->outer_padding_left - widget->x, widget_container->inv->y + widget_container->inv->h + container_strip->inner_padding - widget->y);
1988  }
1989 
1990  break;
1991  }
1992  } else if (!absolute) {
1993  /* no widgets inside it yet, so snap it to the bounds of the container */
1994  move_widget(widget, widget_container->x + container->outer_padding_left - widget->x, widget_container->y + container->outer_padding_top - widget->y);
1995  }
1996 
1997  /* link up the adjacent nodes, there *should* be at least two nodes next to
1998  * each other here so no sanity checks should be required */
1999  if (!widget->prev) {
2000  /* widget is no longer the root now, pass it on to the next widget */
2001  if (widget == widget_list_head) {
2002  widget_list_head = widget->next;
2003  }
2004 
2005  widget->next->prev = NULL;
2006  } else if (!widget->next) {
2007  /* widget is no longer the foot, move it to the previous widget */
2008  if (widget == widget_list_foot) {
2009  widget_list_foot = widget->prev;
2010  }
2011 
2012  widget->prev->next = NULL;
2013  } else {
2014  widget->prev->next = widget->next;
2015  widget->next->prev = widget->prev;
2016  }
2017 
2018  /* the widget to be placed inside will have a new sibling next to it, or
2019  * NULL if it doesn't exist */
2020  widget->next = widget_container->inv;
2021  /* it's also going to be the first child node, so it has no siblings on the
2022  * other side */
2023  widget->prev = NULL;
2024 
2025  /* if inventory doesn't exist, set the end child pointer too */
2026  if (!widget_container->inv) {
2027  widget_container->inv_rev = widget;
2028  } else {
2029  /* otherwise, link the first child in the inventory to the widget about to
2030  * be inserted */
2031  widget_container->inv->prev = widget;
2032  }
2033 
2034  /* this new widget becomes the first widget in the container */
2035  widget_container->inv = widget;
2036  /* set the environment of the widget inside */
2037  widget->env = widget_container;
2038 
2039  /* resize the container to fit the new widget. a little dirty trick here,
2040  * we just resize the widget inside by nothing and it will trigger the
2041  * auto-resize */
2042  resize_widget(widget, RESIZE_RIGHT, widget->w);
2043  resize_widget(widget, RESIZE_BOTTOM, widget->h);
2044 }
2045 
2048 {
2049  widgetdata *tmp = widget;
2050 
2051  /* Sanity check. */
2052  if (!widget) {
2053  return NULL;
2054  }
2055 
2056  /* Get the outsidemost container if the widget is inside one. */
2057  while (tmp->env) {
2058  tmp = tmp->env;
2059  widget = tmp;
2060  }
2061 
2062  return widget;
2063 }
2064 
2065 widgetdata *get_innermost_container(widgetdata *widget)
2066 {
2067  widgetdata *tmp;
2068 
2069  for (tmp = widget; tmp; tmp = tmp->env) {
2070  if (tmp->type == CONTAINER_ID) {
2071  return tmp;
2072  }
2073  }
2074 
2075  return widget;
2076 }
2077 
2091 widgetdata *widget_find(widgetdata *where, int type, const char *id, SDL_Surface *surface)
2092 {
2093  widgetdata *tmp, *tmp2;
2094 
2095  if (!where) {
2096  where = widget_list_head;
2097  }
2098 
2099  for (tmp = where; tmp; tmp = tmp->next) {
2100  if ((type == -1 || tmp->type == type) && (id == NULL || (tmp->id != NULL && strcmp(tmp->id, id) == 0)) && (surface == NULL || tmp->surface == surface)) {
2101  return tmp;
2102  }
2103 
2104  if (tmp->inv) {
2105  tmp2 = widget_find(tmp->inv, type, id, surface);
2106 
2107  if (tmp2) {
2108  return tmp2;
2109  }
2110  }
2111  }
2112 
2113  return NULL;
2114 }
2115 
2116 widgetdata *widget_find_create_id(int type, const char *id)
2117 {
2118  widgetdata *tmp;
2119 
2120  tmp = widget_find(NULL, type, id, NULL);
2121 
2122  if (!tmp) {
2123  tmp = create_widget_object(type);
2124  tmp->id = estrdup(id);
2125  }
2126 
2127  return tmp;
2128 }
2129 
2140 static void widget_switch_focus_do(widgetdata *widget, int type, const char *id)
2141 {
2142  for (widgetdata *tmp = widget; tmp != NULL; tmp = tmp->prev) {
2143  if (!tmp->show) {
2144  continue;
2145  }
2146 
2147  if (tmp->type == type && (id == NULL || strcmp(tmp->id, id) == 0)) {
2148  SetPriorityWidget(tmp);
2149  return;
2150  }
2151 
2152  if (tmp->inv) {
2153  widget_switch_focus_do(tmp->inv_rev, type, id);
2154  }
2155  }
2156 }
2157 
2165 void widget_switch_focus(int type, const char *id)
2166 {
2167  widget_switch_focus_do(widget_list_foot, type, id);
2168 }
2169 
2170 /* wrapper function to get the outermost container the widget is inside before
2171  * moving it */
2172 void move_widget(widgetdata *widget, int x, int y)
2173 {
2174  widget = get_outermost_container(widget);
2175 
2176  move_widget_rec(widget, x, y);
2177 }
2178 
2179 /* move all widgets inside the container with the container at the same time */
2180 void move_widget_rec(widgetdata *widget, int x, int y)
2181 {
2182  /* widget doesn't exist, means the parent node has no children */
2183  if (!widget) {
2184  return;
2185  }
2186 
2187  /* no movement needed */
2188  if (x == 0 && y == 0) {
2189  return;
2190  }
2191 
2192  /* move the widget */
2193  widget->x += x;
2194  widget->y += y;
2195 
2196  /* here, we want to walk through the inventory of the widget, if it exists.
2197  * when we come across a widget, we move it like we did with the container.
2198  * we loop until we reach the last sibling, but we also need to go recursive
2199  * if we find a child node */
2200  for (widget = widget->inv; widget; widget = widget->next) {
2201  move_widget_rec(widget, x, y);
2202  }
2203 }
2204 
2205 void resize_widget(widgetdata *widget, int side, int offset)
2206 {
2207  int x = widget->x;
2208  int y = widget->y;
2209  int width = widget->w;
2210  int height = widget->h;
2211 
2212  if (side & RESIZE_LEFT) {
2213  x = widget->x + widget->w - offset;
2214  width = offset;
2215  } else if (side & RESIZE_RIGHT) {
2216  width = offset;
2217  }
2218 
2219  if (side & RESIZE_TOP) {
2220  y = widget->y + widget->h - offset;
2221  height = offset;
2222  } else if (side & RESIZE_BOTTOM) {
2223  height = offset;
2224  }
2225 
2226  resize_widget_rec(widget, x, width, y, height);
2227 }
2228 
2229 void resize_widget_rec(widgetdata *widget, int x, int width, int y, int height)
2230 {
2231  widgetdata *widget_container, *tmp, *cmp1, *cmp2, *cmp3, *cmp4;
2232  _widget_container_strip *container_strip = NULL;
2233  _widget_container *container = NULL;
2234 
2235  /* move the widget. this is the easy bit, watch as your eyes bleed when you
2236  * see the next thing we have to do */
2237  widget->x = x;
2238  widget->y = y;
2239  widget->w = width;
2240  widget->h = height;
2241 
2242  WIDGET_REDRAW(widget);
2243 
2244  /* now we get our parent node if it exists */
2245 
2246  /* loop until we hit the first sibling */
2247  for (widget_container = widget; widget_container->prev; widget_container = widget_container->prev) {
2248  }
2249 
2250  /* does the first sibling have a parent node? */
2251  if (widget_container->env) {
2252  /* ok, now we need to resize the parent too. but before we do this, we
2253  * need to see if other widgets inside should prevent it from
2254  * being resized. the code below is ugly, but necessary in order to
2255  * calculate the new size of the container. and one more thing...
2256  * MY EYES! THE GOGGLES DO NOTHING! */
2257 
2258  widget_container = widget_container->env;
2259  container = CONTAINER(widget_container);
2260 
2261  /* special case for strip containers */
2262  switch (widget_container->sub_type) {
2263  case CONTAINER_STRIP_ID:
2264  case MENU_ID:
2265  case MENUITEM_ID:
2266  container_strip = CONTAINER_STRIP(widget_container);
2267 
2268  /* we move all the widgets before or after the widget that got
2269  * resized, depending on which side got the resize */
2270  if (container_strip->horizontal) {
2271  /* now move everything we come across */
2272  move_widget_rec(widget, 0, widget_container->y + container->outer_padding_top - widget->y);
2273 
2274  /* every node before the widget we push right */
2275  for (tmp = widget->prev; tmp; tmp = tmp->prev) {
2276  move_widget_rec(tmp, tmp->next->x + tmp->next->w - tmp->x + container_strip->inner_padding, widget_container->y + container->outer_padding_top - tmp->y);
2277  }
2278 
2279  /* while every node after the widget we push left */
2280  for (tmp = widget->next; tmp; tmp = tmp->next) {
2281  move_widget_rec(tmp, tmp->prev->x - tmp->x - tmp->w - container_strip->inner_padding, widget_container->y + container->outer_padding_top - tmp->y);
2282  }
2283 
2284  /* we have to set this, otherwise stupid things happen */
2285  x = widget_container->inv_rev->x;
2286  /* we don't want the container moving up or down in this
2287  * case */
2288  y = widget_container->y + container->outer_padding_top;
2289  } else {
2290  /* now move everything we come across */
2291  move_widget_rec(widget, widget_container->x + container->outer_padding_left - widget->x, 0);
2292 
2293  /* every node before the widget we push downwards */
2294  for (tmp = widget->prev; tmp; tmp = tmp->prev) {
2295  move_widget_rec(tmp, widget_container->x + container->outer_padding_left - tmp->x, tmp->next->y + tmp->next->h - tmp->y + container_strip->inner_padding);
2296  }
2297 
2298  /* while every node after the widget we push upwards */
2299  for (tmp = widget->next; tmp; tmp = tmp->next) {
2300  move_widget_rec(tmp, widget_container->x + container->outer_padding_left - tmp->x, tmp->prev->y - tmp->y - tmp->h - container_strip->inner_padding);
2301  }
2302 
2303  /* we don't want the container moving sideways in this case
2304  * */
2305  x = widget_container->x + container->outer_padding_left;
2306  /* we have to set this, otherwise stupid things happen */
2307  y = widget_container->inv_rev->y;
2308  }
2309 
2310  break;
2311  }
2312 
2313  if (!widget->show) {
2314  for (tmp = widget_container->inv; tmp != NULL; tmp = tmp->next) {
2315  if (tmp->show) {
2316  break;
2317  }
2318  }
2319 
2320  widget = tmp;
2321 
2322  if (widget) {
2323  x = widget->x;
2324  y = widget->y;
2325  width = widget->w;
2326  height = widget->h;
2327  } else {
2328  x = y = width = height = 0;
2329  }
2330  }
2331 
2332  /* TODO: add the buffer system so that this mess of code will only need
2333  * to be executed after the user stops resizing the widget */
2334  cmp1 = cmp2 = cmp3 = cmp4 = widget;
2335 
2336  for (tmp = widget_container->inv; tmp; tmp = tmp->next) {
2337  if (!tmp->show) {
2338  continue;
2339  }
2340 
2341  /* widget's left x co-ordinate becomes greater than tmp's left x
2342  * coordinate */
2343  if (cmp1->x > tmp->x) {
2344  x = tmp->x;
2345  width += cmp1->x - tmp->x;
2346  cmp1 = tmp;
2347  }
2348 
2349  /* widget's top y co-ordinate becomes greater than tmp's top y
2350  * coordinate */
2351  if (cmp2->y > tmp->y) {
2352  y = tmp->y;
2353  height += cmp2->y - tmp->y;
2354  cmp2 = tmp;
2355  }
2356 
2357  /* widget's right x co-ordinate becomes less than tmp's right x
2358  * coordinate */
2359  if (cmp3->x + cmp3->w < tmp->x + tmp->w) {
2360  width += tmp->x + tmp->w - cmp3->x - cmp3->w;
2361  cmp3 = tmp;
2362  }
2363 
2364  /* widget's bottom y co-ordinate becomes less than tmp's bottom y
2365  * coordinate */
2366  if (cmp4->y + cmp4->h < tmp->y + tmp->h) {
2367  height += tmp->y + tmp->h - cmp4->y - cmp4->h;
2368  cmp4 = tmp;
2369  }
2370  }
2371 
2372  x -= container->outer_padding_left;
2373  y -= container->outer_padding_top;
2374  width += container->outer_padding_left + container->outer_padding_right;
2375  height += container->outer_padding_top + container->outer_padding_bottom;
2376 
2377  /* after all that, we now check to see if the parent needs to be resized
2378  * before we waste even more resources going recursive */
2379  if (x != widget_container->x || y != widget_container->y || width != widget_container->w || height != widget_container->h) {
2380  resize_widget_rec(widget_container, x, width, y, height);
2381  }
2382  }
2383 }
2384 
2387 widgetdata *add_label(const char *text, font_struct *font, const char *color)
2388 {
2389  widgetdata *widget;
2390  _widget_label *label;
2391 
2392  widget = create_widget_object(LABEL_ID);
2393  SOFT_ASSERT_RC(widget != NULL, NULL, "Failed to create a label widget.");
2394  label = LABEL(widget);
2395 
2396  label->text = estrdup(text);
2397  label->color = color;
2398 
2399  font_free(label->font);
2400  FONT_INCREF(font);
2401  label->font = font;
2402 
2403  resize_widget(widget, RESIZE_RIGHT, text_get_width(font, text, TEXT_MARKUP));
2404  resize_widget(widget, RESIZE_BOTTOM, text_get_height(font, text, TEXT_MARKUP) + 3);
2405 
2406  return widget;
2407 }
2408 
2410 widgetdata *add_texture(const char *texture)
2411 {
2412  widgetdata *widget;
2413  _widget_texture *widget_texture;
2414 
2415  widget = create_widget_object(TEXTURE_ID);
2416  SOFT_ASSERT_RC(widget != NULL, NULL, "Failed to create a texture widget.");
2417  widget_texture = WIDGET_TEXTURE(widget);
2418 
2419  widget_texture->texture = texture_get(TEXTURE_TYPE_CLIENT, texture);
2420 
2421  resize_widget(widget, RESIZE_RIGHT, texture_surface(widget_texture->texture)->w);
2422  resize_widget(widget, RESIZE_BOTTOM, texture_surface(widget_texture->texture)->h);
2423 
2424  return widget;
2425 }
2426 
2428 widgetdata *create_menu(int x, int y, widgetdata *owner)
2429 {
2430  widgetdata *widget_menu = create_widget_object(MENU_ID);
2431  _widget_container *container_menu = CONTAINER(widget_menu);
2432  _widget_container_strip *container_strip_menu = CONTAINER_STRIP(widget_menu);
2433 
2434  /* Place the menu at these co-ordinates. */
2435  widget_menu->x = x;
2436  widget_menu->y = y;
2437  /* Point the menu to the owner. */
2438  (MENU(widget_menu))->owner = owner;
2439  /* Magic numbers for now, maybe it will be possible in future to customize
2440  * this in files. */
2441  container_menu->outer_padding_left = 2;
2442  container_menu->outer_padding_right = 2;
2443  container_menu->outer_padding_top = 2;
2444  container_menu->outer_padding_bottom = 2;
2445  container_strip_menu->inner_padding = 0;
2446 
2447  return widget_menu;
2448 }
2449 
2451 void add_menuitem(widgetdata *menu, const char *text, void (*menu_func_ptr)(widgetdata *, widgetdata *, SDL_Event *event), int menu_type, int val)
2452 {
2453  widgetdata *widget_menuitem, *widget_label, *widget_texture, *tmp;
2454  _widget_container *container_menuitem, *container_menu;
2455  _widget_container_strip *container_strip_menuitem;
2456  _menuitem *menuitem;
2457 
2458  widget_menuitem = create_widget_object(MENUITEM_ID);
2459  SOFT_ASSERT(widget_menuitem != NULL, "Failed to create a menuitem widget.");
2460 
2461  container_menuitem = CONTAINER(widget_menuitem);
2462  container_strip_menuitem = CONTAINER_STRIP(widget_menuitem);
2463 
2464  /* Initialize attributes. */
2465  container_menuitem->outer_padding_left = 4;
2466  container_menuitem->outer_padding_right = 2;
2467  container_menuitem->outer_padding_top = 2;
2468  container_menuitem->outer_padding_bottom = 0;
2469  container_strip_menuitem->inner_padding = 4;
2470  container_strip_menuitem->horizontal = 1;
2471 
2472  widget_label = add_label(text, FONT_ARIAL10, COLOR_WHITE);
2473 
2474  if (menu_type == MENU_CHECKBOX) {
2475  widget_texture = add_texture(val ? "checkbox_on" : "checkbox_off");
2476  insert_widget_in_container(widget_menuitem, widget_texture, 0);
2477  } else if (menu_type == MENU_RADIO) {
2478  widget_texture = add_texture(val ? "radio_on" : "radio_off");
2479  insert_widget_in_container(widget_menuitem, widget_texture, 0);
2480  }
2481 
2482  insert_widget_in_container(widget_menuitem, widget_label, 0);
2483  insert_widget_in_container(menu, widget_menuitem, 0);
2484 
2485  /* Add the pointer to the function to the menuitem. */
2486  menuitem = MENUITEM(widget_menuitem);
2487  menuitem->menu_func_ptr = menu_func_ptr;
2488  menuitem->menu_type = menu_type;
2489  menuitem->val = val;
2490 
2491  /* Sanity check. Menuitems should always exist inside a menu. */
2492  if (widget_menuitem->env && widget_menuitem->env->sub_type == MENU_ID) {
2493  container_menu = CONTAINER(widget_menuitem->env);
2494 
2495  /* Resize labels in each menuitem to the width of the menu. */
2496  for (tmp = widget_menuitem; tmp; tmp = tmp->next) {
2497  if (tmp->inv) {
2498  container_menuitem = CONTAINER(tmp);
2499 
2500  if (menu_type == MENU_CHECKBOX || menu_type == MENU_RADIO) {
2501  resize_widget(tmp->inv, RESIZE_RIGHT, menu->w - tmp->inv_rev->w - container_strip_menuitem->inner_padding - container_menu->outer_padding_left - container_menu->outer_padding_right - container_menuitem->outer_padding_left - container_menuitem->outer_padding_right);
2502  } else {
2503  resize_widget(tmp->inv, RESIZE_RIGHT, menu->w - container_menu->outer_padding_left - container_menu->outer_padding_right - container_menuitem->outer_padding_left - container_menuitem->outer_padding_right);
2504  }
2505  }
2506  }
2507  }
2508 }
2509 
2512 {
2513  (void) widget;
2514 }
2515 
2525 {
2526  int xoff = 0, yoff = 0;
2527 
2528  /* Would the menu go over the maximum screen width? */
2529  if (widget->x + widget->w > ScreenSurface->w) {
2530  /* Will appear to the left of the cursor instead of right of it. */
2531  xoff = -widget->w;
2532 
2533  /* Take submenus into account, and shift them depending on the
2534  * parent menu's width. */
2535  if (widget->type_prev && widget->type_prev->sub_type == MENU_ID) {
2536  xoff += -widget->type_prev->w + 4;
2537  }
2538  }
2539 
2540  /* Similar checks for screen height. */
2541  if (widget->y + widget->h > ScreenSurface->h) {
2542  /* Submenu, shift it up, so all of it can appear. */
2543  if (widget->type_prev && widget->type_prev->sub_type == MENU_ID) {
2544  yoff = ScreenSurface->h - widget->h - widget->y - 1;
2545  } else {
2546  /* Will appear above the cursor. */
2547  yoff = -widget->h;
2548  }
2549  }
2550 
2551  move_widget(widget, xoff, yoff);
2552 }
2553 
2555 void widget_redraw_all(int widget_type_id)
2556 {
2557  widgetdata *widget;
2558 
2559  for (widget = cur_widget[widget_type_id]; widget; widget = widget->type_next) {
2560  widget->redraw = 1;
2561  }
2562 }
2563 
2564 void widget_redraw_type_id(int type, const char *id)
2565 {
2566  widgetdata *widget;
2567 
2568  for (widget = cur_widget[type]; widget; widget = widget->type_next) {
2569  if (widget->id && strcmp(widget->id, id) == 0) {
2570  widget->redraw = 1;
2571  }
2572  }
2573 }
2574 
2582 void widget_show(widgetdata *widget, int show)
2583 {
2584  /* Visibility is already the same, nothing to do. */
2585  if (widget->show == show) {
2586  return;
2587  }
2588 
2589  widget->show = show;
2590 
2591  if (show) {
2592  widget->showed_ticks = SDL_GetTicks();
2593  }
2594 
2595  /* So that containers can factor in the widget's visibility */
2596  resize_widget(widget, 0, 0);
2597 
2598  /* TODO: make a callback function for this? seems a bit hacky ATM */
2599  if (widget->type == INVENTORY_ID) {
2600  if (!show) {
2601  const char *id = strcmp(widget->id, "main") == 0 ? "below" : "main";
2602  widget = widget_find(NULL, widget->type, id, NULL);
2603  SOFT_ASSERT(widget != NULL, "Could not find inventory widget: %s",
2604  id);
2605  }
2606 
2607  SetPriorityWidget(widget);
2608  widget->redraw = 1;
2609  }
2610 }
2611 
2617 void widget_show_toggle_all(int type_id)
2618 {
2619  widgetdata *widget;
2620 
2621  for (widget = cur_widget[type_id]; widget; widget = widget->type_next) {
2622  WIDGET_SHOW_TOGGLE(widget);
2623  }
2624 }
2625 
2626 void menu_move_widget(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
2627 {
2628  widget_event_start_move(widget);
2629 }
2630 
2631 void menu_create_widget(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
2632 {
2633  widgetdata *tmp;
2634 
2635  tmp = create_widget_object(widget->sub_type);
2636  SOFT_ASSERT(tmp != NULL, "Failed to create a widget of type %d",
2637  widget->sub_type);
2638  tmp->x = menuitem->env->x;
2639  tmp->y = menuitem->env->y;
2641 }
2642 
2643 void menu_remove_widget(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
2644 {
2645  remove_widget_object(widget);
2646 }
2647 
2648 void menu_detach_widget(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
2649 {
2650  detach_widget(widget);
2651 }
2652 
2653 void menu_inventory_submenu_quickslots(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
2654 {
2655 }
2656 
2661 {
2662  widget_render_debug = 1;
2663 }
int type
Definition: widget.h:118
int outer_padding_left
Definition: widget.h:178
void remove_widget_object(widgetdata *widget)
Definition: widget.c:460
int outer_padding_top
Definition: widget.h:170
static void process_widgets_rec(int draw, widgetdata *widget)
Definition: widget.c:1673
int widget_y(const widgetdata *widget)
Definition: widget.c:633
static widgetdata * widget_list_foot
Definition: widget.c:68
#define COLOR_BLACK
Definition: text.h:323
void kill_widgets(void)
Definition: widget.c:573
void remove_widget(widgetdata *widget)
Definition: widget.c:899
struct widgetdata * env
Definition: widget.h:98
void widgets_reset(void)
Definition: widget.c:590
static int widget_menu_handle(widgetdata *widget, SDL_Event *event)
Definition: widget.c:283
void widget_inventory_init(widgetdata *widget)
Definition: inventory.c:695
void widget_active_effects_init(widgetdata *widget)
void widget_stat_init(widgetdata *widget)
Definition: stat.c:282
void * subwidget
Definition: widget.h:107
struct widgetdata * prev
Definition: widget.h:89
static bool widget_mouse_over(widgetdata *widget, int x, int y)
Definition: widget.c:732
SDL_Surface * texture_surface(texture_struct *texture)
Definition: texture.c:303
static void widget_ensure_onscreen(widgetdata *widget)
Definition: widget.c:766
SDL_Surface * surface
Definition: widget.h:110
int sub_type
Definition: widget.h:122
void widget_notification_init(widgetdata *widget)
Definition: notification.c:287
int widgets_need_redraw(void)
Definition: widget.c:1661
char * name
Definition: widget.h:39
void widget_buddy_init(widgetdata *widget)
Definition: buddy.c:477
double zoom
Zoom factor of the widget.
Definition: widget.h:142
int widget_event_respond(int x, int y)
Definition: widget.c:1546
void menu_finalize(widgetdata *widget)
Definition: widget.c:2524
void widget_texture_init(widgetdata *widget)
Definition: texture.c:50
void widget_protections_init(widgetdata *widget)
Definition: protections.c:88
struct widgetdata * type_next
Definition: widget.h:101
int active
Definition: widget.h:372
texture_struct * texture_get(texture_type_t type, const char *name)
Definition: texture.c:279
void font_free(font_struct *font)
Definition: text.c:262
struct widgetdata * next
Definition: widget.h:86
int hidden
Definition: widget.h:140
void widget_party_init(widgetdata *widget)
Definition: party.c:420
SDL_Surface * ScreenSurface
Definition: main.c:47
#define TEXT_MARKUP
Definition: text.h:224
font_struct * font
Definition: widget.h:213
void SetPriorityWidget_reverse(widgetdata *node)
Definition: widget.c:1902
struct widgetdata * inv
Definition: widget.h:92
void remove_widget_inv(widgetdata *widget)
Definition: widget.c:553
char * id
Definition: widget.h:42
static widgetmove widget_event_move
Definition: widget.c:87
void widget_render_enable_debug(void)
Definition: widget.c:2660
widgetdata * widget_find(widgetdata *where, int type, const char *id, SDL_Surface *surface)
Definition: widget.c:2091
widgetdata * get_widget_owner(int x, int y, widgetdata *start, widgetdata *end)
Definition: widget.c:1563
void widget_skills_init(widgetdata *widget)
Definition: skills.c:444
texture_struct * texture
Definition: widget.h:113
widgetdata * owner
Definition: widget.h:390
int menu_type
Definition: widget.h:274
uint8_t show
Definition: widget.h:69
widgetdata * create_widget_object(int widget_subtype_id)
Definition: widget.c:414
char bg[32]
Definition: widget.h:45
void widget_playerinfo_init(widgetdata *widget)
Definition: playerinfo.c:127
void widgets_ensure_onscreen(void)
Definition: widget.c:804
int widget_h(const widgetdata *widget)
Definition: widget.c:665
int text_get_height(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2364
void widget_container_init(widgetdata *widget)
Definition: container.c:142
static int widgets_need_redraw_rec(widgetdata *widget)
Definition: widget.c:1638
Client_Player cpl
Definition: client.c:50
void widget_network_graph_init(widgetdata *widget)
static widgetresize widget_event_resize
Definition: widget.c:92
void widget_show(widgetdata *widget, int show)
Definition: widget.c:2582
uint8_t required
Definition: widget.h:78
void widget_label_init(widgetdata *widget)
Definition: label.c:64
uint8_t save
Definition: widget.h:83
void texture_delete(texture_struct *texture)
Definition: texture.c:208
#define FONT_INCREF(font)
Definition: text.h:67
int xOffset
Definition: widget.h:378
bool widget_set_zoom(widgetdata *widget, double zoom)
Definition: widget.c:681
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
widgetdata * cur_widget[TOTAL_SUBWIDGETS]
Definition: widget.c:75
uint8_t redraw
Definition: widget.h:72
void toolkit_widget_init(void)
Definition: widget.c:243
uint8_t moveable
Definition: widget.h:66
static widgetdata * widget_list_head
Definition: widget.c:66
void widget_show_toggle_all(int type_id)
Definition: widget.c:2617
widgetdata * add_texture(const char *texture)
Definition: widget.c:2410
int zoom_x
Definition: widget.h:60
widgetdata * get_outermost_container(widgetdata *widget)
Definition: widget.c:2047
void add_separator(widgetdata *widget)
Definition: widget.c:2511
int x
Definition: widget.h:48
void widget_mapname_init(widgetdata *widget)
Definition: mapname.c:105
#define WIDGET_REDRAW(__tmp)
Definition: widget.h:398
static int widget_render_debug
Definition: widget.c:106
void widget_map_init(widgetdata *widget)
Definition: map.c:2471
int widgets_event(SDL_Event *event)
Definition: widget.c:1205
int active
Definition: widget.h:387
void(* menu_func_ptr)(widgetdata *, widgetdata *, SDL_Event *event)
Definition: widget.h:271
widgetdata * owner
Definition: widget.h:375
int yOffset
Definition: widget.h:381
widgetdata * create_menu(int x, int y, widgetdata *owner)
Definition: widget.c:2428
int widget_x(const widgetdata *widget)
Definition: widget.c:615
int w
Definition: widget.h:54
static void widget_switch_focus_do(widgetdata *widget, int type, const char *id)
Definition: widget.c:2140
int h
Definition: widget.h:57
void widget_spells_init(widgetdata *widget)
Definition: spells.c:598
void surface_show_fill(SDL_Surface *surface, int x, int y, SDL_Rect *srcsize, SDL_Surface *src, SDL_Rect *box)
Definition: sprite.c:792
int text_get_width(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2328
void widget_redraw_all(int widget_type_id)
Definition: widget.c:2555
char * file_path(const char *path, const char *mode)
Definition: wrapper.c:369
void surface_show(SDL_Surface *surface, int x, int y, SDL_Rect *srcrect, SDL_Surface *src)
Definition: sprite.c:761
void widget_quickslots_init(widgetdata *widget)
Definition: quickslots.c:376
void process_widgets(int draw)
Definition: widget.c:1770
widgetdata * inventory_focus
Definition: player.h:200
int zoom_y
Definition: widget.h:63
void widget_fps_init(widgetdata *widget)
Definition: fps.c:107
widgetdata * owner
Definition: widget.h:360
void remove_widget_object_intern(widgetdata *widget)
Definition: widget.c:478
void SetPriorityWidget(widgetdata *node)
Definition: widget.c:1788
widgetevent widget_mouse_event
Definition: widget.c:82
void text_show_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2289
const char * color
Definition: widget.h:216
struct widgetdata * type_prev
Definition: widget.h:104
int outer_padding_bottom
Definition: widget.h:174
static bool widget_is_ellipse(widgetdata *widget)
Definition: widget.c:708
int widget_event_start_move(widgetdata *widget)
Definition: widget.c:1484
void detach_widget(widgetdata *widget)
Definition: widget.c:995
widgetdata * add_label(const char *text, font_struct *font, const char *color)
Definition: widget.c:2387
static int IsMouseExclusive
Definition: widget.c:101
int outer_padding_right
Definition: widget.h:182
struct widgetdata * inv_rev
Definition: widget.h:95
int widget_w(const widgetdata *widget)
Definition: widget.c:651
void add_menuitem(widgetdata *menu, const char *text, void(*menu_func_ptr)(widgetdata *, widgetdata *, SDL_Event *event), int menu_type, int val)
Definition: widget.c:2451
void widget_target_init(widgetdata *widget)
Definition: target.c:192
void widget_switch_focus(int type, const char *id)
Definition: widget.c:2165
void widget_mplayer_init(widgetdata *widget)
Definition: mplayer.c:595
widgetdata * create_widget(int widget_id)
Definition: widget.c:845
void kill_widget_tree(widgetdata *widget)
Definition: widget.c:814
#define COLOR_WHITE
Definition: text.h:289
texture_struct * texture
Definition: widget.h:221
int y
Definition: widget.h:51
char * text
Definition: widget.h:210