Crossfire Client, Branch  R11627
config.c
Go to the documentation of this file.
00001 const char * const rcsid_gtk_config_c =
00002     "$Id: config.c 9576 2008-07-19 23:42:35Z kbulgrien $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 /* This file handles the loading and saving of the configuration options,
00026  * as well as presenting a nice gui to select the
00027  * options
00028  */
00029 
00030 #include <config.h>
00031 
00032 #ifdef __CYGWIN__
00033 #include <errno.h>
00034 #endif
00035 
00036 /* gtk */
00037 #include <gtk/gtk.h>
00038 #ifndef WIN32
00039 #include <gdk/gdkx.h>
00040 #else
00041 #include <gdk/gdkwin32.h>
00042 #endif
00043 #include <gdk/gdkkeysyms.h>
00044 
00045 
00046 /* always include our local headers after the system headers are included */
00047 #include "client.h"
00048 /*#include "clientbmap.h"*/
00049 #include "item.h"
00050 #include "gx11.h"
00051 #include "gtkproto.h"
00052 #include <ctype.h>
00053 
00054 
00055 /* Abstract this out a bit - rather than have a whole bunch of
00056  * duplicated code that generates these values, instead use
00057  * pointers to functions that set and get the values - this
00058  * makes adding new widgets a lot easier in most cases.
00059  *
00060  * button is the actual widget that is created.
00061  *
00062  * label is the label that is to be printed
00063  *
00064  * type if the type of widget.  As long as the widget only
00065  * needs to deal with numeric type values, this works
00066  * fine - this means that dials can get just as easily
00067  * added to this list
00068  *
00069  * config holds the corresponding CONFIG value from
00070  * the common/client.h file.  This allows
00071  * use to use a fairly common function for most
00072  * values.
00073  *
00074  * flags holds flags.  Currently, the
00075  * only flag is FLAG_UPDATE - if set,
00076  * then we automatically the running total
00077  * immediately.  Otherwise, the want_config value
00078  * only gets updated, and depending on the value,
00079  * that value may get used/copied into the use_config
00080  * at a later point.  Many values which can change at
00081  * runtime do not have this flag set simply so that it
00082  * is easier to notice if it has changed and do the
00083  * appropraite thing (eg, stop/start sound daemon, etc)
00084  *
00085  * Note on RBUTTON (radio button usage):  Since a radio
00086  * button is a collection of buttons of only which one can
00087  * be pressed, the logic the program uses is this:
00088  * 1) If the previous widget was a radio button, we add
00089  * this one to the same group.  This means if you want to
00090  * have multiple sets of radio buttons, you should seperate
00091  * them with something.
00092  * 2) Since the radio button is several widgets, its not as simple
00093  * as normal buttons to map them to a config value.  Instead,
00094  * use a range so that it is easy to tell what config value
00095  * your button belongs to, eg, 100-199 is for the lighting
00096  * options.
00097  */
00098 
00099 #define MAX_BUTTONS         33
00100 #define RBUTTON     1
00101 #define CBUTTON     2
00102 #define SEPERATOR   3       /* Seperator in the window */
00103 
00104 #define SPIN        0x100
00105 #define SPIN_SCALE  0x101   /* Spin Button that is image scale */
00106 #define SPIN_MAP    0x102   /* Spin button that is map size */
00107 #define SPIN_CWINDOW 0x103  /* Spin command window */
00108 
00109 #define FLAG_UPDATE     0x1
00110 #define FLAG_MAPPANE    0x2     /* Display on the map/image pane */
00111 
00112 typedef struct {
00113     GtkWidget   *widget;
00114     int         type;
00115     int         config;
00116     int         flags;
00117     const char  *label;
00118 } CButtons;
00119 
00120 static GtkWidget *gtkwin_config = NULL,     /* main window */
00121     *faceset_combo;                         /* Combo box for faceset selection */
00122 
00123 
00124 /* A dispatch table that can deal with the entire selection of
00125  * config gui elements.
00126  */
00127 
00128 static CButtons cbuttons[MAX_BUTTONS] = {
00129 {NULL,      CBUTTON,        CONFIG_FOODBEEP,    FLAG_UPDATE,
00130     "Beep When Food is Low"},
00131 {NULL,      CBUTTON,        CONFIG_TIMESTAMP,   FLAG_UPDATE,
00132     "Timestamp Messages"},
00133 {NULL,      SPIN_CWINDOW,   CONFIG_CWINDOW,     FLAG_UPDATE,
00134     "Command Window"},
00135 {NULL,      CBUTTON,        CONFIG_ECHO,        FLAG_UPDATE,
00136     "Echo Bound Commands"},
00137 {NULL,      CBUTTON,        CONFIG_FASTTCP,     0,
00138     "Fast TCP Send (May improve performance at expense\n of outgoing bandwidth)"},
00139 {NULL,      CBUTTON,        CONFIG_GRAD_COLOR,  FLAG_UPDATE,
00140     "Gradually change stat bar color based on value of the stat.\nThis option will result in some extra CPU usage."},
00141 {NULL,      CBUTTON,        CONFIG_POPUPS,      FLAG_UPDATE,
00142     "Popup Windows"},
00143 {NULL,      CBUTTON,        CONFIG_SIGNPOPUP,   FLAG_UPDATE,
00144     "Popup Sign Windows (need Popup Windows checked to be used)"},
00145 {NULL,      CBUTTON,        CONFIG_SPLASH,      FLAG_UPDATE,
00146     "Splash Window"},
00147 {NULL,      CBUTTON,        CONFIG_SHOWICON,    FLAG_UPDATE,
00148     "Show Inventory Icon"},
00149 {NULL,      CBUTTON,        CONFIG_TOOLTIPS,    0,
00150     "Show Tooltips"},
00151 {NULL,      CBUTTON,        CONFIG_SOUND,       0,
00152     "Sound"},
00153 {NULL,      CBUTTON,        CONFIG_SPLITINFO,   0,
00154     "Split Information Window (Takes effect next run)"},
00155 {NULL,      CBUTTON,        CONFIG_SPLITWIN,    0,
00156     "Split Windows"},
00157 {NULL,      CBUTTON,        CONFIG_TRIMINFO,    FLAG_UPDATE,
00158     "Trims text in the information window - " 
00159     "improves performance but bugs in\n gtk make the client unstable if this is used." 
00160     "This may work better with gtk 2.0"},
00161 {NULL,      CBUTTON,        CONFIG_APPLY_CONTAINER,     FLAG_UPDATE,
00162     "Automatically re-applies a container when you use apply to close it. \nIf off, when you use apply to close the container, it stays unapplied"},
00163 
00164 {NULL,      CBUTTON,        CONFIG_RESISTS,     0,
00165     "Display resistances in two columns rather than only one."},
00166 
00167 /* The following items are shown in the map tag.
00168  * I grouped them together to make reading them a bit easier,
00169  * but in fact, they could be intermixed with the other
00170  * options.
00171  */
00172 
00173 {NULL,      CBUTTON,        CONFIG_CACHE,       FLAG_MAPPANE,
00174     "Cache Images"},
00175 {NULL,      CBUTTON,        CONFIG_DOWNLOAD,    FLAG_MAPPANE | FLAG_UPDATE,
00176     "Download All Image Information (Takes effect on next server connection)"},
00177 {NULL,      CBUTTON,        CONFIG_FOGWAR,      FLAG_MAPPANE | FLAG_UPDATE,
00178     "Fog of War"},
00179 {NULL,      SPIN_SCALE,     CONFIG_ICONSCALE,   FLAG_MAPPANE,
00180     "Icon Scale (Takes effect next run)"},
00181 {NULL,      SPIN_SCALE,     CONFIG_MAPSCALE,    FLAG_MAPPANE,
00182     "Map Scale (Takes effect next run)"},
00183 {NULL,      CBUTTON,        CONFIG_SMOOTH,      FLAG_MAPPANE | FLAG_UPDATE,
00184     "Enable smoothing - Use additionnal CPU (Take effect on next connection)."},
00185 {NULL,      CBUTTON,        CONFIG_DISPLAYMODE, FLAG_MAPPANE,
00186     "SDL Image Support (Take effect next run)"},
00187 {NULL,      CBUTTON,        CONFIG_SHOWGRID,    FLAG_MAPPANE | FLAG_UPDATE,
00188     "Print Grid Overlay (SDL only, Slow, useful for debugging/development"},
00189 
00190 {NULL,      SEPERATOR,          0,              FLAG_MAPPANE,
00191     "Lighting options, per pixel is prettier, per tile is faster.\nIf the darkness code is off, the pixel/tile options will be ignored."},
00192 {NULL,      RBUTTON,        100 + CFG_LT_PIXEL_BEST,    FLAG_MAPPANE,
00193     "Best Per Pixel Lighting (slowest)"},
00194 {NULL,      RBUTTON,        100 + CFG_LT_PIXEL, FLAG_MAPPANE,
00195     "Fast Per Pixel Lighting"},
00196 {NULL,      RBUTTON,        100 + CFG_LT_TILE,  FLAG_MAPPANE,
00197     "Per Tile Lighting"},
00198 {NULL,      CBUTTON,        CONFIG_DARKNESS,    FLAG_MAPPANE | FLAG_UPDATE,
00199     "Enable darkness code - if off, all spaces will not be dimmed."},
00200 
00201 {NULL,      SEPERATOR,      0,                  FLAG_MAPPANE,
00202     "Map Size: Larger map lets you see more information, but takes more CPU\npower and bandwidth.  Changing these will not take effect until the next time\nyou connect to a server"},
00203 {NULL,      SPIN_MAP,       CONFIG_MAPHEIGHT,   FLAG_MAPPANE,
00204     "Map Height"},
00205 {NULL,      SPIN_MAP,       CONFIG_MAPWIDTH,    FLAG_MAPPANE,
00206     "Map Width"},
00207 };
00208 
00209 
00210 static void set_config_value(int cval, int value)
00211 {
00212     want_config[cbuttons[cval].config] = value;
00213     if (cbuttons[cval].flags & FLAG_UPDATE)
00214         use_config[cbuttons[cval].config] = value;
00215 }
00216 
00217 static int splitwin_toggling = FALSE;
00218 
00219 void main_window_destroyed(void) {
00220     if (!splitwin_toggling) {
00221         client_exit();
00222     }
00223 }
00224 
00225 static void toggle_splitwin(int newval)
00226 {
00227     splitwin_toggling = TRUE;
00228 
00229     inventory_splitwin_toggling();
00230         gtk_widget_destroy(gtkwin_root);
00231 
00232     if (newval) {
00233         ; /* Currently don't have it, but want splitwindows */
00234     } else {
00235         /* opposite - do have it, but don't want it */
00236         gtk_widget_destroy(gtkwin_info);
00237         gtk_widget_destroy(gtkwin_stats);
00238         gtk_widget_destroy(gtkwin_message);
00239         gtk_widget_destroy(gtkwin_inv);
00240         gtk_widget_destroy(gtkwin_look);
00241     }
00242 
00243         create_windows();
00244         display_map_doneupdate(TRUE, FALSE);
00245         draw_stats (1);
00246     update_list_labels(&inv_list); /* After exploding or unexploding client, redraw weight labels. */
00247     update_list_labels(&look_list);
00248 
00249     splitwin_toggling = FALSE;
00250 }
00251 
00252 /* Ok, here it sets the config and saves it. This is sorta dangerous, and I'm not sure
00253  * if it's actually possible to do dynamic reconfiguration of everything this way. Something may
00254  * blow up in our faces.
00255  */
00256 
00257 #define IS_DIFFERENT(TYPE) (want_config[TYPE] != use_config[TYPE])
00258 
00259 static void applyconfig(void) {
00260 
00261     int onbutton;
00262     int lighting = 0;
00263 
00264     free(face_info.want_faceset);
00265     face_info.want_faceset = strdup_local(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(faceset_combo)->entry)));
00266     for (onbutton =0; onbutton < MAX_BUTTONS; onbutton++) {
00267         if (cbuttons[onbutton].type == CBUTTON) {
00268             set_config_value(onbutton, GTK_TOGGLE_BUTTON (cbuttons[onbutton].widget)->active);
00269         } else if (cbuttons[onbutton].type & SPIN) {
00270             set_config_value(onbutton, gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cbuttons[onbutton].widget)));
00271             /*
00272              * Nothing special for command window, icon_scale, map_scale,
00273              * map width and height.  It should be possible to dynamically
00274              * change the width and height values, but that is for another day.
00275              */
00276 
00277         } else if (cbuttons[onbutton].type == RBUTTON) {
00278             /* We know that the only radio buttons currently in use are those for
00279              * lighting.  IF other radio buttons are added later, this should
00280              * be changed.
00281              */
00282             if ( GTK_TOGGLE_BUTTON (cbuttons[onbutton].widget)->active) {
00283                 if ( cbuttons[onbutton].config >= 100 &&  cbuttons[onbutton].config < 200)
00284                     lighting = cbuttons[onbutton].config - 100;
00285             }
00286         }
00287     } /* for onbutton ... loop */
00288 
00289 
00290     /* User has toggled splitwindows - adjust accordingly */
00291     if (IS_DIFFERENT(CONFIG_SPLITWIN)) {
00292         use_config[CONFIG_SPLITWIN] = want_config[CONFIG_SPLITWIN];
00293         toggle_splitwin(want_config[CONFIG_SPLITWIN]);
00294     }
00295     if (IS_DIFFERENT(CONFIG_SOUND)) {
00296         int tmp;
00297         if (want_config[CONFIG_SOUND]) {
00298             tmp = init_sounds();
00299             if (csocket.fd)
00300                 cs_print_string(csocket.fd, "setup sound %d", tmp >= 0);
00301         } else {
00302             if (csocket.fd)
00303                 cs_print_string(csocket.fd, "setup sound 0");
00304         }
00305         use_config[CONFIG_SOUND] = want_config[CONFIG_SOUND];
00306     }
00307     if (IS_DIFFERENT(CONFIG_TOOLTIPS)) {
00308         if (want_config[CONFIG_TOOLTIPS]) gtk_tooltips_enable(tooltips);
00309         else gtk_tooltips_disable(tooltips);
00310         use_config[CONFIG_TOOLTIPS] = want_config[CONFIG_TOOLTIPS];
00311     }
00312     else if (IS_DIFFERENT(CONFIG_FASTTCP)) {
00313 #ifdef TCP_NODELAY
00314 #ifndef WIN32
00315         int q = want_config[CONFIG_FASTTCP];
00316 
00317         if (csocket.fd && setsockopt(csocket.fd, SOL_TCP, TCP_NODELAY, &q, sizeof(q)) == -1)
00318             perror("TCP_NODELAY");
00319 #else
00320         int q = want_config[CONFIG_FASTTCP];
00321 
00322         if (csocket.fd && setsockopt(csocket.fd, SOL_TCP, TCP_NODELAY, ( const char* )&q, sizeof(q)) == -1)
00323             perror("TCP_NODELAY");
00324 #endif
00325 #endif
00326         use_config[CONFIG_FASTTCP] = want_config[CONFIG_FASTTCP];
00327     }
00328     if (IS_DIFFERENT(CONFIG_SHOWICON)) {
00329         itemlist_set_show_icon(&inv_list, want_config[CONFIG_SHOWICON]);
00330         /* TODO What about the look list? And should showicon propogate back here? */
00331         use_config[CONFIG_SHOWICON] = want_config[CONFIG_SHOWICON];
00332     }
00333     if (IS_DIFFERENT(CONFIG_RESISTS)) {
00334         use_config[CONFIG_RESISTS] = want_config[CONFIG_RESISTS];
00335         resize_resistance_table(use_config[CONFIG_RESISTS]);
00336     }
00337     if (!use_config[CONFIG_GRAD_COLOR]) {
00338         reset_stat_bars();
00339     }
00340 
00341     if (lighting) {
00342         if (want_config[CONFIG_LIGHTING] != lighting) {
00343             want_config[CONFIG_LIGHTING] = lighting;
00344             use_config[CONFIG_LIGHTING] = lighting;
00345         }
00346 #ifdef HAVE_SDL
00347         if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL)
00348             /* This is done to make the 'lightmap' in the proper format */
00349             init_SDL( NULL, 1);
00350 #endif
00351     }
00352     if (want_config[CONFIG_RESISTS] != use_config[CONFIG_RESISTS]) {
00353         resize_resistance_table(want_config[CONFIG_RESISTS]);
00354         use_config[CONFIG_RESISTS] = want_config[CONFIG_RESISTS];
00355         draw_message_window(1);
00356     }
00357 }
00358 
00359 
00360 /* Ok, here it sets the config and saves it. This is sorta dangerous, and I'm not sure
00361  * if it's actually possible to do dynamic reconfiguration of everything this way.
00362  */
00363 
00364 static void saveconfig(void) {
00365 
00366     /* No idea why applyconfig was basically replicated - just call the
00367      * function instead!
00368      */
00369     applyconfig();
00370     save_defaults();
00371 }
00372 
00373 /*
00374  *  GUI Config dialog.
00375  *
00376  *
00377  */
00378 
00379 void configdialog(GtkWidget *widget) {
00380     GtkWidget *vbox;
00381     GtkWidget *tablabel;
00382     GtkWidget *notebook;
00383     GtkWidget *vbox1;
00384     GtkWidget *vbox2;
00385     GtkWidget *hbox1;
00386     GtkWidget *applybutton;
00387     GtkWidget *cancelbutton;
00388     GtkWidget *savebutton;
00389     GtkWidget *frame1;
00390     GtkWidget *frame_map, *vbox_map;    /* frame and vbox for map notebook */
00391     GtkWidget *addwidget;               /* Used in buildin the tab to point to the widget to add to */
00392     GtkWidget *ehbox;
00393     GtkWidget *clabel1, *clabel2, *clabel4, *clabel5, *cb1, *cb2, *cb3;
00394     GtkWidget *cclists;
00395     GtkWidget *extras[250];
00396     GList       *flist;
00397     int i, num_extras=0;
00398 
00399     gchar *titles[] ={"#","Key","(#)","Mods","Command"};
00400 
00401     /* If the window isnt already up (in which case it's just raised) */
00402     if(!gtkwin_config) {
00403         int x, y, wx, wy, w, h;
00404 
00405 
00406         gtkwin_config = gtk_window_new (GTK_WINDOW_DIALOG);
00407         /* Pet peeve - center new window on top of parent, and not on the
00408          * the center of the screen - the later is really annoying in
00409          * xinerama mode.  Thankfully, GTK 2.0 adds an option to
00410          * center on parent - for now, just fake it by getting the parents
00411          * geometry.
00412          */
00413         /*gtk_window_position (GTK_WINDOW (gtkwin_config), GTK_WIN_POS_CENTER);*/
00414         get_window_coord(gtkwin_root, &x,&y, &wx,&wy,&w,&h);
00415         gtk_widget_set_uposition(gtkwin_config, (wx + w - 450)/2, (wy + h-500) / 2);
00416         gtk_widget_set_usize (gtkwin_config,450,600);
00417         gtk_window_set_title (GTK_WINDOW (gtkwin_config), "Crossfire Configure");
00418         gtk_window_set_policy (GTK_WINDOW (gtkwin_config), TRUE, TRUE, FALSE);
00419 
00420         gtk_signal_connect (GTK_OBJECT (gtkwin_config), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &gtkwin_config);
00421 
00422         gtk_container_border_width (GTK_CONTAINER (gtkwin_config), 0);
00423 
00424         /* vbox splits the window - top portion is the notebook, bottom
00425          * portion is for the tabs for apply/save/config.
00426          */
00427         vbox = gtk_vbox_new(FALSE, 2);
00428         gtk_container_add (GTK_CONTAINER(gtkwin_config),vbox);
00429 
00430         notebook = gtk_notebook_new ();
00431         gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP );
00432         gtk_box_pack_start (GTK_BOX(vbox),notebook, TRUE, TRUE, 0);
00433 
00434         tablabel = gtk_label_new ("General");
00435         gtk_widget_show (tablabel);
00436 
00437         frame1 = gtk_frame_new("General options");
00438         gtk_frame_set_shadow_type (GTK_FRAME(frame1), GTK_SHADOW_ETCHED_IN);
00439         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame1, tablabel);
00440 
00441         vbox1 = gtk_vbox_new(FALSE, 0);
00442         gtk_container_add (GTK_CONTAINER(frame1), vbox1);
00443 
00444         tablabel = gtk_label_new ("Map & Image");
00445         gtk_widget_show (tablabel);
00446 
00447         frame_map = gtk_frame_new("Map and Image options");
00448         gtk_frame_set_shadow_type (GTK_FRAME(frame_map), GTK_SHADOW_ETCHED_IN);
00449         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame_map, tablabel);
00450 
00451         vbox_map = gtk_vbox_new(FALSE, 0);
00452         gtk_container_add (GTK_CONTAINER(frame_map), vbox_map);
00453 
00454         for (i=0; i < MAX_BUTTONS; i++) {
00455             if (cbuttons[i].flags & FLAG_MAPPANE)
00456                 addwidget = vbox_map;
00457             else
00458                 addwidget = vbox1;
00459 
00460             if (cbuttons[i].type == CBUTTON) {
00461                 cbuttons[i].widget = gtk_check_button_new_with_label(cbuttons[i].label);
00462                 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cbuttons[i].widget), want_config[cbuttons[i].config]);
00463             }
00464             else if (cbuttons[i].type == RBUTTON) {
00465                 if ((i>0) && (cbuttons[i-1].type == RBUTTON)) {
00466                     cbuttons[i].widget = gtk_radio_button_new_with_label_from_widget(
00467                                 GTK_RADIO_BUTTON(cbuttons[i-1].widget), cbuttons[i].label);
00468                 } else {
00469                     cbuttons[i].widget = gtk_radio_button_new_with_label(NULL, cbuttons[i].label);
00470                 }
00471                 if ((want_config[CONFIG_LIGHTING]+100) == cbuttons[i].config)
00472                     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cbuttons[i].widget), 1);
00473             }
00474             else if (cbuttons[i].type & SPIN) {
00475                 GtkAdjustment *adj=NULL;
00476 
00477                 if (cbuttons[i].type == SPIN_SCALE)
00478                     adj = (GtkAdjustment *) gtk_adjustment_new(want_config[cbuttons[i].config], 25, 200, 1, 5, 5);
00479                 else if (cbuttons[i].type == SPIN_MAP)
00480                     adj = (GtkAdjustment *) gtk_adjustment_new(want_config[cbuttons[i].config], 9, MAP_MAX_SIZE, 1, 5, 5);
00481                 else if (cbuttons[i].type == SPIN_CWINDOW)
00482                     adj = (GtkAdjustment *) gtk_adjustment_new(want_config[cbuttons[i].config], 1, 127, 1, 5, 5);
00483                 cbuttons[i].widget = gtk_spin_button_new(adj, 1, 0);
00484                 extras[num_extras] = gtk_hbox_new(FALSE, 2);
00485                 gtk_box_pack_start(GTK_BOX(extras[num_extras]), cbuttons[i].widget, FALSE, FALSE, 0);
00486                 extras[++num_extras] = gtk_label_new(cbuttons[i].label);
00487                 gtk_box_pack_start(GTK_BOX(extras[num_extras-1]), extras[num_extras], FALSE, FALSE, 0);
00488                 gtk_box_pack_start(GTK_BOX(addwidget), extras[num_extras-1], FALSE, FALSE, 0);
00489                 num_extras++;
00490                 extras[num_extras++] = cbuttons[i].widget;
00491                 continue;   /* What to skip the box_pack_start below */
00492             }
00493             else if (cbuttons[i].type == SEPERATOR) {
00494                 extras[num_extras] = (GtkWidget*)gtk_hseparator_new ();
00495                 gtk_box_pack_start (GTK_BOX (addwidget), extras[num_extras], FALSE, FALSE, 0);
00496                 cbuttons[i].widget = gtk_label_new(cbuttons[i].label);
00497                 gtk_label_set_justify(GTK_LABEL(cbuttons[i].widget), GTK_JUSTIFY_LEFT);
00498                 num_extras++;
00499             }
00500             else {
00501             LOG(LOG_WARNING,"gtk::configdialog","Unknown cbutton type %d", cbuttons[i].type);
00502             }
00503             if (cbuttons[i].widget) {
00504                 extras[num_extras++] = cbuttons[i].widget;
00505                 gtk_box_pack_start(GTK_BOX(addwidget), cbuttons[i].widget, FALSE, FALSE, 0);
00506             }
00507         }
00508 
00509         for (i=0; i < num_extras; i++) {
00510             gtk_widget_show(extras[i]);
00511         }
00512 
00513         /* faceset is special because it is string data. */
00514         faceset_combo = gtk_combo_new();
00515         flist = NULL;
00516         if (face_info.want_faceset) {
00517             gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(faceset_combo)->entry), face_info.want_faceset);
00518             flist = g_list_append(flist, face_info.want_faceset);
00519         }
00520 
00521         /* If we have real faceset info from the server, use it */
00522         if (face_info.have_faceset_info) {
00523             for (i=0; i<MAX_FACE_SETS; i++)
00524                 if (face_info.facesets[i].fullname)
00525                     flist = g_list_append(flist, face_info.facesets[i].fullname);
00526         } else {
00527             flist = g_list_append(flist, "standard");
00528             flist = g_list_append(flist, "classic");
00529         }
00530         if (flist) gtk_combo_set_popdown_strings(GTK_COMBO(faceset_combo), flist);
00531         addwidget = gtk_hbox_new(FALSE, 0);
00532         gtk_box_pack_start(GTK_BOX(addwidget), faceset_combo, FALSE, FALSE, 0);
00533         tablabel = gtk_label_new("Faceset to use.  Only takes effect for new\n face information from server.  Not supported on\n all servers.");
00534         gtk_label_set_justify(GTK_LABEL(tablabel), GTK_JUSTIFY_LEFT);
00535         gtk_box_pack_start(GTK_BOX(addwidget), tablabel, FALSE, FALSE, 0);
00536 
00537         gtk_box_pack_start(GTK_BOX(vbox_map), addwidget, FALSE, FALSE, 0);
00538         gtk_widget_show(tablabel);
00539         gtk_widget_show(faceset_combo);
00540         gtk_widget_show(addwidget);
00541 
00542         gtk_widget_show (vbox1);
00543         gtk_widget_show (frame1);
00544         gtk_widget_show(vbox_map);
00545         gtk_widget_show(frame_map);
00546 
00547 
00548         /*
00549          * This block deals with drawing the keybindings
00550          * block.
00551          */
00552 
00553         tablabel = gtk_label_new ("Keybindings");
00554         gtk_widget_show (tablabel);
00555         vbox2 = gtk_vbox_new(FALSE, 0);
00556         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox2, tablabel);
00557         frame1 = gtk_frame_new("Keybindings");
00558         gtk_frame_set_shadow_type (GTK_FRAME(frame1), GTK_SHADOW_ETCHED_IN);
00559         gtk_box_pack_start (GTK_BOX (vbox2), frame1, TRUE, TRUE, 0);
00560         vbox1 = gtk_vbox_new(FALSE, 0);
00561         gtk_container_add (GTK_CONTAINER(frame1), vbox1);
00562         cclists = gtk_scrolled_window_new (0,0);
00563         cclist = gtk_clist_new_with_titles (5, titles);
00564 
00565         gtk_clist_set_column_width (GTK_CLIST(cclist), 0, 20);
00566         gtk_clist_set_column_width (GTK_CLIST(cclist), 1, 50);
00567         gtk_clist_set_column_width (GTK_CLIST(cclist), 2, 20);
00568         gtk_clist_set_column_width (GTK_CLIST(cclist), 3, 40);
00569         gtk_clist_set_column_width (GTK_CLIST(cclist), 4, 245);
00570         gtk_clist_set_selection_mode (GTK_CLIST(cclist) , GTK_SELECTION_SINGLE);
00571 
00572         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(cclists),
00573                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
00574         gtk_container_add (GTK_CONTAINER (cclists), cclist);
00575         gtk_box_pack_start (GTK_BOX(vbox1),cclists, TRUE, TRUE, 0);
00576         draw_keybindings (cclist);
00577 
00578         gtk_signal_connect_after (GTK_OBJECT(cclist),
00579                               "select_row",
00580                               GTK_SIGNAL_FUNC(cclist_button_event),
00581                               NULL);
00582 
00583         gtk_widget_show(cclist);
00584         gtk_widget_show(cclists);
00585 
00586         ehbox=gtk_hbox_new(FALSE, 0);
00587 
00588 
00589         clabel1 =  gtk_label_new ("Binding #:");
00590         gtk_box_pack_start (GTK_BOX (ehbox),clabel1, FALSE, TRUE, 2);
00591         gtk_widget_show (clabel1);
00592 
00593         cnumentrytext = gtk_label_new ("0");
00594         gtk_box_pack_start (GTK_BOX (ehbox),cnumentrytext, FALSE, TRUE, 2);
00595         gtk_widget_set_usize (cnumentrytext, 25, 0);
00596         gtk_widget_show (cnumentrytext);
00597 
00598         clabel2 =  gtk_label_new ("Key:");
00599         gtk_box_pack_start (GTK_BOX (ehbox),clabel2, FALSE, TRUE, 2);
00600         gtk_widget_show (clabel2);
00601 
00602         ckeyentrytext = gtk_entry_new ();
00603         gtk_box_pack_start (GTK_BOX (ehbox),ckeyentrytext, TRUE, TRUE, 2);
00604         gtk_widget_set_usize (ckeyentrytext, 110, 0);
00605         gtk_signal_connect(GTK_OBJECT(ckeyentrytext), "key_press_event",
00606                        GTK_SIGNAL_FUNC(ckeyentry_callback),
00607                        ckeyentrytext);
00608         gtk_widget_show (ckeyentrytext);
00609         gtk_entry_set_text (GTK_ENTRY(ckeyentrytext),  "Press key to bind here");
00610 
00611         clabel4 =  gtk_label_new ("Mods:");
00612         gtk_box_pack_start (GTK_BOX (ehbox),clabel4, FALSE, TRUE, 2);
00613         gtk_widget_show (clabel4);
00614 
00615         cmodentrytext = gtk_entry_new ();
00616         gtk_box_pack_start (GTK_BOX (ehbox),cmodentrytext, FALSE, TRUE, 2);
00617         gtk_widget_set_usize (cmodentrytext, 45, 0);
00618         gtk_widget_show (cmodentrytext);
00619 
00620         gtk_box_pack_start (GTK_BOX (vbox1),ehbox, FALSE, TRUE, 2);
00621 
00622         gtk_widget_show (ehbox);
00623 
00624         ehbox=gtk_hbox_new(FALSE, 0);
00625 
00626         clabel5 =  gtk_label_new ("Command:");
00627         gtk_box_pack_start (GTK_BOX (ehbox),clabel5, FALSE, TRUE, 2);
00628         gtk_widget_show (clabel5);
00629 
00630         ckentrytext = gtk_entry_new ();
00631         gtk_box_pack_start (GTK_BOX (ehbox),ckentrytext, TRUE, TRUE, 2);
00632         gtk_widget_show (ckentrytext);
00633 
00634         gtk_box_pack_start (GTK_BOX (vbox1),ehbox, FALSE, TRUE, 2);
00635 
00636         gtk_widget_show (ehbox);
00637 
00638         ehbox=gtk_hbox_new(TRUE, 0);
00639 
00640 
00641         cb1 = gtk_button_new_with_label ("Unbind");
00642         gtk_box_pack_start (GTK_BOX (ehbox),cb1, FALSE, TRUE, 4);
00643         /*gtk_widget_set_usize (cb1, 45, 0);*/
00644         gtk_signal_connect_object (GTK_OBJECT (cb1), "clicked",
00645                                GTK_SIGNAL_FUNC(ckeyunbind),
00646                                NULL);
00647         gtk_widget_show (cb1);
00648 
00649         cb2 = gtk_button_new_with_label ("Bind");
00650         gtk_box_pack_start (GTK_BOX (ehbox),cb2, FALSE, TRUE, 4);
00651         gtk_signal_connect_object (GTK_OBJECT (cb2), "clicked",
00652                                GTK_SIGNAL_FUNC(bind_callback),
00653                                NULL);
00654         /*  gtk_widget_set_usize (cb2, 45, 0);*/
00655         gtk_widget_show (cb2);
00656 
00657         cb3 = gtk_button_new_with_label ("Clear");
00658         gtk_box_pack_start (GTK_BOX (ehbox),cb3, FALSE, TRUE, 4);
00659         /*    gtk_widget_set_usize (cb2, 45, 0);*/
00660         gtk_signal_connect_object (GTK_OBJECT (cb3), "clicked",
00661                                GTK_SIGNAL_FUNC(ckeyclear),
00662                                NULL);
00663         gtk_widget_show (cb3);
00664         gtk_box_pack_start (GTK_BOX (vbox1),ehbox, FALSE, TRUE, 2);
00665 
00666         gtk_widget_show (ehbox);
00667 
00668         gtk_widget_show (vbox1);
00669         gtk_widget_show (frame1);
00670         gtk_widget_show (vbox2);
00671 
00672         gtk_widget_show (notebook);
00673 
00674         /* And give some options to actually do something with our new nifty configuration */
00675 
00676         hbox1 = gtk_hbox_new(TRUE, 0);
00677         gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 6);
00678         savebutton = gtk_button_new_with_label("Save");
00679         gtk_signal_connect_object (GTK_OBJECT (savebutton), "clicked",
00680                                GTK_SIGNAL_FUNC(saveconfig),
00681                                NULL);
00682         gtk_box_pack_start(GTK_BOX(hbox1), savebutton, FALSE, TRUE, 4);
00683 
00684         applybutton = gtk_button_new_with_label("Apply");
00685         gtk_signal_connect_object (GTK_OBJECT (applybutton), "clicked",
00686                                GTK_SIGNAL_FUNC(applyconfig),
00687                                NULL);
00688         gtk_box_pack_start(GTK_BOX(hbox1), applybutton, FALSE, TRUE, 4);
00689 
00690         cancelbutton = gtk_button_new_with_label("Close");
00691         gtk_signal_connect_object (GTK_OBJECT (cancelbutton), "clicked",
00692                                GTK_SIGNAL_FUNC(gtk_widget_destroy),
00693                                GTK_OBJECT (gtkwin_config));
00694 
00695         gtk_box_pack_start(GTK_BOX(hbox1), cancelbutton, FALSE, TRUE, 4);
00696         gtk_widget_show(savebutton);
00697         gtk_widget_show(applybutton);
00698         gtk_widget_show(cancelbutton);
00699 
00700         gtk_widget_show (hbox1);
00701         gtk_widget_show (vbox);
00702         gtk_widget_show (gtkwin_config);
00703     }
00704     else {
00705         gdk_window_raise (gtkwin_config->window);
00706     }
00707 }
00708 
00709 
00710 void load_defaults(void)
00711 {
00712     char path[MAX_BUF],inbuf[MAX_BUF],*cp;
00713     FILE *fp;
00714     int i, val;
00715 
00716     /* Copy over the want values to use values now */
00717     for (i=0; i<CONFIG_NUMS; i++) {
00718         use_config[i] = want_config[i];
00719     }
00720 
00721     sprintf(path,"%s/.crossfire/gdefaults", getenv("HOME"));
00722     if ((fp=fopen(path,"r"))==NULL) return;
00723     while (fgets(inbuf, MAX_BUF-1, fp)) {
00724         inbuf[MAX_BUF-1]='\0';
00725         inbuf[strlen(inbuf)-1]='\0';    /* kill newline */
00726 
00727         if (inbuf[0]=='#') continue;
00728         /* IF no colon, then we certainly don't have a real value, so just skip */
00729         if (!(cp=strchr(inbuf,':'))) continue;
00730         *cp='\0';
00731         cp+=2;      /* colon, space, then value */
00732 
00733         val = -1;
00734         if (isdigit(*cp)) val=atoi(cp);
00735         else if (!strcmp(cp,"True")) val = TRUE;
00736         else if (!strcmp(cp,"False")) val = FALSE;
00737 
00738         for (i=1; i<CONFIG_NUMS; i++) {
00739             if (!strcmp(config_names[i], inbuf)) {
00740                 if (val == -1) {
00741                     LOG(LOG_WARNING,"gtk::load_defaults","Invalid value/line: %s: %s", inbuf, cp);
00742                 } else {
00743                     want_config[i] = val;
00744                 }
00745                 break;  /* Found a match - won't find another */
00746             }
00747         }
00748         /* We found a match in the loop above, so no need to do anything more */
00749         if (i < CONFIG_NUMS) continue;
00750 
00751         /* Legacy - now use the map_width and map_height values
00752          * Don't do sanity checking - that will be done below
00753          */
00754         if (!strcmp(inbuf,"mapsize")) {
00755             if (sscanf(cp,"%hdx%hd", &want_config[CONFIG_MAPWIDTH], &want_config[CONFIG_MAPHEIGHT])!=2) {
00756                 LOG(LOG_WARNING,"gtk::load_defaults","Malformed mapsize option in gdefaults.  Ignoring");
00757             }
00758         }
00759         else if (!strcmp(inbuf, "server")) {
00760             server = strdup_local(cp);  /* memory leak ! */
00761             continue;
00762         }
00763         else if (!strcmp(inbuf, "sound_server")) {
00764             sound_server = strdup_local(cp);    /* memory leak ! */
00765             continue;
00766         }
00767         else if (!strcmp(inbuf, "nopopups")) {
00768             /* Changed name from nopopups to popups, so inverse value */
00769             want_config[CONFIG_POPUPS] = !val;
00770             continue;
00771         }
00772         else if (!strcmp(inbuf, "nosplash")) {
00773             want_config[CONFIG_SPLASH] = !val;
00774             continue;
00775         }
00776         else if (!strcmp(inbuf, "splash")) {
00777             want_config[CONFIG_SPLASH] = val;
00778             continue;
00779         }
00780         else if (!strcmp(inbuf, "faceset")) {
00781             face_info.want_faceset = strdup_local(cp);  /* memory leak ! */
00782             continue;
00783         }
00784         /* legacy support for the old resistances values, we need to adjust the values to the new form */
00785         else if (!strcmp(inbuf, "resists")) {
00786             if (val) want_config[CONFIG_RESISTS] = val-1;
00787         }
00788         else if (!strcmp(inbuf, "sdl")) {
00789             if (val) want_config[CONFIG_DISPLAYMODE] = CFG_DM_SDL;
00790         }
00791 
00792 
00793         else LOG(LOG_WARNING,"gtk::load_defaults","Unknown line in gdefaults: %s %s", inbuf, cp);
00794     }
00795     fclose(fp);
00796     /* Make sure some of the values entered are sane - since a user can
00797      * edit the defaults file directly, they could put bogus values
00798      * in
00799      */
00800     if (want_config[CONFIG_ICONSCALE]< 25 || want_config[CONFIG_ICONSCALE]>200) {
00801         LOG(LOG_WARNING,"gtk::load_defaults","Ignoring iconscale value read for gdefaults file.\n"
00802             "Invalid iconscale range (%d), valid range for -iconscale is 25 through 200",
00803             want_config[CONFIG_ICONSCALE]);
00804         want_config[CONFIG_ICONSCALE] = use_config[CONFIG_ICONSCALE];
00805     }
00806     if (want_config[CONFIG_MAPSCALE]< 25 || want_config[CONFIG_MAPSCALE]>200) {
00807         LOG(LOG_WARNING,"gtk::load_defaults","ignoring mapscale value read for gdefaults file.\n"
00808                 "Invalid mapscale range (%d), valid range for -iconscale is 25 through 200",
00809             want_config[CONFIG_MAPSCALE]);
00810         want_config[CONFIG_MAPSCALE] = use_config[CONFIG_MAPSCALE];
00811     }
00812     if (!want_config[CONFIG_LIGHTING]) {
00813         LOG(LOG_WARNING,"gtk::load_defaults","No lighting mechanism selected - will not use darkness code");
00814         want_config[CONFIG_DARKNESS] = FALSE;
00815     }
00816 
00817     /* Make sure the map size os OK */
00818     if (want_config[CONFIG_MAPWIDTH] < 9 || want_config[CONFIG_MAPWIDTH] > MAP_MAX_SIZE) {
00819         LOG(LOG_WARNING,"gtk::load_defaults",
00820             "Invalid map width (%d) option in gdefaults. Valid range is 9 to %d",
00821             want_config[CONFIG_MAPWIDTH], MAP_MAX_SIZE);
00822         want_config[CONFIG_MAPWIDTH] = use_config[CONFIG_MAPWIDTH];
00823     }
00824     if (want_config[CONFIG_MAPHEIGHT] < 9 || want_config[CONFIG_MAPHEIGHT] > MAP_MAX_SIZE) {
00825         LOG(LOG_WARNING,"gtk::load_defaults",
00826             "Invalid map height (%d) option in gdefaults. Valid range is 9 to %d",
00827             want_config[CONFIG_MAPHEIGHT], MAP_MAX_SIZE);
00828         want_config[CONFIG_MAPHEIGHT] = use_config[CONFIG_MAPHEIGHT];
00829     }
00830 
00831 #ifndef HAVE_SDL
00832         /* If SDL is not built in, having SDL mode turned on causes many issues. */
00833     want_config[CONFIG_DISPLAYMODE] = CFG_DM_PIXMAP;
00834 #endif
00835 
00836     /* Now copy over the values just loaded */
00837     for (i=0; i<CONFIG_NUMS; i++) {
00838         use_config[i] = want_config[i];
00839     }
00840 
00841     image_size = DEFAULT_IMAGE_SIZE * use_config[CONFIG_ICONSCALE] / 100;
00842     map_image_size = DEFAULT_IMAGE_SIZE * use_config[CONFIG_MAPSCALE] / 100;
00843     map_image_half_size = DEFAULT_IMAGE_SIZE * use_config[CONFIG_MAPSCALE] / 200;
00844     itemlist_set_show_icon(&inv_list, use_config[CONFIG_SHOWICON]);
00845 
00846 }
00847 
00848 void save_defaults(void)
00849 {
00850     char path[MAX_BUF],buf[MAX_BUF];
00851     FILE *fp;
00852     int i;
00853 
00854     sprintf(path,"%s/.crossfire/gdefaults", getenv("HOME"));
00855     if (make_path_to_file(path)==-1) {
00856         LOG(LOG_ERROR,"gtk::save_defaults","Could not create %s", path);
00857         return;
00858     }
00859     if ((fp=fopen(path,"w"))==NULL) {
00860         LOG(LOG_ERROR,"gtk::save_defaults","Could not open %s", path);
00861         return;
00862     }
00863     fprintf(fp,"# This file is generated automatically by crossfire-client-gtk.\n");
00864     fprintf(fp,"# Manual editing is allowed, but the client may be finicky about\n");
00865     fprintf(fp,"# some of the matching it does.  All comparisons are case sensitive.\n");
00866     fprintf(fp,"# 'True' and 'False' are the proper cases for those two values\n");
00867     fprintf(fp,"# 'True' and 'False' have been replaced with 1 and 0 respectively\n");
00868     fprintf(fp,"server: %s\n", server);
00869     fprintf(fp,"sound_server: %s\n", sound_server);
00870     fprintf(fp,"faceset: %s\n", face_info.want_faceset);
00871 
00872     /* This isn't quite as good as before, as instead of saving things as 'True'
00873      * or 'False', it is just 1 or 0.  However, for the most part, the user isn't
00874      * going to be editing the file directly.
00875      */
00876     for (i=1; i < CONFIG_NUMS; i++) {
00877         fprintf(fp,"%s: %d\n", config_names[i], want_config[i]);
00878     }
00879 
00880     fclose(fp);
00881     sprintf(buf,"Defaults saved to %s",path);
00882     draw_info(buf,NDI_BLUE);
00883 }