Atrinik Client 2.5
toolkit/widget.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00038 #include <global.h>
00039 
00040 static int load_interface_file(char *filename);
00041 static void process_widget(widgetdata *widget);
00042 
00044 static widgetdata def_widget[TOTAL_SUBWIDGETS];
00045 
00047 /* {name, x1, y1, wd, ht, moveable?, show?, redraw?, unique?, no_kill?, visible?, delete_inv?, save?, save_width_height?
00048  * * the next members are used internally *
00049  * next(NULL), prev(NULL), inv(NULL), inv_rev(NULL), env(NULL), type_next(NULL), type_prev(NULL),
00050  * subwidget(NULL), widgetSF(NULL), WidgetTypeID(0), WidgetSubtypeID(0), WidgetObjID(0)} */
00051 static const widgetdata con_widget[TOTAL_SUBWIDGETS] =
00052 {
00053     /* base widgets */
00054     {"MAP", 0, 10, 850, 600, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 1},
00055     {"STATS",           227,   0, 172, 102, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00056     {"RESIST",          497,   0, 198,  79, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00057     {"MAIN_LVL",        399,  39,  98,  62, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00058     {"SKILL_EXP",       497,  79, 198,  22, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00059     {"REGEN",           399,   0,  98,  39, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00060     {"SKILL_LVL",       695,   0,  52, 101, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00061     {"MENUBUTTONS",     747,   0,  47, 101, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00062     {"QUICKSLOTS",      735, 489, 282,  34, 1, 1, 1, 1, 1, 1, 1, 1, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00063     {"CHATWIN",         631, 540, 392, 226, 1, 1, 1, 0, 1, 1, 1, 1, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 1, 80, 80, 0, 0},
00064     {"MSGWIN",            1, 540, 308, 226, 1, 1, 1, 0, 1, 1, 1, 1, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 1, 80, 80, 0, 0},
00065     {"PLAYERDOLL",        0,  41, 219, 243, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00066     {"BELOWINV",        331, 713, 274,  55, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00067     {"PLAYERINFO",        0,   0, 219,  41, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00068     {"RANGEBOX",          6,  51,  94,  60, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00069     {"TARGET",          336, 681, 264,  31, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00070     {"MAININV",           1, 508, 271,  32, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00071     {"MAPNAME",         228, 106,  36,  16, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00072     {"CONSOLE",         339, 655, 256,  25, 1, 0, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00073     {"NUMBER",          340, 637, 256,  43, 1, 0, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00074     {"FPS",             123,  47,  70,  22, 1, 1, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00075     {"MPLAYER", 474, 101, 320, 190, 1, 0, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00076     {"SPELLS", 474, 101, 320, 190, 1, 0, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00077     {"SKILLS", 474, 101, 320, 190, 1, 0, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00078     {"PARTY", 474, 101, 320, 190, 1, 0, 1, 1, 1, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00079     {"CONTAINER",         0,   0, 128, 128, 1, 0, 1, 0, 1, 1, 0, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00080     {"LABEL",             0,   0,   5,   5, 1, 1, 1, 0, 0, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00081     {"BITMAP",            0,   0,   5,   5, 1, 1, 1, 0, 0, 1, 1, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00082     /* subwidgets */
00083     {"CONTAINER_STRIP",   0,   0, 128, 128, 1, 0, 1, 0, 1, 1, 0, 1, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00084     {"MENU",              0,   0,   5,   5, 0, 1, 1, 0, 0, 1, 1, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00085     {"MENUITEM",          0,   0,   5,   5, 0, 1, 1, 0, 0, 0, 1, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0},
00086 };
00087 
00088 
00089 /* Default overall priority tree. Will change during runtime.
00090  * Widget at the top (head) of the tree has highest priority.
00091  * Events go to the top (head) of the tree first.
00092  * Displaying goes to the right (foot) of the tree first. */
00094 static widgetdata *widget_list_head;
00096 static widgetdata *widget_list_foot;
00097 
00101 /* TODO: change cur_widget to type_list_head */
00102 widgetdata *cur_widget[TOTAL_SUBWIDGETS];
00103 static widgetdata *type_list_foot[TOTAL_SUBWIDGETS];
00104 
00108 widgetevent widget_mouse_event =
00109 {
00110     NULL, 0, 0
00111 };
00112 
00114 static widgetmove widget_event_move =
00115 {
00116     0, NULL, 0, 0
00117 };
00118 
00120 static widgetresize widget_event_resize =
00121 {
00122     0, NULL
00123 };
00124 
00128 static int IsMouseExclusive = 0;
00129 
00133 static void init_widgets_fromDefault()
00134 {
00135     int lp;
00136 
00137     /* Exit, if there are no widget IDs */
00138     if (!TOTAL_SUBWIDGETS)
00139     {
00140         return;
00141     }
00142 
00143     /* Store the constant default widget lookup in the current lookup(s) */
00144     for (lp = 0; lp < TOTAL_SUBWIDGETS; ++lp)
00145     {
00146         def_widget[lp] = con_widget[lp];
00147     }
00148 
00149     /* Initiate the linked lists for the widgets. */
00150     init_widgets();
00151 }
00152 
00156 void init_widgets_fromCurrent()
00157 {
00158     /* Exit, if there are no widgets */
00159     if (!TOTAL_SUBWIDGETS)
00160     {
00161         return;
00162     }
00163 
00164     /* If can't open/load the interface file load defaults and create file */
00165     if (!load_interface_file(INTERFACE_FILE))
00166     {
00167         /* Inform user */
00168         LOG(llevInfo, "Can't open/load the interface file - %s. Resetting\n", INTERFACE_FILE);
00169 
00170         /* Load the defaults - this also allocates priority list */
00171         init_widgets_fromDefault();
00172 
00173         /* Create the interface file */
00174         save_interface_file();
00175     }
00176 }
00177 
00179 widgetdata *create_widget_object(int widget_subtype_id)
00180 {
00181     widgetdata *widget;
00182     textwin_struct *textwin;
00183     _widget_container *container;
00184     _widget_container_strip *container_strip;
00185     _menu *menu;
00186     _menuitem *menuitem;
00187     _widget_label *label;
00188     _widget_bitmap *bitmap;
00189     int widget_type_id = widget_subtype_id;
00190 
00191     /* map the widget subtype to widget type */
00192     if (widget_subtype_id >= TOTAL_WIDGETS)
00193     {
00194         switch (widget_subtype_id)
00195         {
00196             case CONTAINER_STRIP_ID:
00197             case MENU_ID:
00198             case MENUITEM_ID:
00199                 widget_type_id = CONTAINER_ID;
00200                 break;
00201 
00202             /* no subtype was found, so get out of here */
00203             default:
00204                 return NULL;
00205         }
00206     }
00207 
00208     /* sanity check */
00209     if (widget_subtype_id < 0 || widget_subtype_id >= TOTAL_SUBWIDGETS)
00210     {
00211         return NULL;
00212     }
00213 
00214     /* don't create more than one widget if it is a unique widget */
00215     if (con_widget[widget_subtype_id].unique && cur_widget[widget_subtype_id])
00216     {
00217         return NULL;
00218     }
00219 
00220     /* allocate the widget node, this should always be the first function called in here */
00221     widget = create_widget(widget_subtype_id);
00222     widget->WidgetTypeID = widget_type_id;
00223 
00224     /* allocate the custom attributes for the widget if applicable */
00225     switch (widget->WidgetTypeID)
00226     {
00227         case CHATWIN_ID:
00228         case MSGWIN_ID:
00229             textwin = calloc(1, sizeof(textwin_struct));
00230 
00231             if (!textwin)
00232             {
00233                 exit(0);
00234             }
00235 
00236             textwin->font = FONT_ARIAL11;
00237             textwin->selection_start = -1;
00238             textwin->selection_end = -1;
00239             /* that's right, a void * cast to _textwin *.
00240              * usually it's not a nice thing to do, but in this case it's an excellent way of extending a struct */
00241             widget->subwidget = (textwin_struct *) textwin;
00242             break;
00243 
00244         case MAPNAME_ID:
00245             /* set the bounding box to another one that exists, otherwise it can be wrong initially */
00246             if (cur_widget[MAPNAME_ID])
00247             {
00248                 widget->wd = cur_widget[MAPNAME_ID]->wd;
00249                 widget->ht = cur_widget[MAPNAME_ID]->ht;
00250             }
00251 
00252             break;
00253 
00254         case CONTAINER_ID:
00255             container = malloc(sizeof(_widget_container));
00256 
00257             if (!container)
00258             {
00259                 exit(0);
00260             }
00261 
00262             /* begin initializing the members */
00263             container->widget_type = -1;
00264             container->outer_padding_top = 10;
00265             container->outer_padding_bottom = 10;
00266             container->outer_padding_left = 10;
00267             container->outer_padding_right = 10;
00268             container->x_left_buf1 = 0;
00269             container->x_left_buf2 = 0;
00270             container->x_right_buf1 = 0;
00271             container->x_right_buf2 = 0;
00272             container->y_top_buf1 = 0;
00273             container->y_top_buf2 = 0;
00274             container->y_bottom_buf1 = 0;
00275             container->y_bottom_buf2 = 0;
00276             /* have the subwidget point to it */
00277             widget->subwidget = (_widget_container *) container;
00278 
00279             /* allocate the custom attributes for the container if applicable */
00280             switch (widget->WidgetSubtypeID)
00281             {
00282                 case CONTAINER_STRIP_ID:
00283                 case MENU_ID:
00284                 case MENUITEM_ID:
00285                     container_strip = malloc(sizeof(_widget_container_strip));
00286 
00287                     if (!container_strip)
00288                     {
00289                         exit(0);
00290                     }
00291 
00292                     /* Begin initializing the members. */
00293                     container_strip->inner_padding = 10;
00294                     container_strip->horizontal = 0;
00295                     container_strip->size = 0;
00296                     /* Have the subcontainer point to it. */
00297                     container->subcontainer = (_widget_container_strip *) container_strip;
00298 
00299                     /* Allocate the custom attributes for the strip container if applicable. */
00300                     switch (widget->WidgetSubtypeID)
00301                     {
00302                         case MENU_ID:
00303                             menu = malloc(sizeof(_menu));
00304 
00305                             if (!menu)
00306                             {
00307                                 exit(0);
00308                             }
00309 
00310                             /* Begin initializing the members. */
00311                             menu->submenu = NULL;
00312                             menu->owner = NULL;
00313                             /* Have the sub strip container point to it. */
00314                             container_strip->subcontainer_strip = (_menu *) menu;
00315                             break;
00316 
00317                         case MENUITEM_ID:
00318                             menuitem = malloc(sizeof(_menuitem));
00319 
00320                             if (!menuitem)
00321                             {
00322                                 exit(0);
00323                             }
00324 
00325                             /* Begin initializing the members. */
00326                             menuitem->menu_func_ptr = NULL;
00327                             menuitem->menu_type = MENU_NORMAL;
00328                             /* Have the sub strip container point to it. */
00329                             container_strip->subcontainer_strip = (_menuitem *) menuitem;
00330                             break;
00331                     }
00332 
00333                     break;
00334             }
00335 
00336             break;
00337 
00338         case LABEL_ID:
00339             label = malloc(sizeof(_widget_label));
00340 
00341             if (!label)
00342             {
00343                 exit(0);
00344             }
00345 
00346             /* begin initializing the members */
00347             label->text = "";
00348             label->font = FONT_ARIAL10;
00349             label->color = COLOR_WHITE;
00350             /* have the subwidget point to it */
00351             widget->subwidget = (_widget_label *) label;
00352             break;
00353 
00354         case BITMAP_ID:
00355             bitmap = malloc(sizeof(_widget_bitmap));
00356 
00357             if (!bitmap)
00358             {
00359                 exit(0);
00360             }
00361 
00362             /* begin initializing the members */
00363             bitmap->bitmap_id = 0;
00364             /* have the subwidget point to it */
00365             widget->subwidget = (_widget_bitmap *) bitmap;
00366             break;
00367     }
00368 
00369     return widget;
00370 }
00371 
00373 void remove_widget_object(widgetdata *widget)
00374 {
00375     /* don't delete the last widget if there needs to be at least one of this widget type */
00376     if (widget->no_kill && cur_widget[widget->WidgetSubtypeID] == type_list_foot[widget->WidgetSubtypeID])
00377     {
00378         return;
00379     }
00380 
00381     remove_widget_object_intern(widget);
00382 }
00383 
00388 void remove_widget_object_intern(widgetdata *widget)
00389 {
00390     widgetdata *tmp;
00391     _widget_container *container;
00392     _widget_container_strip *container_strip;
00393     int widget_subtype_id = widget->WidgetSubtypeID;
00394 
00395     /* If this flag is enabled, we need to delete all contents of the widget too, which calls for some recursion. */
00396     if (widget->delete_inv)
00397     {
00398         remove_widget_inv(widget);
00399     }
00400 
00401     /* If this widget happens to be the owner of an event, keeping them pointed to it is a bad idea. */
00402     if (widget_mouse_event.owner == widget)
00403     {
00404         widget_mouse_event.owner = NULL;
00405     }
00406 
00407     if (widget_event_move.owner == widget)
00408     {
00409         widget_event_move.owner = NULL;
00410     }
00411 
00412     if (widget_event_resize.owner == widget)
00413     {
00414         widget_event_resize.owner = NULL;
00415     }
00416 
00417     /* If any menu is open and this widget is the owner, bad things could happen here too. Clear the pointers. */
00418     if (cur_widget[MENU_ID] && (MENU(cur_widget[MENU_ID]))->owner == widget)
00419     {
00420         for (tmp = cur_widget[MENU_ID]; tmp; tmp = tmp->type_next)
00421         {
00422             (MENU(cur_widget[MENU_ID]))->owner = NULL;
00423         }
00424     }
00425 
00426     /* Get the environment if it exists, this is used to make containers auto-resize when the widget is deleted. */
00427     tmp = widget->env;
00428 
00429     /* remove the custom attribute nodes if they exist */
00430     if (widget->subwidget)
00431     {
00432         switch (widget_subtype_id)
00433         {
00434             case CONTAINER_STRIP_ID:
00435             case MENU_ID:
00436             case MENUITEM_ID:
00437                 if (widget_subtype_id == MENUITEM_ID)
00438                 {
00439                     container_strip = CONTAINER_STRIP(widget);
00440 
00441                     if (container_strip->subcontainer_strip)
00442                     {
00443                         free(container_strip->subcontainer_strip);
00444                         container_strip->subcontainer_strip = NULL;
00445                     }
00446                 }
00447 
00448                 container = CONTAINER(widget);
00449 
00450                 if (container->subcontainer)
00451                 {
00452                     free(container->subcontainer);
00453                     container->subcontainer = NULL;
00454                 }
00455 
00456                 break;
00457 
00458             case CHATWIN_ID:
00459             case MSGWIN_ID:
00460             {
00461                 textwin_struct *textwin = TEXTWIN(widget);
00462 
00463                 if (textwin->entries)
00464                 {
00465                     free(textwin->entries);
00466                 }
00467 
00468                 break;
00469             }
00470         }
00471 
00472         free(widget->subwidget);
00473         widget->subwidget = NULL;
00474     }
00475 
00476     switch (widget_subtype_id)
00477     {
00478         case MPLAYER_ID:
00479             widget_mplayer_deinit(widget);
00480             break;
00481     }
00482 
00483     /* finally de-allocate the widget node, this should always be the last node removed in here */
00484     remove_widget(widget);
00485 
00486     /* resize the container that used to contain this widget, if it exists */
00487     if (tmp)
00488     {
00489         /* if something else exists in its inventory, make it auto-resize to fit the widgets inside */
00490         if (tmp->inv)
00491         {
00492             resize_widget(tmp->inv, RESIZE_RIGHT, tmp->inv->wd);
00493             resize_widget(tmp->inv, RESIZE_BOTTOM, tmp->inv->ht);
00494         }
00495         /* otherwise if its inventory is empty, resize it to its default size */
00496         else
00497         {
00498             resize_widget(tmp, RESIZE_RIGHT, con_widget[tmp->WidgetSubtypeID].wd);
00499             resize_widget(tmp, RESIZE_BOTTOM, con_widget[tmp->WidgetSubtypeID].ht);
00500         }
00501     }
00502 }
00503 
00509 void remove_widget_inv(widgetdata *widget)
00510 {
00511     widgetdata *tmp;
00512 
00513     for (widget = widget->inv; widget; widget = tmp)
00514     {
00515         /* call this function recursively to get to the first child node deep down inside the widget */
00516         remove_widget_inv(widget);
00517         /* we need a temp pointer for the next node, as the current node is about to be no more */
00518         tmp = widget->next;
00519         /* then remove the widget, and slowly work our way up the tree deleting widgets until we get to the original widget again */
00520         remove_widget_object(widget);
00521     }
00522 }
00523 
00525 /* TODO: this is looking more and more like a function to simply initiate all the widgets with their default attributes,
00526  * as loading from a file now creates nodes dynamically instead, so this function is now doomed to that role */
00527 void init_widgets()
00528 {
00529     int i;
00530 
00531     /* exit, if there're no widgets */
00532     if (!TOTAL_SUBWIDGETS)
00533     {
00534         return;
00535     }
00536 
00537     /* in all cases should reset */
00538     kill_widgets();
00539 
00540     /* initiate the widget tree and everything else that links to it. */
00541     for (i = 0; i < TOTAL_SUBWIDGETS; ++i)
00542     {
00543         if (!con_widget[i].no_kill)
00544         {
00545             continue;
00546         }
00547 
00548         create_widget_object(i);
00549     }
00550 
00551     LOG(llevDebug, "..Allocated %d nodes!\n", i);
00552 }
00553 
00556 void kill_widgets()
00557 {
00558     /* get rid of the pointer to the widgets first */
00559     widget_mouse_event.owner = NULL;
00560     widget_event_move.owner = NULL;
00561     widget_event_resize.owner = NULL;
00562 
00563     /* kick off the chain reaction, there's no turning back now :) */
00564     if (widget_list_head)
00565     {
00566         kill_widget_tree(widget_list_head);
00567     }
00568 }
00569 
00573 void reset_widget(const char *name)
00574 {
00575     widgetdata *tmp;
00576 
00577     for (tmp = widget_list_head; tmp; tmp = tmp->next)
00578     {
00579         if (!tmp->moveable)
00580         {
00581             continue;
00582         }
00583 
00584         if (!name || !strcasecmp(tmp->name, name))
00585         {
00586             tmp->x1 = con_widget[tmp->WidgetTypeID].x1;
00587             tmp->y1 = con_widget[tmp->WidgetTypeID].y1;
00588             tmp->wd = con_widget[tmp->WidgetTypeID].wd;
00589             tmp->ht = con_widget[tmp->WidgetTypeID].ht;
00590             tmp->show = con_widget[tmp->WidgetTypeID].show;
00591             WIDGET_REDRAW(tmp);
00592         }
00593     }
00594 }
00595 
00599 static void widget_ensure_onscreen(widgetdata *widget)
00600 {
00601     int dx = 0, dy = 0;
00602 
00603     if (widget->x1 < 0)
00604     {
00605         dx = -widget->x1;
00606     }
00607     else if (widget->x1 + widget->wd > setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_X))
00608     {
00609         dx = setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_X) - widget->wd - widget->x1;
00610     }
00611 
00612     if (widget->y1 < 0)
00613     {
00614         dy = -widget->y1;
00615     }
00616     else if (widget->y1 + widget->ht > setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_Y))
00617     {
00618         dy = setting_get_int(OPT_CAT_CLIENT, OPT_RESOLUTION_Y) - widget->ht - widget->y1;
00619     }
00620 
00621     move_widget_rec(widget, dx, dy);
00622 }
00623 
00626 void widgets_ensure_onscreen()
00627 {
00628     widgetdata *tmp;
00629 
00630     for (tmp = widget_list_head; tmp; tmp = tmp->next)
00631     {
00632         widget_ensure_onscreen(tmp);
00633     }
00634 }
00635 
00637 void kill_widget_tree(widgetdata *widget)
00638 {
00639     widgetdata *tmp;
00640 
00641     do
00642     {
00643         /* we want to process the widgets starting from the left hand side of the tree first */
00644         if (widget->inv)
00645         {
00646             kill_widget_tree(widget->inv);
00647         }
00648 
00649         /* store the next sibling in a tmp variable, as widget is about to be zapped from existence */
00650         tmp = widget->next;
00651 
00652         /* here we call our widget kill function, and force removal by using the internal one */
00653         remove_widget_object_intern(widget);
00654 
00655         /* get the next sibling for our next loop */
00656         widget = tmp;
00657     }
00658     while (widget);
00659 }
00660 
00665 widgetdata *create_widget(int widget_id)
00666 {
00667     widgetdata *node;
00668     /* our unique widget count variable */
00669     static int widget_uid = 0;
00670 
00671 #ifdef DEBUG_WIDGET
00672     LOG(llevInfo, "Entering create_widget()..\n");
00673 #endif
00674 
00675     /* allocate it */
00676     node = malloc(sizeof(widgetdata));
00677 
00678     if (!node)
00679     {
00680         exit(0);
00681     }
00682 
00683     /* set the members */
00684     /* this also sets all pointers in the struct to NULL */
00685     *node = con_widget[widget_id];
00686     node->WidgetSubtypeID = widget_id;
00687     /* give it a unique ID */
00688     node->WidgetObjID = widget_uid;
00689 
00690     /* link it up to the tree if the root exists */
00691     if (widget_list_head)
00692     {
00693         node->next = widget_list_head;
00694         widget_list_head->prev = node;
00695     }
00696 
00697     /* set the foot if it doesn't exist */
00698     if (!widget_list_foot)
00699     {
00700         widget_list_foot = node;
00701     }
00702 
00703     /* the new node becomes the new root node, which also automatically brings it to the front */
00704     widget_list_head = node;
00705 
00706     /* if head of widget type linked list doesn't exist, set the head and foot */
00707     if (!cur_widget[widget_id])
00708     {
00709         cur_widget[widget_id] = type_list_foot[widget_id] = node;
00710     }
00711     /* otherwise, link the node in to the existing type list */
00712     else
00713     {
00714         type_list_foot[widget_id]->type_next = node;
00715         node->type_prev = type_list_foot[widget_id];
00716         type_list_foot[widget_id] = node;
00717     }
00718 
00719     /* increment the unique ID counter */
00720     ++widget_uid;
00721 
00722 #ifdef DEBUG_WIDGET
00723     LOG(llevDebug, "..ALLOCATED: %s, WidgetObjID: %d\n", node->name, node->WidgetObjID);
00724     debug_count_nodes(1);
00725 
00726     LOG(llevInfo, "..create_widget(): Done.\n");
00727 #endif
00728 
00729     return node;
00730 }
00731 
00733 void remove_widget(widgetdata *widget)
00734 {
00735     widgetdata *tmp = NULL;
00736 
00737 #ifdef DEBUG_WIDGET
00738     LOG(llevInfo, "Entering remove_widget()..\n");
00739 #endif
00740 
00741     /* node to delete is the only node in the tree, bye-bye binary tree :) */
00742     if (!widget_list_head->next && !widget_list_head->inv)
00743     {
00744         widget_list_head = NULL;
00745         widget_list_foot = NULL;
00746         cur_widget[widget->WidgetSubtypeID] = NULL;
00747         type_list_foot[widget->WidgetSubtypeID] = NULL;
00748     }
00749     else
00750     {
00751         /* node to delete is the head, move the pointer to next node */
00752         if (widget == widget_list_head)
00753         {
00754             widget_list_head = widget_list_head->next;
00755             widget_list_head->prev = NULL;
00756         }
00757         /* node to delete is the foot, move the pointer to the previous node */
00758         else if (widget == widget_list_foot)
00759         {
00760             widget_list_foot = widget_list_foot->prev;
00761             widget_list_foot->next = NULL;
00762         }
00763         /* node is first sibling, and should have a parent since it is not the root node */
00764         else if (!widget->prev)
00765         {
00766             /* node is also the last sibling, so NULL the parent's inventory */
00767             if (!widget->next)
00768             {
00769                 widget->env->inv = NULL;
00770                 widget->env->inv_rev = NULL;
00771             }
00772             /* or else make it the parent's first child */
00773             else
00774             {
00775                 widget->env->inv = widget->next;
00776                 widget->next->prev = NULL;
00777             }
00778         }
00779         /* node is last sibling and should have a parent, move the inv_rev pointer to the previous sibling */
00780         else if (!widget->next)
00781         {
00782             widget->env->inv_rev = widget->prev;
00783             widget->prev->next = NULL;
00784         }
00785         /* node to delete is in the middle of the tree somewhere */
00786         else
00787         {
00788             widget->next->prev = widget->prev;
00789             widget->prev->next = widget->next;
00790         }
00791 
00792         /* move the children to the top level of the list, starting from the end child */
00793         for (tmp = widget->inv_rev; tmp; tmp = tmp->prev)
00794         {
00795             /* tmp is no longer in a container */
00796             tmp->env = NULL;
00797             widget_list_head->prev = tmp;
00798             tmp->next = widget_list_head;
00799             widget_list_head = tmp;
00800         }
00801 
00802         /* if widget type list has only one node, kill it */
00803         if (cur_widget[widget->WidgetSubtypeID] == type_list_foot[widget->WidgetSubtypeID])
00804         {
00805             cur_widget[widget->WidgetSubtypeID] = type_list_foot[widget->WidgetSubtypeID] = NULL;
00806         }
00807         /* widget is head node */
00808         else if (widget == cur_widget[widget->WidgetSubtypeID])
00809         {
00810             cur_widget[widget->WidgetSubtypeID] = cur_widget[widget->WidgetSubtypeID]->type_next;
00811             cur_widget[widget->WidgetSubtypeID]->type_prev = NULL;
00812         }
00813         /* widget is foot node */
00814         else if (widget == type_list_foot[widget->WidgetSubtypeID])
00815         {
00816             type_list_foot[widget->WidgetSubtypeID] = type_list_foot[widget->WidgetSubtypeID]->type_prev;
00817             type_list_foot[widget->WidgetSubtypeID]->type_next = NULL;
00818         }
00819         /* widget is in middle of type list */
00820         else
00821         {
00822             widget->type_prev->type_next = widget->type_next;
00823             widget->type_next->type_prev = widget->type_prev;
00824         }
00825     }
00826 
00827 #ifdef DEBUG_WIDGET
00828     LOG(llevDebug, "..REMOVED: %s, WidgetObjID: %d\n", widget->name, widget->WidgetObjID);
00829 #endif
00830 
00831     /* free the surface */
00832     if (widget->widgetSF)
00833     {
00834         SDL_FreeSurface(widget->widgetSF);
00835         widget->widgetSF = NULL;
00836     }
00837 
00838     free(widget);
00839 
00840 #ifdef DEBUG_WIDGET
00841     debug_count_nodes(1);
00842     LOG(llevInfo, "..remove_widget(): Done.\n");
00843 #endif
00844 }
00845 
00847 void detach_widget(widgetdata *widget)
00848 {
00849     /* sanity check */
00850     if (!widget->env)
00851     {
00852         return;
00853     }
00854 
00855     /* first unlink the widget from the container and siblings */
00856 
00857     /* if widget is only one in the container's inventory, clear both pointers */
00858     if (!widget->prev && !widget->next)
00859     {
00860         widget->env->inv = NULL;
00861         widget->env->inv_rev = NULL;
00862     }
00863     /* widget is first sibling */
00864     else if (!widget->prev)
00865     {
00866         widget->env->inv = widget->next;
00867         widget->next->prev = NULL;
00868     }
00869     /* widget is last sibling */
00870     else if (!widget->next)
00871     {
00872         widget->env->inv_rev = widget->prev;
00873         widget->prev->next = NULL;
00874     }
00875     /* widget is a middle sibling */
00876     else
00877     {
00878         widget->prev->next = widget->next;
00879         widget->next->prev = widget->prev;
00880     }
00881 
00882     /* if something else exists in the container's inventory, make it auto-resize to fit the widgets inside */
00883     if (widget->env->inv)
00884     {
00885         resize_widget(widget->env->inv, RESIZE_RIGHT, widget->env->inv->wd);
00886         resize_widget(widget->env->inv, RESIZE_BOTTOM, widget->env->inv->ht);
00887     }
00888     /* otherwise if its inventory is empty, resize it to its default size */
00889     else
00890     {
00891         resize_widget(widget->env, RESIZE_RIGHT, con_widget[widget->env->WidgetSubtypeID].wd);
00892         resize_widget(widget->env, RESIZE_BOTTOM, con_widget[widget->env->WidgetSubtypeID].ht);
00893     }
00894 
00895     /* widget is no longer in a container */
00896     widget->env = NULL;
00897     /* move the widget to the top of the priority tree */
00898     widget->prev = NULL;
00899     widget_list_head->prev = widget;
00900     widget->next = widget_list_head;
00901     widget_list_head = widget;
00902 }
00903 
00904 #ifdef DEBUG_WIDGET
00905 
00906 int debug_count_nodes_rec(widgetdata *widget, int i, int j, int output)
00907 {
00908     int tmp = 0;
00909 
00910     do
00911     {
00912         /* we print out the top node, and then go down a level, rather than go down first */
00913         if (output)
00914         {
00915             /* a way of representing graphically how many levels down we are */
00916             for (tmp = 0; tmp < j; ++ tmp)
00917             {
00918                 printf("..");
00919             }
00920 
00921             LOG(llevInfo, "..%s, WidgetObjID: %d\n", widget->name, widget->WidgetObjID);
00922         }
00923 
00924         i++;
00925 
00926         /* we want to process the widgets starting from the left hand side of the tree first */
00927         if (widget->inv)
00928         {
00929             i = debug_count_nodes_rec(widget->inv, i, j + 1, output);
00930         }
00931 
00932         /* get the next sibling for our next loop */
00933         widget = widget->next;
00934     }
00935     while (widget);
00936 
00937     return i;
00938 }
00939 
00940 void debug_count_nodes(int output)
00941 {
00942     int i = 0;
00943 
00944     LOG(llevInfo, "Output of widget nodes:\n");
00945     LOG(llevInfo, "========================================\n");
00946 
00947     if (widget_list_head)
00948     {
00949         i = debug_count_nodes_rec(widget_list_head, 0, 0, output);
00950     }
00951 
00952     LOG(llevInfo, "========================================\n");
00953     LOG(llevInfo, "..Total widget nodes: %d\n", i);
00954 }
00955 #endif
00956 
00961 static int load_interface_file(char *filename)
00962 {
00963     int i = -1, pos;
00964     FILE *stream;
00965     widgetdata *widget = NULL;
00966     char line[256], keyword[256], parameter[256];
00967     int found_widget[TOTAL_SUBWIDGETS] = {0};
00968 
00969 #ifdef DEBUG_WIDGET
00970     LOG(llevDebug, "Entering load_interface_file()..\n");
00971 #endif
00972 
00973     /* Sanity check - if the file doesn't exist, exit with error */
00974     if (!(stream = fopen_wrapper(filename, "r")))
00975     {
00976         /* Inform user */
00977         LOG(llevInfo, "load_interface_file(): Can't find file %s.\n", filename);
00978         return 0;
00979     }
00980 
00981     /* Read the settings from the file */
00982     while (fgets(line, 255, stream))
00983     {
00984         if (line[0] == '#' || line[0] == '\n')
00985         {
00986             continue;
00987         }
00988 
00989         i = 0;
00990 
00991         while (line[i] && line[i] != ':')
00992         {
00993             i++;
00994         }
00995 
00996         line[++i] = '\0';
00997 
00998         strncpy(keyword, line, sizeof(keyword));
00999         strncpy(parameter, line + i + 1, sizeof(parameter));
01000 
01001         /* Remove the newline character */
01002         parameter[strcspn(line + i + 1, "\n")] = 0;
01003 
01004         /* Beginning */
01005         if (strncmp(keyword, "Widget:", 7) == 0)
01006         {
01007 #ifdef DEBUG_WIDGET
01008             LOG(llevDebug, "..Trying to find \"Widget: %s\"\n", parameter);
01009 #endif
01010 
01011             pos = 0;
01012 
01013             /* Find the index of the widget for reference */
01014             while (pos < TOTAL_SUBWIDGETS && (strcmp(con_widget[pos].name, parameter) != 0))
01015             {
01016                 ++pos;
01017             }
01018 
01019             /* The widget name couldn't be found? */
01020             if (pos >= TOTAL_SUBWIDGETS)
01021             {
01022                 continue;
01023             }
01024             /* Get the block */
01025             else
01026             {
01027                 if (!con_widget[pos].no_kill)
01028                 {
01029                     continue;
01030                 }
01031 
01032                 /* If we haven't found this widget, mark it */
01033                 if (!found_widget[pos])
01034                 {
01035 #ifdef DEBUG_WIDGET
01036                     LOG(llevInfo, "Found! (Index = %d) (%d widgets total)\n", pos, TOTAL_SUBWIDGETS);
01037 #endif
01038                     found_widget[pos] = 1;
01039                 }
01040 
01041                 /* create the widget with that ID, it is already fully initialized to the defaults */
01042                 widget = create_widget_object(pos);
01043 
01044                 /* in case something went wrong */
01045                 if (!widget)
01046                 {
01047 #ifdef DEBUG_WIDGET
01048                     LOG(llevDebug, ".. Failed to create widget!\n");
01049 #endif
01050                     continue;
01051                 }
01052 
01053                 while (fgets(line, 255, stream))
01054                 {
01055                     if (line[0] == '#' || line[0] == '\n')
01056                     {
01057                         continue;
01058                     }
01059 
01060                     /* End marker */
01061                     if (!strncmp(line, "end", 3))
01062                     {
01063                         break;
01064                     }
01065 
01066                     i = 0;
01067 
01068                     while (line[i] && line[i] != ':')
01069                     {
01070                         i++;
01071                     }
01072 
01073                     line[++i] = '\0';
01074                     strcpy(keyword, line);
01075                     strcpy(parameter, line + i + 1);
01076 
01077                     if (strncmp(keyword, "x:", 2) == 0)
01078                     {
01079                         widget->x1 = atoi(parameter);
01080 #ifdef DEBUG_WIDGET
01081                         LOG(llevDebug, "..Loading: (%s %d)\n", keyword, widget->x1);
01082 #endif
01083                     }
01084                     else if (strncmp(keyword, "y:", 2) == 0)
01085                     {
01086                         widget->y1 = atoi(parameter);
01087 #ifdef DEBUG_WIDGET
01088                         LOG(llevDebug, "..Loading: (%s %d)\n", keyword, widget->y1);
01089 #endif
01090                     }
01091                     else if (strncmp(keyword, "moveable:", 9) == 0)
01092                     {
01093                         widget->moveable = atoi(parameter);
01094 #ifdef DEBUG_WIDGET
01095                         LOG(llevDebug, "..Loading: (%s %d)\n", keyword, widget->moveable);
01096 #endif
01097                     }
01098                     else if (strncmp(keyword, "active:", 7) == 0)
01099                     {
01100                         widget->show = atoi(parameter);
01101 #ifdef DEBUG_WIDGET
01102                         LOG(llevDebug, "..Loading: (%s %d)\n", keyword, widget->show);
01103 #endif
01104                     }
01105                     else if (strncmp(keyword, "width:", 6) == 0)
01106                     {
01107                         widget->wd = atoi(parameter);
01108 #ifdef DEBUG_WIDGET
01109                         LOG(llevDebug, "..Loading: (%s %d)\n", keyword, widget->wd);
01110 #endif
01111                     }
01112                     else if (strncmp(keyword, "height:", 7) == 0)
01113                     {
01114                         widget->ht = atoi(parameter);
01115 #ifdef DEBUG_WIDGET
01116                         LOG(llevDebug, "..Loading: (%s %d)\n", keyword, widget->ht);
01117 #endif
01118                     }
01119                     else if (!strncmp(keyword, "font:", 5))
01120                     {
01121                         textwin_struct *textwin = TEXTWIN(widget);
01122                         char font_name[MAX_BUF];
01123                         int font_size, font_id;
01124 
01125                         if (textwin && sscanf(parameter, "%s %d", font_name, &font_size) == 2 && (font_id = get_font_id(font_name, font_size)) != -1)
01126                         {
01127                             textwin->font = font_id;
01128                         }
01129                     }
01130                 }
01131             }
01132         }
01133     }
01134 
01135     fclose(stream);
01136 
01137     /* Go through the widgets */
01138     for (pos = 0; pos < TOTAL_SUBWIDGETS; ++pos)
01139     {
01140         /* If a required widget was not found, load the default data for it. */
01141         if (!found_widget[pos] && con_widget[pos].no_kill)
01142         {
01143             /* A newly created widget is loaded with the default values. */
01144             create_widget_object(pos);
01145             LOG(llevDebug, "load_interface_file(): Critical widget is missing! Recreating with default values.\n");
01146         }
01147     }
01148 
01149 #ifdef DEBUG_WIDGET
01150     LOG(llevDebug, "..load_interface_file(): Done.\n");
01151 #endif
01152 
01153     return 1;
01154 }
01155 
01158 void save_interface_file()
01159 {
01160     FILE *stream;
01161 
01162     /* Leave, if there's an error opening or creating */
01163     if (!(stream = fopen_wrapper(INTERFACE_FILE, "w")))
01164     {
01165         return;
01166     }
01167 
01168     fputs("#############################################\n", stream);
01169     fputs("# This is the Atrinik client interface file #\n", stream);
01170     fputs("#############################################\n", stream);
01171 
01172     /* start walking through the widgets */
01173     save_interface_file_rec(widget_list_foot, stream);
01174 
01175     fclose(stream);
01176 }
01177 
01181 void save_interface_file_rec(widgetdata *widget, FILE *stream)
01182 {
01183     do
01184     {
01185         /* skip the widget if it shouldn't be saved */
01186         if (!widget->save)
01187         {
01188             widget = widget->prev;
01189             continue;
01190         }
01191 
01192         /* we want to process the widgets starting from the left hand side of the tree first */
01193         if (widget->inv_rev)
01194         {
01195             save_interface_file_rec(widget->inv_rev, stream);
01196         }
01197 
01198         fprintf(stream, "\nWidget: %s\n", widget->name);
01199         fprintf(stream, "moveable: %d\n", widget->moveable);
01200         fprintf(stream, "active: %d\n", widget->show);
01201         fprintf(stream, "x: %d\n", widget->x1);
01202         fprintf(stream, "y: %d\n", widget->y1);
01203 
01204         if (widget->save_width_height)
01205         {
01206             fprintf(stream, "width: %d\n", widget->wd);
01207             fprintf(stream, "height: %d\n", widget->ht);
01208         }
01209 
01210         switch (widget->WidgetTypeID)
01211         {
01212             case CHATWIN_ID:
01213             case MSGWIN_ID:
01214             {
01215                 textwin_struct *textwin = TEXTWIN(widget);
01216 
01217                 fprintf(stream, "font: %s %"FMT64U"\n", get_font_filename(textwin->font), (uint64) fonts[textwin->font].size);
01218                 break;
01219             }
01220         }
01221 
01222         /* End of block */
01223         fputs("end\n", stream);
01224 
01225         /* get the next sibling for our next loop */
01226         widget = widget->prev;
01227     }
01228     while (widget);
01229 }
01230 
01238 int widget_event_mousedn(int x, int y, SDL_Event *event)
01239 {
01240     widgetdata *widget;
01241 
01242     /* update the widget event struct if the mouse is in a widget, or else get out of here for sanity reasons */
01243     if (!widget_event_respond(x, y))
01244     {
01245         return 0;
01246     }
01247 
01248     widget = widget_mouse_event.owner;
01249 
01250     /* sanity check */
01251     if (!widget)
01252     {
01253         return 0;
01254     }
01255 
01256     /* Set the priority to this widget */
01257     SetPriorityWidget(widget);
01258 
01259     if (widget_event_move.active)
01260     {
01261         widgetdata *widget_container;
01262 
01263         widget = widget_mouse_event.owner;
01264 
01265         widget_event_move.active = 0;
01266         widget_mouse_event.x = x;
01267         widget_mouse_event.y = y;
01268         /* no widgets are being moved now */
01269         widget_event_move.owner = NULL;
01270 
01271         /* Disable the custom cursor */
01272         f_custom_cursor = 0;
01273 
01274         /* Show the system cursor */
01275         SDL_ShowCursor(1);
01276 
01277         /* Due to a bug in SDL 1.2.x, the mouse X/Y position is not updated
01278          * while in fullscreen with the cursor hidden, so we must take care
01279          * of it ourselves. Apparently SDL 1.3 should fix it.
01280          * See http://old.nabble.com/Mouse-movement-problems-in-fullscreen-mode-td20890669.html
01281          * for details. */
01282         SDL_WarpMouse(x, y);
01283 
01284         /* Somehow the owner before the widget dragging is gone now. Not a good idea to carry on... */
01285         if (!widget)
01286         {
01287             return 0;
01288         }
01289 
01290         /* check to see if it's on top of a widget container */
01291         widget_container = get_widget_owner(x, y, widget->next, NULL);
01292 
01293         /* attempt to insert it into the widget container if it exists */
01294         insert_widget_in_container(widget_container, widget);
01295         return 1;
01296     }
01297 
01298     /* Right mouse button was clicked */
01299     if (event->button.button == SDL_BUTTON_RIGHT && widget->WidgetTypeID != MAP_ID)
01300     {
01301         widgetdata *menu;
01302 
01303         /* Create a context menu for the widget clicked on. */
01304         menu = create_menu(x, y, widget);
01305         /* This bit probably shouldn't be hard coded in future. */
01306         add_menuitem(menu, "Move Widget", &menu_move_widget, MENU_NORMAL, 0);
01307 
01308         if (widget->WidgetSubtypeID == MAIN_INV_ID)
01309         {
01310             add_menuitem(menu, "Inventory Filters  >", &menu_detach_widget, MENU_SUBMENU, 0);
01311         }
01312         else if (widget->WidgetSubtypeID == MSGWIN_ID || widget->WidgetSubtypeID == CHATWIN_ID)
01313         {
01314             add_menuitem(menu, "Clear", &menu_textwin_clear, MENU_NORMAL, 0);
01315             add_menuitem(menu, "Increase Font Size", &menu_textwin_font_inc, MENU_NORMAL, 0);
01316             add_menuitem(menu, "Decrease Font Size", &menu_textwin_font_dec, MENU_NORMAL, 0);
01317         }
01318 
01319         menu_finalize(menu);
01320 
01321         return 1;
01322     }
01323     /* Start resizing. */
01324     else if (widget->resizeable && widget->resize_flags && event->button.button == SDL_BUTTON_LEFT)
01325     {
01326         widget_event_resize.active = 1;
01327         widget_event_resize.owner = widget;
01328     }
01329     /* Normal condition - respond to mouse down event */
01330     else
01331     {
01332         /* Handler(s) for miscellaneous mouse movement(s) go here. */
01333 
01334         /* Special case for menuitems, if menuitem or a widget inside is clicked on, calls the function tied to the menuitem. */
01335         widget_menu_event(widget, x, y);
01336 
01337         /* Place here all the mousedown handlers. */
01338         switch (widget->WidgetTypeID)
01339         {
01340             case MENU_B_ID:
01341                 widget_menubuttons_event(widget, event);
01342                 break;
01343 
01344             case QUICKSLOT_ID:
01345                 widget_quickslots_mouse_event(widget, event);
01346                 break;
01347 
01348             case CHATWIN_ID:
01349             case MSGWIN_ID:
01350                 textwin_event(widget, event);
01351                 break;
01352 
01353             case RANGE_ID:
01354                 widget_range_event(widget, x, y, *event, MOUSE_DOWN);
01355                 break;
01356 
01357             case BELOW_INV_ID:
01358                 widget_below_window_event(widget, x, y, MOUSE_DOWN);
01359                 break;
01360 
01361             case TARGET_ID:
01362                 widget_event_target(widget, x, y);
01363                 break;
01364 
01365             case MAIN_INV_ID:
01366                 widget_inventory_event(widget, x, y, *event);
01367                 break;
01368 
01369             case PLAYER_INFO_ID:
01370                 widget_player_data_event(widget, x, y);
01371                 break;
01372 
01373             case IN_NUMBER_ID:
01374                 widget_number_event(widget, x, y);
01375                 break;
01376 
01377             case MPLAYER_ID:
01378                 widget_mplayer_mevent(widget, event);
01379                 break;
01380 
01381             case SPELLS_ID:
01382                 widget_spells_mevent(widget, event);
01383                 break;
01384 
01385             case MAP_ID:
01386                 widget_map_mevent(widget, event);
01387                 break;
01388 
01389             case SKILLS_ID:
01390                 widget_skills_mevent(widget, event);
01391                 break;
01392 
01393             case PARTY_ID:
01394                 widget_party_mevent(widget, event);
01395                 break;
01396         }
01397     }
01398 
01399     /* User didn't click on a menu, so remove any menus that exist. */
01400     if (widget->WidgetSubtypeID != MENU_ID)
01401     {
01402         widgetdata *menu, *tmp;
01403 
01404         for (menu = cur_widget[MENU_ID]; menu; menu = tmp)
01405         {
01406             tmp = menu->type_next;
01407             remove_widget_object(menu);
01408         }
01409     }
01410 
01411     return 1;
01412 }
01413 
01421 int widget_event_mouseup(int x, int y, SDL_Event *event)
01422 {
01423     widgetdata *widget;
01424 
01425     /* Widget is now being moved, don't do anything. */
01426     if (widget_event_move.active)
01427     {
01428         return 1;
01429     }
01430     /* End resizing. */
01431     else if (widget_event_resize.active)
01432     {
01433         widget_event_resize.active = 0;
01434         widget_event_resize.owner = NULL;
01435         return 1;
01436     }
01437     /* Normal condition - respond to mouse up event */
01438     else
01439     {
01440         /* update the widget event struct if the mouse is in a widget, or else get out of here for sanity reasons */
01441         if (!widget_event_respond(x, y))
01442         {
01443             return 0;
01444         }
01445 
01446         widget = widget_mouse_event.owner;
01447 
01448         /* sanity check */
01449         if (!widget)
01450         {
01451             return 0;
01452         }
01453 
01454         /* Handler for the widgets go here */
01455         switch (widget->WidgetTypeID)
01456         {
01457             case QUICKSLOT_ID:
01458                 widget_quickslots_mouse_event(widget, event);
01459                 break;
01460 
01461             case CHATWIN_ID:
01462             case MSGWIN_ID:
01463                 textwin_event(widget, event);
01464                 break;
01465 
01466             case PDOLL_ID:
01467                 widget_show_player_doll_event();
01468                 break;
01469 
01470             case RANGE_ID:
01471                 widget_range_event(widget, x, y, *event, MOUSE_UP);
01472                 break;
01473 
01474             case MAIN_INV_ID:
01475                 widget_inventory_event(widget, x, y, *event);
01476                 break;
01477 
01478             case MPLAYER_ID:
01479                 widget_mplayer_mevent(widget, event);
01480                 break;
01481 
01482             case SPELLS_ID:
01483                 widget_spells_mevent(widget, event);
01484                 break;
01485 
01486             case MENU_B_ID:
01487                 widget_menubuttons_event(widget, event);
01488                 break;
01489 
01490             case MAP_ID:
01491                 widget_map_mevent(widget, event);
01492                 break;
01493 
01494             case SKILLS_ID:
01495                 widget_skills_mevent(widget, event);
01496                 break;
01497 
01498             case PARTY_ID:
01499                 widget_party_mevent(widget, event);
01500                 break;
01501         }
01502 
01503         return 1;
01504     }
01505 }
01506 
01514 int widget_event_mousemv(int x, int y, SDL_Event *event)
01515 {
01516     widgetdata *widget;
01517 
01518     /* Widget moving condition */
01519     if (widget_event_move.active)
01520     {
01521         int nx, ny;
01522 
01523         widget = widget_event_move.owner;
01524 
01525         /* The widget being moved doesn't exist. Sanity check in case something mad like this happens. */
01526         if (!widget)
01527         {
01528             return 0;
01529         }
01530 
01531         x -= widget_event_move.xOffset;
01532         y -= widget_event_move.yOffset;
01533         nx = x;
01534         ny = y;
01535 
01536         /* Widget snapping logic courtesy of OpenTTD (GPLv2). */
01537         if (setting_get_int(OPT_CAT_GENERAL, OPT_SNAP_RADIUS))
01538         {
01539             widgetdata *tmp;
01540             int delta, hsnap, vsnap;
01541 
01542             delta = 0;
01543             hsnap = vsnap = setting_get_int(OPT_CAT_GENERAL, OPT_SNAP_RADIUS);
01544 
01545             for (tmp = widget_list_head; tmp; tmp = tmp->next)
01546             {
01547                 if (tmp == widget || tmp->disable_snapping || !tmp->show || !tmp->visible)
01548                 {
01549                     continue;
01550                 }
01551 
01552                 if (y + widget->ht > tmp->y1 && y < tmp->y1 + tmp->ht)
01553                 {
01554                     /* Your left border <-> other right border */
01555                     delta = abs(tmp->x1 + tmp->wd - x);
01556 
01557                     if (delta <= hsnap)
01558                     {
01559                         nx = tmp->x1 + tmp->wd;
01560                         hsnap = delta;
01561                     }
01562 
01563                     /* Your right border <-> other left border */
01564                     delta = abs(tmp->x1 - x - widget->wd);
01565 
01566                     if (delta <= hsnap)
01567                     {
01568                         nx = tmp->x1 - widget->wd;
01569                         hsnap = delta;
01570                     }
01571                 }
01572 
01573                 if (widget->y1 + widget->ht >= tmp->y1 && widget->y1 <= tmp->y1 + tmp->ht)
01574                 {
01575                     /* Your left border <-> other left border */
01576                     delta = abs(tmp->x1 - x);
01577 
01578                     if (delta <= hsnap)
01579                     {
01580                         nx = tmp->x1;
01581                         hsnap = delta;
01582                     }
01583 
01584                     /* Your right border <-> other right border */
01585                     delta = abs(tmp->x1 + tmp->wd - x - widget->wd);
01586 
01587                     if (delta <= hsnap)
01588                     {
01589                         nx = tmp->x1 + tmp->wd - widget->wd;
01590                         hsnap = delta;
01591                     }
01592                 }
01593 
01594                 if (x + widget->wd > tmp->x1 && x < tmp->x1 + tmp->wd)
01595                 {
01596                     /* Your top border <-> other bottom border */
01597                     delta = abs(tmp->y1 + tmp->ht - y);
01598 
01599                     if (delta <= vsnap)
01600                     {
01601                         ny = tmp->y1 + tmp->ht;
01602                         vsnap = delta;
01603                     }
01604 
01605                     /* Your bottom border <-> other top border */
01606                     delta = abs(tmp->y1 - y - widget->ht);
01607 
01608                     if (delta <= vsnap)
01609                     {
01610                         ny = tmp->y1 - widget->ht;
01611                         vsnap = delta;
01612                     }
01613                 }
01614 
01615                 if (widget->x1 + widget->wd >= tmp->x1 && widget->x1 <= tmp->x1 + tmp->wd)
01616                 {
01617                     /* Your top border <-> other top border */
01618                     delta = abs(tmp->y1 - y);
01619 
01620                     if (delta <= vsnap)
01621                     {
01622                         ny = tmp->y1;
01623                         vsnap = delta;
01624                     }
01625 
01626                     /* Your bottom border <-> other bottom border */
01627                     delta = abs(tmp->y1 + tmp->ht - y - widget->ht);
01628 
01629                     if (delta <= vsnap)
01630                     {
01631                         ny = tmp->y1 + tmp->ht - widget->ht;
01632                         vsnap = delta;
01633                     }
01634                 }
01635             }
01636         }
01637 
01638         /* we move the widget here, as well as all the widgets inside it if they exist */
01639         /* we use the recursive version since we already have the outermost container */
01640         move_widget_rec(widget, nx - widget->x1, ny - widget->y1);
01641 
01642         /* Ensure widget is on-screen. */
01643         if (!setting_get_int(OPT_CAT_CLIENT, OPT_OFFSCREEN_WIDGETS))
01644         {
01645             widget_ensure_onscreen(widget);
01646         }
01647 
01648         map_udate_flag = 2;
01649 
01650         return 1;
01651     }
01652     else if (widget_event_resize.active)
01653     {
01654         widget = widget_event_resize.owner;
01655 
01656         if (!widget)
01657         {
01658             return 0;
01659         }
01660 
01661         if (widget->resize_flags & (RESIZE_LEFT | RESIZE_RIGHT))
01662         {
01663             resize_widget(widget, widget->resize_flags & ~(RESIZE_TOP | RESIZE_BOTTOM), MAX(widget->min_w, widget->resize_flags & RESIZE_LEFT ? widget->x1 - x + widget->wd : x - widget->x1));
01664         }
01665 
01666         if (widget->resize_flags & (RESIZE_TOP | RESIZE_BOTTOM))
01667         {
01668             resize_widget(widget, widget->resize_flags & ~(RESIZE_LEFT | RESIZE_RIGHT), MAX(widget->min_h, widget->resize_flags & RESIZE_TOP ? widget->y1 - y + widget->ht : y - widget->y1));
01669         }
01670 
01671         switch (widget->WidgetTypeID)
01672         {
01673             case MSGWIN_ID:
01674             case CHATWIN_ID:
01675                 textwin_readjust(widget);
01676                 break;
01677         }
01678 
01679         return 1;
01680     }
01681     /* Normal condition - respond to mouse move event */
01682     else
01683     {
01684         textwin_struct *textwin = NULL;
01685 
01686         /* update the widget event struct if the mouse is in a widget, or else get out of here for sanity reasons */
01687         if (!widget_event_respond(x, y))
01688         {
01689             return 0;
01690         }
01691 
01692         widget = widget_mouse_event.owner;
01693 
01694         /* sanity check */
01695         if (!widget)
01696         {
01697             return 0;
01698         }
01699 
01700         if (widget->resizeable)
01701         {
01702             widget->resizeable = 1;
01703             widget->resize_flags = 0;
01704 
01705             if (y >= widget->y1 && y <= widget->y1 + 2)
01706             {
01707                 widget->resize_flags |= RESIZE_TOP;
01708             }
01709             else if (y >= widget->y1 + widget->ht - 2 && y <= widget->y1 + widget->ht)
01710             {
01711                 widget->resize_flags |= RESIZE_BOTTOM;
01712             }
01713 
01714             if (x >= widget->x1 && x <= widget->x1 + 2)
01715             {
01716                 widget->resize_flags |= RESIZE_LEFT;
01717             }
01718             else if (x >= widget->x1 + widget->wd - 2 && x <= widget->x1 + widget->wd)
01719             {
01720                 widget->resize_flags |= RESIZE_RIGHT;
01721             }
01722         }
01723 
01724         /* Handlers for miscellaneous mouse movements go here */
01725 
01726         /* Handlers for the widgets mouse move */
01727         switch (widget->WidgetTypeID)
01728         {
01729             case CHATWIN_ID:
01730             case MSGWIN_ID:
01731                 textwin = TEXTWIN(widget);
01732 
01733                 /* textwin special handling */
01734                 if (textwin)
01735                 {
01736                     if (textwin->highlight != TW_HL_NONE)
01737                     {
01738                         textwin->highlight = TW_HL_NONE;
01739                         WIDGET_REDRAW(widget);
01740                     }
01741                 }
01742 
01743                 textwin_event(widget, event);
01744                 break;
01745 
01746             case MAIN_INV_ID:
01747                 widget_inventory_event(widget, x, y, *event);
01748                 break;
01749 
01750             case MPLAYER_ID:
01751                 widget_mplayer_mevent(widget, event);
01752                 break;
01753 
01754             case SPELLS_ID:
01755                 widget_spells_mevent(widget, event);
01756                 break;
01757 
01758             case MENU_B_ID:
01759                 widget_menubuttons_event(widget, event);
01760                 break;
01761 
01762             case MAP_ID:
01763                 widget_map_mevent(widget, event);
01764                 break;
01765 
01766             case SKILLS_ID:
01767                 widget_skills_mevent(widget, event);
01768                 break;
01769 
01770             case PARTY_ID:
01771                 widget_party_mevent(widget, event);
01772                 break;
01773         }
01774 
01775         return 1;
01776     }
01777 }
01778 
01780 int widget_event_start_move(widgetdata *widget, int x, int y)
01781 {
01782     /* get the outermost container so we can move the container with everything in it */
01783     widget = get_outermost_container(widget);
01784 
01785     /* if its moveable, start moving it when the conditions warrant it, or else run away */
01786     if (!widget->moveable)
01787     {
01788         return 0;
01789     }
01790 
01791     /* we know this widget owns the mouse.. */
01792     widget_event_move.active = 1;
01793 
01794     /* start the movement procedures */
01795     widget_event_move.owner = widget;
01796     widget_event_move.xOffset = x - widget->x1;
01797     widget_event_move.yOffset = y - widget->y1;
01798 
01799     /* enable the custom cursor */
01800     f_custom_cursor = MSCURSOR_MOVE;
01801     /* hide the system cursor */
01802     SDL_ShowCursor(0);
01803 
01804 #ifdef WIN32
01805     /* Workaround another bug with SDL 1.2.x on Windows. Make sure the cursor
01806      * is in the center of the screen if we are in fullscreen mode. */
01807     if (ScreenSurface->flags & SDL_FULLSCREEN)
01808     {
01809         SDL_WarpMouse(ScreenSurface->w / 2, ScreenSurface->h / 2);
01810     }
01811 #endif
01812 
01813     return 1;
01814 }
01815 
01817 int widget_event_respond(int x, int y)
01818 {
01819     /* only update the owner if there is no event override taking place */
01820     if (!widget_event_override())
01821     {
01822         widget_mouse_event.owner = get_widget_owner(x, y, NULL, NULL);
01823     }
01824 
01825     /* sanity check.. return if mouse is not in a widget */
01826     if (!widget_mouse_event.owner)
01827     {
01828         return 0;
01829     }
01830 
01831     /* setup the event structure in response */
01832     widget_mouse_event.x = x;
01833     widget_mouse_event.y = y;
01834 
01835     return 1;
01836 }
01837 
01839 int widget_event_override()
01840 {
01841     return 0;
01842 }
01843 
01845 widgetdata *get_widget_owner(int x, int y, widgetdata *start, widgetdata *end)
01846 {
01847     widgetdata *success;
01848 
01849     /* mouse cannot be used by widgets */
01850     if (IsMouseExclusive)
01851     {
01852         return NULL;
01853     }
01854 
01855     /* no widgets exist */
01856     if (!widget_list_head)
01857     {
01858         return NULL;
01859     }
01860 
01861     /* sometimes we want a fast way to get the widget behind the widget at the front.
01862      * this is what start is for, and we will only start walking the list beginning with start.
01863      * if start is NULL, we just do a regular search */
01864     if (!start)
01865     {
01866         start = widget_list_head;
01867     }
01868 
01869     /* ok, let's kick off the recursion. if we find our widget, we get a widget back. if not, we get a big fat NULL */
01870     success = get_widget_owner_rec(x, y, start, end);
01871 
01872     /*LOG(llevDebug, "WIDGET OWNER: %s, WidgetObjID: %d\n", success? success->name: "NULL", success? success->WidgetObjID: -1);*/
01873 
01874     return success;
01875 }
01876 
01877 /* traverse through the tree & perform custom or default hit-test */
01878 widgetdata *get_widget_owner_rec(int x, int y, widgetdata *widget, widgetdata *end)
01879 {
01880     widgetdata *success = NULL;
01881 
01882     do
01883     {
01884         /* we want to get the first widget starting from the left hand side of the tree first */
01885         if (widget->inv)
01886         {
01887             success = get_widget_owner_rec(x, y, widget->inv, end);
01888 
01889             /* we found a widget in the hit test? if so, get out of this recursive mess with our prize */
01890             if (success)
01891             {
01892                 return success;
01893             }
01894         }
01895 
01896         /* skip if widget is hidden */
01897         if (!widget->show)
01898         {
01899             widget = widget->next;
01900             continue;
01901         }
01902 
01903         switch (widget->WidgetTypeID)
01904         {
01905             default:
01906                 if (x >= widget->x1 && x <= (widget->x1 + widget->wd) && y >= widget->y1 && y <= (widget->y1 + widget->ht))
01907                 {
01908                     return widget;
01909                 }
01910         }
01911 
01912         /* get the next sibling for our next loop */
01913         widget = widget->next;
01914     }
01915     while (widget || widget != end);
01916 
01917     return NULL;
01918 }
01919 
01923 static void process_widget(widgetdata *widget)
01924 {
01925     switch (widget->WidgetTypeID)
01926     {
01927         case STATS_ID:
01928             widget_player_stats(widget);
01929             break;
01930 
01931         case RESIST_ID:
01932             widget_show_resist(widget);
01933             break;
01934 
01935         case MAIN_LVL_ID:
01936             widget_show_main_lvl(widget);
01937             break;
01938 
01939         case SKILL_EXP_ID:
01940             widget_show_skill_exp(widget);
01941             break;
01942 
01943         case REGEN_ID:
01944             widget_show_regeneration(widget);
01945             break;
01946 
01947         case SKILL_LVL_ID:
01948             widget_skillgroups(widget);
01949             break;
01950 
01951         case MENU_B_ID:
01952             widget_menubuttons(widget);
01953             break;
01954 
01955         case QUICKSLOT_ID:
01956             widget_quickslots(widget);
01957             break;
01958 
01959         case CHATWIN_ID:
01960         case MSGWIN_ID:
01961             widget_textwin_show(widget);
01962             break;
01963 
01964         case PDOLL_ID:
01965             widget_show_player_doll(widget);
01966             break;
01967 
01968         case BELOW_INV_ID:
01969             widget_show_below_window(widget);
01970             break;
01971 
01972         case PLAYER_INFO_ID:
01973             widget_show_player_data(widget);
01974             break;
01975 
01976         case RANGE_ID:
01977             widget_show_range(widget);
01978             break;
01979 
01980         case TARGET_ID:
01981             widget_show_target(widget);
01982             break;
01983 
01984         case MAIN_INV_ID:
01985             widget_show_inventory_window(widget);
01986             break;
01987 
01988         case MAPNAME_ID:
01989             widget_show_mapname(widget);
01990             break;
01991 
01992         case IN_CONSOLE_ID:
01993             widget_show_console(widget);
01994             break;
01995 
01996         case IN_NUMBER_ID:
01997             widget_show_number(widget);
01998             break;
01999 
02000         case FPS_ID:
02001             widget_show_fps(widget);
02002             break;
02003 
02004         case CONTAINER_ID:
02005             widget_show_container(widget);
02006             break;
02007 
02008         case LABEL_ID:
02009             widget_show_label(widget);
02010             break;
02011 
02012         case BITMAP_ID:
02013             widget_show_bitmap(widget);
02014             break;
02015 
02016         case MPLAYER_ID:
02017             widget_show_mplayer(widget);
02018             break;
02019 
02020         case SPELLS_ID:
02021             widget_spells_render(widget);
02022             break;
02023 
02024         case SKILLS_ID:
02025             widget_skills_render(widget);
02026             break;
02027 
02028         case PARTY_ID:
02029             widget_party_render(widget);
02030             break;
02031     }
02032 }
02033 
02038 static void process_widget_background(widgetdata *widget)
02039 {
02040     switch (widget->WidgetTypeID)
02041     {
02042         case MPLAYER_ID:
02043             widget_mplayer_background(widget);
02044             break;
02045 
02046         case PARTY_ID:
02047             widget_party_background(widget);
02048             break;
02049     }
02050 }
02051 
02055 void process_widgets()
02056 {
02057     /* sanity check */
02058     if (!widget_list_foot)
02059     {
02060         return;
02061     }
02062 
02063     process_widgets_rec(widget_list_foot);
02064 }
02065 
02070 void process_widgets_rec(widgetdata *widget)
02071 {
02072     popup_struct *popup;
02073 
02074     popup = popup_get_visible();
02075 
02076     do
02077     {
02078         /* if widget isn't hidden, process it. this is mostly to do with rendering them */
02079         if (widget->show && widget->visible && (!popup || popup_overlay_need_update(popup)))
02080         {
02081             process_widget(widget);
02082         }
02083 
02084         process_widget_background(widget);
02085 
02086         /* we want to process the widgets starting from the right hand side of the tree first */
02087         if (widget->inv_rev)
02088         {
02089             process_widgets_rec(widget->inv_rev);
02090         }
02091 
02092         /* get the previous sibling for our next loop */
02093         widget = widget->prev;
02094     }
02095     while (widget);
02096 }
02097 
02103 void SetPriorityWidget(widgetdata *node)
02104 {
02105 #ifdef DEBUG_WIDGET
02106     LOG(llevDebug, "Entering SetPriorityWidget(WidgetObjID=%d)..\n", node->WidgetObjID);
02107 #endif
02108 
02109     /* widget doesn't exist, means parent node has no children, so nothing to do here */
02110     if (!node)
02111     {
02112 #ifdef DEBUG_WIDGET
02113         LOG(llevDebug, "..SetPriorityWidget(): Done (Node does not exist).\n");
02114 #endif
02115         return;
02116     }
02117 
02118     if (node->WidgetTypeID == MAP_ID)
02119     {
02120         return;
02121     }
02122 
02123 #ifdef DEBUG_WIDGET
02124     LOG(llevDebug, "..BEFORE:\n");
02125     LOG(llevDebug, "....node: %p - %s\n", node, node->name);
02126     LOG(llevDebug, "....node->env: %p - %s\n", node->env, node->env? node->env->name: "NULL");
02127     LOG(llevDebug, "....node->prev: %p - %s, node->next: %p - %s\n", node->prev, node->prev? node->prev->name: "NULL", node->next, node->next? node->next->name: "NULL");
02128     LOG(llevDebug, "....node->inv: %p - %s, node->inv_rev: %p - %s\n", node->inv, node->inv? node->inv->name: "NULL", node->inv_rev, node->inv_rev? node->inv_rev->name: "NULL");
02129 #endif
02130 
02131     /* see if the node has a parent before continuing */
02132     if (node->env)
02133     {
02134         SetPriorityWidget(node->env);
02135 
02136         /* Strip containers are sorted in a fixed order, and no part of any widget inside should be covered by a sibling.
02137          * This means we don't need to bother moving the node to the front inside the container. */
02138         switch (node->env->WidgetSubtypeID)
02139         {
02140             case CONTAINER_STRIP_ID:
02141             case MENU_ID:
02142             case MENUITEM_ID:
02143                 return;
02144         }
02145     }
02146 
02147     /* now we need to move our other node in front of the first sibling */
02148     if (!node->prev)
02149     {
02150 #ifdef DEBUG_WIDGET
02151         LOG(llevDebug, "..SetPriorityWidget(): Done (Node already at front).\n");
02152 #endif
02153         /* no point continuing, node is already at the front */
02154         return;
02155     }
02156 
02157     /* Unlink node from its current position in the priority tree. */
02158 
02159     /* node is last sibling, clear the pointer of the previous sibling */
02160     if (!node->next)
02161     {
02162         /* node also has a parent pointing to it, hand the inv_rev pointer to the previous sibling */
02163         if (node->env)
02164         {
02165             node->env->inv_rev = node->prev;
02166         }
02167         /* no parent, this must be the foot then, so move it to the previous node */
02168         else
02169         {
02170             widget_list_foot = node->prev;
02171         }
02172 
02173         node->prev->next = NULL;
02174     }
02175     else
02176     {
02177         /* link up the adjacent nodes */
02178         node->prev->next = node->next;
02179         node->next->prev = node->prev;
02180     }
02181 
02182     /* Insert node at the head of its container, or make it the root node if it is not in a container. */
02183 
02184     /* Node is now the first sibling so the parent should point to it. */
02185     if (node->env)
02186     {
02187         node->next = node->env->inv;
02188         node->env->inv = node;
02189     }
02190     /* We are out of containers and this node is about to become the first sibling, which means it's taking the place of the root node. */
02191     else
02192     {
02193         node->next = widget_list_head;
02194         widget_list_head = node;
02195     }
02196 
02197     /* Point the former head node to this node. */
02198     node->next->prev = node;
02199     /* There's no siblings in front of node now. */
02200     node->prev = NULL;
02201 
02202 #ifdef DEBUG_WIDGET
02203     LOG(llevDebug, "..AFTER:\n");
02204     LOG(llevDebug, "....node: %p - %s\n", node, node->name);
02205     LOG(llevDebug, "....node->env: %p - %s\n", node->env, node->env? node->env->name: "NULL");
02206     LOG(llevDebug, "....node->prev: %p - %s, node->next: %p - %s\n", node->prev, node->prev? node->prev->name: "NULL", node->next, node->next? node->next->name: "NULL");
02207     LOG(llevDebug, "....node->inv: %p - %s, node->inv_rev: %p - %s\n", node->inv, node->inv? node->inv->name: "NULL", node->inv_rev, node->inv_rev? node->inv_rev->name: "NULL");
02208 
02209     LOG(llevDebug, "..SetPriorityWidget(): Done.\n");
02210 #endif
02211 }
02212 
02216 void SetPriorityWidget_reverse(widgetdata *node)
02217 {
02218     if (!node)
02219     {
02220         return;
02221     }
02222 
02223     if (!node->next)
02224     {
02225         return;
02226     }
02227 
02228     if (!node->prev)
02229     {
02230         if (node->env)
02231         {
02232             node->env->inv_rev = node->next;
02233         }
02234         else
02235         {
02236             widget_list_head = node->next;
02237         }
02238 
02239         node->next->prev = NULL;
02240     }
02241     else
02242     {
02243         node->next->prev = node->prev;
02244         node->prev->next = node->next;
02245     }
02246 
02247     if (node->env)
02248     {
02249         node->prev = node->env->inv;
02250         node->env->inv = node;
02251     }
02252     else
02253     {
02254         node->prev = widget_list_foot;
02255         widget_list_foot = node;
02256     }
02257 
02258     node->prev->next = node;
02259     node->next = NULL;
02260 }
02261 
02262 void insert_widget_in_container(widgetdata *widget_container, widgetdata *widget)
02263 {
02264     _widget_container *container;
02265     _widget_container_strip *container_strip;
02266 
02267     /* sanity checks */
02268     if (!widget_container || !widget)
02269     {
02270         return;
02271     }
02272 
02273     /* no, we don't want to end the universe just yet... */
02274     if (widget_container == widget)
02275     {
02276         return;
02277     }
02278 
02279     /* is the widget already in a container? */
02280     if (widget->env)
02281     {
02282         return;
02283     }
02284 
02285     /* if the widget isn't a container, get out of here */
02286     if (widget_container->WidgetTypeID != CONTAINER_ID)
02287     {
02288         return;
02289     }
02290 
02291     /* we have our container, now we attempt to place the widget inside it */
02292     container = CONTAINER(widget_container);
02293 
02294     /* check to see if the widget is compatible with it */
02295     if (container->widget_type != -1 && container->widget_type != widget->WidgetTypeID)
02296     {
02297         return;
02298     }
02299 
02300     /* if we get here, we now proceed to insert the widget into the container */
02301 
02302     /* snap the widget into the widget container if it is a strip container */
02303     if (widget_container->inv)
02304     {
02305         switch (widget_container->WidgetSubtypeID)
02306         {
02307             case CONTAINER_STRIP_ID:
02308             case MENU_ID:
02309             case MENUITEM_ID:
02310                 container_strip = CONTAINER_STRIP(widget_container);
02311 
02312                 /* container is horizontal, insert the widget to the right of the first widget in its inventory */
02313                 if (container_strip->horizontal)
02314                 {
02315                     move_widget_rec(widget, widget_container->inv->x1 + widget_container->inv->wd + container_strip->inner_padding - widget->x1, widget_container->y1 + container->outer_padding_top - widget->y1);
02316                 }
02317                 /* otherwise the container is vertical, so insert the widget below the first child widget */
02318                 else
02319                 {
02320                     move_widget_rec(widget, widget_container->x1 + container->outer_padding_left - widget->x1, widget_container->inv->y1 + widget_container->inv->ht + container_strip->inner_padding - widget->y1);
02321                 }
02322 
02323                 break;
02324         }
02325     }
02326     /* no widgets inside it yet, so snap it to the bounds of the container */
02327     else
02328     {
02329         move_widget(widget, widget_container->x1 + container->outer_padding_left - widget->x1, widget_container->y1 + container->outer_padding_top - widget->y1);
02330     }
02331 
02332     /* link up the adjacent nodes, there *should* be at least two nodes next to each other here so no sanity checks should be required */
02333     if (!widget->prev)
02334     {
02335         /* widget is no longer the root now, pass it on to the next widget */
02336         if (widget == widget_list_head)
02337         {
02338             widget_list_head = widget->next;
02339         }
02340 
02341         widget->next->prev = NULL;
02342     }
02343     else if (!widget->next)
02344     {
02345         /* widget is no longer the foot, move it to the previous widget */
02346         if (widget == widget_list_foot)
02347         {
02348             widget_list_foot = widget->prev;
02349         }
02350 
02351         widget->prev->next = NULL;
02352     }
02353     else
02354     {
02355         widget->prev->next = widget->next;
02356         widget->next->prev = widget->prev;
02357     }
02358 
02359     /* the widget to be placed inside will have a new sibling next to it, or NULL if it doesn't exist */
02360     widget->next = widget_container->inv;
02361     /* it's also going to be the first child node, so it has no siblings on the other side */
02362     widget->prev = NULL;
02363 
02364     /* if inventory doesn't exist, set the end child pointer too */
02365     if (!widget_container->inv)
02366     {
02367         widget_container->inv_rev = widget;
02368     }
02369     /* otherwise, link the first child in the inventory to the widget about to be inserted */
02370     else
02371     {
02372         widget_container->inv->prev = widget;
02373     }
02374 
02375     /* this new widget becomes the first widget in the container */
02376     widget_container->inv = widget;
02377     /* set the environment of the widget inside */
02378     widget->env = widget_container;
02379 
02380     /* resize the container to fit the new widget. a little dirty trick here,
02381      * we just resize the widget inside by nothing and it will trigger the auto-resize */
02382     resize_widget(widget, RESIZE_RIGHT, widget->wd);
02383     resize_widget(widget, RESIZE_BOTTOM, widget->ht);
02384 }
02385 
02387 widgetdata *get_outermost_container(widgetdata *widget)
02388 {
02389     widgetdata *tmp = widget;
02390 
02391     /* Sanity check. */
02392     if (!widget)
02393     {
02394         return NULL;
02395     }
02396 
02397     /* Get the outsidemost container if the widget is inside one. */
02398     while (tmp->env)
02399     {
02400         tmp = tmp->env;
02401         widget = tmp;
02402     }
02403 
02404     return widget;
02405 }
02406 
02412 widgetdata *widget_find_by_surface(SDL_Surface *surface)
02413 {
02414     widgetdata *tmp;
02415 
02416     for (tmp = widget_list_head; tmp; tmp = tmp->next)
02417     {
02418         if (tmp->widgetSF == surface)
02419         {
02420             return tmp;
02421         }
02422     }
02423 
02424     return NULL;
02425 }
02426 
02427 /* wrapper function to get the outermost container the widget is inside before moving it */
02428 void move_widget(widgetdata *widget, int x, int y)
02429 {
02430     widget = get_outermost_container(widget);
02431 
02432     move_widget_rec(widget, x, y);
02433 }
02434 
02435 /* move all widgets inside the container with the container at the same time */
02436 void move_widget_rec(widgetdata *widget, int x, int y)
02437 {
02438     /* widget doesn't exist, means the parent node has no children */
02439     if (!widget)
02440     {
02441         return;
02442     }
02443 
02444     /* no movement needed */
02445     if (x == 0 && y == 0)
02446     {
02447         return;
02448     }
02449 
02450     /* move the widget */
02451     widget->x1 += x;
02452     widget->y1 += y;
02453 
02454     /* here, we want to walk through the inventory of the widget, if it exists.
02455      * when we come across a widget, we move it like we did with the container.
02456      * we loop until we reach the last sibling, but we also need to go recursive if we find a child node */
02457     for (widget = widget->inv; widget; widget = widget->next)
02458     {
02459         move_widget_rec(widget, x, y);
02460     }
02461 }
02462 
02463 void resize_widget(widgetdata *widget, int side, int offset)
02464 {
02465     int x = widget->x1;
02466     int y = widget->y1;
02467     int width = widget->wd;
02468     int height = widget->ht;
02469 
02470     if (side & RESIZE_LEFT)
02471     {
02472         x = widget->x1 + widget->wd - offset;
02473         width = offset;
02474     }
02475     else if (side & RESIZE_RIGHT)
02476     {
02477         width = offset;
02478     }
02479 
02480     if (side & RESIZE_TOP)
02481     {
02482         y = widget->y1 + widget->ht - offset;
02483         height = offset;
02484     }
02485     else if (side & RESIZE_BOTTOM)
02486     {
02487         height = offset;
02488     }
02489 
02490     resize_widget_rec(widget, x, width, y, height);
02491 }
02492 
02493 void resize_widget_rec(widgetdata *widget, int x, int width, int y, int height)
02494 {
02495     widgetdata *widget_container, *tmp, *cmp1, *cmp2, *cmp3, *cmp4;
02496     _widget_container_strip *container_strip = NULL;
02497     _widget_container *container = NULL;
02498 
02499     /* move the widget. this is the easy bit, watch as your eyes bleed when you see the next thing we have to do */
02500     widget->x1 = x;
02501     widget->y1 = y;
02502     widget->wd = width;
02503     widget->ht = height;
02504 
02505     WIDGET_REDRAW(widget);
02506 
02507     /* now we get our parent node if it exists */
02508 
02509     /* loop until we hit the first sibling */
02510     for (widget_container = widget; widget_container->prev; widget_container = widget_container->prev)
02511     {
02512     }
02513 
02514     /* does the first sibling have a parent node? */
02515     if (widget_container->env)
02516     {
02517         /* ok, now we need to resize the parent too. but before we do this, we need to see if other widgets inside should prevent it from
02518          * being resized. the code below is ugly, but necessary in order to calculate the new size of the container. and one more thing...
02519          * MY EYES! THE GOGGLES DO NOTHING! */
02520 
02521         widget_container = widget_container->env;
02522         container = CONTAINER(widget_container);
02523 
02524         /* special case for strip containers */
02525         switch (widget_container->WidgetSubtypeID)
02526         {
02527             case CONTAINER_STRIP_ID:
02528             case MENU_ID:
02529             case MENUITEM_ID:
02530                 container_strip = CONTAINER_STRIP(widget_container);
02531 
02532                 /* we move all the widgets before or after the widget that got resized, depending on which side got the resize */
02533                 if (container_strip->horizontal)
02534                 {
02535                     /* now move everything we come across */
02536                     move_widget_rec(widget, 0, widget_container->y1 + container->outer_padding_top - widget->y1);
02537 
02538                     /* every node before the widget we push right */
02539                     for (tmp = widget->prev; tmp; tmp = tmp->prev)
02540                     {
02541                         move_widget_rec(tmp, tmp->next->x1 + tmp->next->wd - tmp->x1 + container_strip->inner_padding, widget_container->y1 + container->outer_padding_top - tmp->y1);
02542                     }
02543 
02544                     /* while every node after the widget we push left */
02545                     for (tmp = widget->next; tmp; tmp = tmp->next)
02546                     {
02547                         move_widget_rec(tmp, tmp->prev->x1 - tmp->x1 - tmp->wd - container_strip->inner_padding, widget_container->y1 + container->outer_padding_top - tmp->y1);
02548                     }
02549 
02550                     /* we have to set this, otherwise stupid things happen */
02551                     x = widget_container->inv_rev->x1;
02552                     /* we don't want the container moving up or down in this case */
02553                     y = widget_container->y1 + container->outer_padding_top;
02554                 }
02555                 else
02556                 {
02557                     /* now move everything we come across */
02558                     move_widget_rec(widget, widget_container->x1 + container->outer_padding_left - widget->x1, 0);
02559 
02560                     /* every node before the widget we push downwards */
02561                     for (tmp = widget->prev; tmp; tmp = tmp->prev)
02562                     {
02563                         move_widget_rec(tmp, widget_container->x1 + container->outer_padding_left - tmp->x1, tmp->next->y1 + tmp->next->ht - tmp->y1 + container_strip->inner_padding);
02564                     }
02565 
02566                     /* while every node after the widget we push upwards */
02567                     for (tmp = widget->next; tmp; tmp = tmp->next)
02568                     {
02569                         move_widget_rec(tmp, widget_container->x1 + container->outer_padding_left - tmp->x1, tmp->prev->y1 - tmp->y1 - tmp->ht - container_strip->inner_padding);
02570                     }
02571 
02572                     /* we don't want the container moving sideways in this case */
02573                     x = widget_container->x1 + container->outer_padding_left;
02574                     /* we have to set this, otherwise stupid things happen */
02575                     y = widget_container->inv_rev->y1;
02576                 }
02577                 break;
02578         }
02579 
02580         /* TODO: add the buffer system so that this mess of code will only need to be executed after the user stops resizing the widget */
02581         cmp1 = cmp2 = cmp3 = cmp4 = widget;
02582 
02583         for (tmp = widget_container->inv; tmp; tmp = tmp->next)
02584         {
02585             /* widget's left x co-ordinate becomes greater than tmp's left x coordinate */
02586             if (cmp1->x1 > tmp->x1)
02587             {
02588                 x = tmp->x1;
02589                 width += cmp1->x1 - tmp->x1;
02590                 cmp1 = tmp;
02591             }
02592 
02593             /* widget's top y co-ordinate becomes greater than tmp's top y coordinate */
02594             if (cmp2->y1 > tmp->y1)
02595             {
02596                 y = tmp->y1;
02597                 height += cmp2->y1 - tmp->y1;
02598                 cmp2 = tmp;
02599             }
02600 
02601             /* widget's right x co-ordinate becomes less than tmp's right x coordinate */
02602             if (cmp3->x1 + cmp3->wd < tmp->x1 + tmp->wd)
02603             {
02604                 width += tmp->x1 + tmp->wd - cmp3->x1 - cmp3->wd;
02605                 cmp3 = tmp;
02606             }
02607 
02608             /* widget's bottom y co-ordinate becomes less than tmp's bottom y coordinate */
02609             if (cmp4->y1 + cmp4->ht < tmp->y1 + tmp->ht)
02610             {
02611                 height += tmp->y1 + tmp->ht - cmp4->y1 - cmp4->ht;
02612                 cmp4 = tmp;
02613             }
02614         }
02615 
02616         x -= container->outer_padding_left;
02617         y -= container->outer_padding_top;
02618         width += container->outer_padding_left + container->outer_padding_right;
02619         height += container->outer_padding_top + container->outer_padding_bottom;
02620 
02621         /* after all that, we now check to see if the parent needs to be resized before we waste even more resources going recursive */
02622         if (x != widget_container->x1 || y != widget_container->y1 || width != widget_container->wd || height != widget_container->ht)
02623         {
02624             resize_widget_rec(widget_container, x, width, y, height);
02625         }
02626     }
02627 }
02628 
02630 widgetdata *add_label(char *text, int font, const char *color)
02631 {
02632     widgetdata *widget;
02633     _widget_label *label;
02634 
02635     widget = create_widget_object(LABEL_ID);
02636     label = LABEL(widget);
02637 
02638     label->text = text;
02639 
02640     label->font = font;
02641     label->color = color;
02642 
02643     resize_widget(widget, RESIZE_RIGHT, string_get_width(font, text, 0));
02644     resize_widget(widget, RESIZE_BOTTOM, string_get_height(font, text, 0) + 3);
02645 
02646     return widget;
02647 }
02648 
02650 widgetdata *add_bitmap(int bitmap_id)
02651 {
02652     widgetdata *widget;
02653     _widget_bitmap *bitmap;
02654 
02655     widget = create_widget_object(BITMAP_ID);
02656     bitmap = BITMAP(widget);
02657 
02658     bitmap->bitmap_id = bitmap_id;
02659 
02660     resize_widget(widget, RESIZE_RIGHT, Bitmaps[bitmap_id]->bitmap->w);
02661     resize_widget(widget, RESIZE_BOTTOM, Bitmaps[bitmap_id]->bitmap->h);
02662 
02663     return widget;
02664 }
02665 
02667 widgetdata *create_menu(int x, int y, widgetdata *owner)
02668 {
02669     widgetdata *widget_menu = create_widget_object(MENU_ID);
02670     _widget_container *container_menu = CONTAINER(widget_menu);
02671     _widget_container_strip *container_strip_menu = CONTAINER_STRIP(widget_menu);
02672 
02673     /* Place the menu at these co-ordinates. */
02674     widget_menu->x1 = x;
02675     widget_menu->y1 = y;
02676     /* Point the menu to the owner. */
02677     (MENU(widget_menu))->owner = owner;
02678     /* Magic numbers for now, maybe it will be possible in future to customize this in files. */
02679     container_menu->outer_padding_left = 2;
02680     container_menu->outer_padding_right = 2;
02681     container_menu->outer_padding_top = 2;
02682     container_menu->outer_padding_bottom = 2;
02683     container_strip_menu->inner_padding = 0;
02684 
02685     return widget_menu;
02686 }
02687 
02689 void add_menuitem(widgetdata *menu, char *text, void (*menu_func_ptr)(widgetdata *, int, int), int menu_type, int val)
02690 {
02691     widgetdata *widget_menuitem, *widget_label, *widget_bitmap, *tmp;
02692     _widget_container *container_menuitem, *container_menu;
02693     _widget_container_strip *container_strip_menuitem;
02694     _menuitem *menuitem;
02695 
02696     widget_menuitem = create_widget_object(MENUITEM_ID);
02697 
02698     container_menuitem = CONTAINER(widget_menuitem);
02699     container_strip_menuitem = CONTAINER_STRIP(widget_menuitem);
02700 
02701     /* Initialize attributes. */
02702     container_menuitem->outer_padding_left = 4;
02703     container_menuitem->outer_padding_right = 2;
02704     container_menuitem->outer_padding_top = 2;
02705     container_menuitem->outer_padding_bottom = 0;
02706     container_strip_menuitem->inner_padding = 4;
02707     container_strip_menuitem->horizontal = 1;
02708 
02709     widget_label = add_label(text, FONT_ARIAL10, COLOR_WHITE);
02710 
02711     if (menu_type == MENU_CHECKBOX)
02712     {
02713         /* This is really just test code to see if bitmaps work.
02714          * Menuitems will later contain checkboxes later anyway,
02715          * so this will probably evolve into proper code later. */
02716         widget_bitmap = add_bitmap(val ? BITMAP_CHECKBOX_ON : BITMAP_CHECKBOX);
02717         insert_widget_in_container(widget_menuitem, widget_bitmap);
02718     }
02719 
02720     insert_widget_in_container(widget_menuitem, widget_label);
02721     insert_widget_in_container(menu, widget_menuitem);
02722 
02723     /* Add the pointer to the function to the menuitem. */
02724     menuitem = MENUITEM(widget_menuitem);
02725     menuitem->menu_func_ptr = menu_func_ptr;
02726     menuitem->menu_type = menu_type;
02727 
02728     /* Sanity check. Menuitems should always exist inside a menu. */
02729     if (widget_menuitem->env && widget_menuitem->env->WidgetSubtypeID == MENU_ID)
02730     {
02731         container_menu = CONTAINER(widget_menuitem->env);
02732 
02733         /* Resize labels in each menuitem to the width of the menu. */
02734         for (tmp = widget_menuitem; tmp; tmp = tmp->next)
02735         {
02736             if (tmp->inv)
02737             {
02738                 container_menuitem = CONTAINER(tmp);
02739 
02740                 if (menu_type == MENU_CHECKBOX)
02741                 {
02742                     resize_widget(tmp->inv, RESIZE_RIGHT, menu->wd - tmp->inv_rev->wd - 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);
02743                 }
02744                 else
02745                 {
02746                     resize_widget(tmp->inv, RESIZE_RIGHT, menu->wd - container_menu->outer_padding_left - container_menu->outer_padding_right - container_menuitem->outer_padding_left - container_menuitem->outer_padding_right);
02747                 }
02748             }
02749         }
02750     }
02751 }
02752 
02754 void add_separator(widgetdata *widget)
02755 {
02756     (void) widget;
02757 }
02758 
02765 void menu_finalize(widgetdata *widget)
02766 {
02767     int xoff = 0, yoff = 0;
02768 
02769     /* Would the menu go over the maximum screen width? */
02770     if (widget->x1 + widget->wd > ScreenSurface->w)
02771     {
02772         /* Will appear to the left of the cursor instead of right of it. */
02773         xoff = -widget->wd;
02774 
02775         /* Take submenus into account, and shift them depending on the
02776          * parent menu's width. */
02777         if (widget->type_prev && widget->type_prev->WidgetSubtypeID == MENU_ID)
02778         {
02779             xoff += -widget->type_prev->wd + 4;
02780         }
02781     }
02782 
02783     /* Similar checks for screen height. */
02784     if (widget->y1 + widget->ht > ScreenSurface->h)
02785     {
02786         /* Submenu, shift it up, so all of it can appear. */
02787         if (widget->type_prev && widget->type_prev->WidgetSubtypeID == MENU_ID)
02788         {
02789             yoff = ScreenSurface->h - widget->ht - widget->y1 - 1;
02790         }
02791         /* Will appear above the cursor. */
02792         else
02793         {
02794             yoff = -widget->ht;
02795         }
02796     }
02797 
02798     move_widget(widget, xoff, yoff);
02799 }
02800 
02802 void widget_redraw_all(int widget_type_id)
02803 {
02804     widgetdata *widget = cur_widget[widget_type_id];
02805 
02806     for (; widget; widget = widget->type_next)
02807     {
02808         widget->redraw = 1;
02809     }
02810 }
02811 
02812 void menu_move_widget(widgetdata *widget, int x, int y)
02813 {
02814     widget_event_start_move(widget, x, y);
02815 }
02816 
02817 void menu_create_widget(widgetdata *widget, int x, int y)
02818 {
02819     (void) x;
02820     (void) y;
02821     create_widget_object(widget->WidgetSubtypeID);
02822 }
02823 
02824 void menu_remove_widget(widgetdata *widget, int x, int y)
02825 {
02826     (void) x;
02827     (void) y;
02828     remove_widget_object(widget);
02829 }
02830 
02831 void menu_detach_widget(widgetdata *widget, int x, int y)
02832 {
02833     (void) x;
02834     (void) y;
02835     detach_widget(widget);
02836 }
02837 
02838 void menu_set_say_filter(widgetdata *widget, int x, int y)
02839 {
02840     (void) widget;
02841     (void) x;
02842     (void) y;
02843 }
02844 
02845 void menu_set_shout_filter(widgetdata *widget, int x, int y)
02846 {
02847     (void) widget;
02848     (void) x;
02849     (void) y;
02850 }
02851 
02852 void menu_set_gsay_filter(widgetdata *widget, int x, int y)
02853 {
02854     (void) widget;
02855     (void) x;
02856     (void) y;
02857 }
02858 
02859 void menu_set_tell_filter(widgetdata *widget, int x, int y)
02860 {
02861     (void) widget;
02862     (void) x;
02863     (void) y;
02864 }
02865 
02866 void menu_set_channel_filter(widgetdata *widget, int x, int y)
02867 {
02868     (void) widget;
02869     (void) x;
02870     (void) y;
02871 }
02872 
02873 void submenu_chatwindow_filters(widgetdata *widget, int x, int y)
02874 {
02875     (void) x;
02876     (void) y;
02877     add_menuitem(widget, "Say", &menu_set_say_filter, MENU_CHECKBOX, 0);
02878     add_menuitem(widget, "Shout", &menu_set_shout_filter, MENU_CHECKBOX, 0);
02879     add_menuitem(widget, "Group", &menu_set_gsay_filter, MENU_CHECKBOX, 0);
02880     add_menuitem(widget, "Tells", &menu_set_tell_filter, MENU_CHECKBOX, 0);
02881     add_menuitem(widget, "Channels", &menu_set_channel_filter, MENU_CHECKBOX, 0);
02882 }
02883 
02884 void menu_inv_filter_all()
02885 {
02886     inventory_filter_set(INVENTORY_FILTER_ALL);
02887 }
02888 
02889 void menu_inv_filter_applied()
02890 {
02891     inventory_filter_toggle(INVENTORY_FILTER_APPLIED);
02892 }
02893 
02894 void menu_inv_filter_containers()
02895 {
02896     inventory_filter_toggle(INVENTORY_FILTER_CONTAINER);
02897 }
02898 
02899 void menu_inv_filter_magical()
02900 {
02901     inventory_filter_toggle(INVENTORY_FILTER_MAGICAL);
02902 }
02903 
02904 void menu_inv_filter_cursed()
02905 {
02906     inventory_filter_toggle(INVENTORY_FILTER_CURSED);
02907 }
02908 
02909 void menu_inv_filter_unidentified()
02910 {
02911     inventory_filter_toggle(INVENTORY_FILTER_UNIDENTIFIED);
02912 }
02913 
02914 void menu_inv_filter_locked()
02915 {
02916     inventory_filter_toggle(INVENTORY_FILTER_LOCKED);
02917 }
02918 
02919 void menu_inv_filter_unapplied()
02920 {
02921     inventory_filter_toggle(INVENTORY_FILTER_UNAPPLIED);
02922 }