Crossfire Client, Branch  R11627
config.c
Go to the documentation of this file.
00001 const char * const rcsid_gtk2_config_c =
00002     "$Id: config.c 11627 2009-04-04 16:55:25Z lalo $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2005,2007 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@metalforge.org
00023 */
00024 
00030 #ifdef HAVE_CONFIG_H
00031 #  include <config.h>
00032 #endif
00033 
00034 #include <gtk/gtk.h>
00035 #include <glade/glade.h>
00036 #include <ctype.h>
00037 
00038 #include "client.h"
00039 
00040 #include "main.h"
00041 #include "image.h"
00042 #include "gtk2proto.h"
00043 
00044 #include <dirent.h>
00045 
00046 #ifdef MINGW
00047 int alphasort(const struct dirent **a, const struct dirent **b)
00048 {
00049     return strcoll((*a)->d_name, (*b)->d_name);
00050 }
00051 
00052 int scandir(const char *dir, struct dirent ***namelist,
00053             int (*select)(const struct dirent *),
00054             int (*compar)(const struct dirent **, const struct dirent **)) {
00055     DIR *d;
00056     struct dirent *entry;
00057     register int i=0;
00058     size_t entrysize;
00059 
00060     if((d = opendir(dir)) == NULL)
00061         return -1;
00062 
00063     *namelist = NULL;
00064     while ((entry = readdir(d)) != NULL)
00065     {
00066         if (select == NULL || (select != NULL && (*select)(entry)))
00067         {
00068             *namelist = (struct dirent **)realloc((void *)(*namelist),
00069                                                   (size_t)((i + 1) * sizeof(struct dirent *)));
00070             if (*namelist == NULL) return -1;
00071             entrysize = sizeof(struct dirent) - sizeof(entry->d_name) + strlen(entry->d_name) + 1;
00072             (*namelist)[i] = (struct dirent *)malloc(entrysize);
00073             if ((*namelist)[i] == NULL) return -1;
00074             memcpy((*namelist)[i], entry, entrysize);
00075             i++;
00076         }
00077     }
00078     if (closedir(d)) return -1;
00079     if (i == 0) return -1;
00080     if (compar != NULL)
00081         qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *),
00082               (int(*)(const void*, const void*))compar);
00083     
00084     return i;
00085 }
00086 #endif
00087 
00088 GtkWidget *config_window, *config_spinbutton_cwindow, *config_button_echo,
00089     *config_button_fasttcp, *config_button_grad_color, *config_button_foodbeep,
00090     *config_button_sound, *config_button_cache, *config_button_download,
00091     *config_button_fog, *config_spinbutton_iconscale, *config_spinbutton_mapscale,
00092     *config_spinbutton_mapwidth, *config_spinbutton_mapheight,
00093     *config_button_smoothing, *config_combobox_displaymode,
00094     *config_combobox_faceset, *config_combobox_lighting,
00095     *config_combobox_theme, *config_combobox_glade;
00096 
00097 /* This is the string names that correspond to the numberic id's in client.h */
00098 
00099 static char *theme = "Standard";
00100 static char *themedir = "themes";
00101 static char *gladedir = "glade-gtk2";
00102 
00103 static const char * const display_modes[] = {"Pixmap", "SDL", "OpenGL"};
00104 
00116 void load_theme(int reload)
00117 {
00118     char path[MAX_BUF];
00119     int i;
00120     static char **default_files=NULL;
00121 
00122     /*
00123      * We should only be called with reload once at startup.  at that time,
00124      * store away the default rc files so we can restore them later.
00125      */
00126     if (!reload) {
00127         char **tmp;
00128 
00129         /*
00130          * GTK man page says copy of this data should be made, so lets go and
00131          * do that.
00132          */
00133         tmp = gtk_rc_get_default_files();
00134         i=0;
00135         while (tmp[i]) {
00136             i++;
00137         }
00138         default_files = malloc(sizeof(char*) * (i+1));
00139         i=0;
00140         while (tmp[i]) {
00141             default_files[i] = strdup(tmp[i]);
00142             i++;
00143         }
00144         default_files[i] = NULL;
00145     }
00146 
00147     if (!strcmp(theme,"None")) {
00148         /*
00149          * If this is default and initial run, nothing to do.  If it not
00150          * initial run, we need to reset the search path.
00151          */
00152         if (reload) {
00153             gtk_rc_set_default_files(default_files);
00154         }
00155     } else {
00156         snprintf(path, MAX_BUF, "%s/%s/%s", CF_DATADIR, themedir, theme);
00157         /*
00158          * Check for existence of file.  Unfortunately, at initial run time,
00159          * the window may not be realized, so we can't print this error to the
00160          * user directly.
00161          */
00162         if (access(path, R_OK) == -1) {
00163             LOG(LOG_ERROR, "config.c::load_theme",
00164                 "Unable to find theme file %s", path);
00165             return;
00166         }
00167         /*
00168          * We need to reset the search path, otherwise the effects are
00169          * additive.  In practice, we could manipulate pointer so it is just
00170          * one call to gtk_rc_set_default_files(), but that is probably more
00171          * complicated than just doing these two calls here.
00172          */
00173         gtk_rc_set_default_files(default_files);
00174         gtk_rc_add_default_file(path);
00175     }
00176 
00177     if (reload) {
00178         /*
00179          * Reload data - so we need to force GTK to reparse and rebind all the
00180          * widget data.  Then, we need to call our own functions to reparse the
00181          * custom widget handling we do.
00182          */
00183         gtk_rc_reparse_all_for_settings(gtk_settings_get_for_screen(gdk_screen_get_default()), TRUE);
00184         gtk_rc_reset_styles(gtk_settings_get_for_screen(gdk_screen_get_default()));
00185         info_get_styles();
00186         inventory_get_styles();
00187         stats_get_styles();
00188         spell_get_styles();
00189         update_spell_information();
00190         /*
00191          * Set the inv_updated to force a redraw - otherwise it will not
00192          * necessarily bind the lists with the new widgets.
00193         */
00194         cpl.ob->inv_updated = 1;
00195         cpl.below->inv_updated = 1;
00196         draw_lists();
00197 
00198         draw_stats(TRUE);
00199         draw_message_window(TRUE);
00200     }
00201 }
00202 
00207 void load_defaults(void)
00208 {
00209     char path[MAX_BUF],inbuf[MAX_BUF],*cp;
00210     FILE *fp;
00211     int i, val;
00212 
00213     /* Copy over the want values to use values now */
00214     for (i=0; i<CONFIG_NUMS; i++) {
00215         use_config[i] = want_config[i];
00216     }
00217 
00218     snprintf(path, sizeof(path), "%s/.crossfire/gdefaults2", getenv("HOME"));
00219     if ((fp=fopen(path,"r"))==NULL) return;
00220     while (fgets(inbuf, MAX_BUF-1, fp)) {
00221         inbuf[MAX_BUF-1]='\0';
00222         inbuf[strlen(inbuf)-1]='\0';    /* kill newline */
00223 
00224         if (inbuf[0]=='#') continue;
00225         /* Skip any setting line that does not contain a colon character */
00226         if (!(cp=strchr(inbuf,':'))) continue;
00227         *cp='\0';
00228         cp+=2;      /* colon, space, then value */
00229 
00230         val = -1;
00231         if (isdigit(*cp)) val=atoi(cp);
00232         else if (!strcmp(cp,"True")) val = TRUE;
00233         else if (!strcmp(cp,"False")) val = FALSE;
00234 
00235         for (i=1; i<CONFIG_NUMS; i++) {
00236             if (!strcmp(config_names[i], inbuf)) {
00237                 if (val == -1) {
00238                     LOG(LOG_WARNING, "config.c::load_defaults",
00239                         "Invalid value/line: %s: %s", inbuf, cp);
00240                 } else {
00241                     want_config[i] = val;
00242                 }
00243                 break;  /* Found a match - won't find another */
00244             }
00245         }
00246         /* We found a match in the loop above, so do not do anything more */
00247         if (i < CONFIG_NUMS) continue;
00248 
00249         /*
00250          * Legacy - now use the map_width and map_height values Don't do sanity
00251          * checking - that will be done below
00252          */
00253         if (!strcmp(inbuf,"mapsize")) {
00254             if (sscanf(cp,"%hdx%hd", &want_config[CONFIG_MAPWIDTH], &want_config[CONFIG_MAPHEIGHT])!=2) {
00255                 LOG(LOG_WARNING, "config.c::load_defaults",
00256                     "Malformed mapsize option in gdefaults2.  Ignoring");
00257             }
00258         }
00259         else if (!strcmp(inbuf, "server")) {
00260             server = strdup_local(cp);  /* memory leak ! */
00261             continue;
00262         }
00263         else if (!strcmp(inbuf, "theme")) {
00264             theme = strdup_local(cp);   /* memory leak ! */
00265             continue;
00266         }
00267         else if (!strcmp(inbuf, "window_layout")) {
00268             strncpy(window_xml_file, cp, MAX_BUF-1);
00269             continue;
00270         }
00271         else if (!strcmp(inbuf, "nopopups")) {
00272             /* Changed name from nopopups to popups, so inverse value */
00273             want_config[CONFIG_POPUPS] = !val;
00274             continue;
00275         }
00276         else if (!strcmp(inbuf, "nosplash")) {
00277             want_config[CONFIG_SPLASH] = !val;
00278             continue;
00279         }
00280         else if (!strcmp(inbuf, "splash")) {
00281             want_config[CONFIG_SPLASH] = val;
00282             continue;
00283         }
00284         else if (!strcmp(inbuf, "faceset")) {
00285             face_info.want_faceset = strdup_local(cp);  /* memory leak ! */
00286             continue;
00287         }
00288         /* legacy, as this is now just saved as 'lighting' */
00289         else if (!strcmp(inbuf, "per_tile_lighting")) {
00290             if (val) want_config[CONFIG_LIGHTING] = CFG_LT_TILE;
00291         }
00292         else if (!strcmp(inbuf, "per_pixel_lighting")) {
00293             if (val) want_config[CONFIG_LIGHTING] = CFG_LT_PIXEL;
00294         }
00295         else if (!strcmp(inbuf, "resists")) {
00296             if (val) want_config[CONFIG_RESISTS] = val;
00297         }
00298         else if (!strcmp(inbuf, "sdl")) {
00299             if (val) want_config[CONFIG_DISPLAYMODE] = CFG_DM_SDL;
00300         }
00301         else LOG(LOG_WARNING, "config.c::load_defaults",
00302                  "Unknown line in gdefaults2: %s %s", inbuf, cp);
00303     }
00304     fclose(fp);
00305     /*
00306      * Make sure some of the values entered are sane - since a user can edit
00307      * the defaults file directly, they could put bogus values in
00308      */
00309     if (want_config[CONFIG_ICONSCALE]< 25 || want_config[CONFIG_ICONSCALE]>200) {
00310         LOG(LOG_WARNING, "config.c::load_defaults",
00311             "Ignoring iconscale value read from gdefaults2 file.\n"
00312             "Invalid iconscale range (%d), valid range for -iconscale "
00313             "is 25 through 200", want_config[CONFIG_ICONSCALE]);
00314         want_config[CONFIG_ICONSCALE] = use_config[CONFIG_ICONSCALE];
00315     }
00316     if (want_config[CONFIG_MAPSCALE]< 25 || want_config[CONFIG_MAPSCALE]>200) {
00317         LOG(LOG_WARNING, "config.c::load_defaults",
00318             "ignoring mapscale value read for gdefaults2 file.\n"
00319             "Invalid mapscale range (%d), valid range for -iconscale "
00320             "is 25 through 200", want_config[CONFIG_MAPSCALE]);
00321         want_config[CONFIG_MAPSCALE] = use_config[CONFIG_MAPSCALE];
00322     }
00323     if (!want_config[CONFIG_LIGHTING]) {
00324         LOG(LOG_WARNING, "config.c::load_defaults",
00325             "No lighting mechanism selected - will not use darkness code");
00326         want_config[CONFIG_DARKNESS] = FALSE;
00327     }
00328     if (want_config[CONFIG_RESISTS] > 2) {
00329         LOG(LOG_WARNING, "config.c::load_defaults",
00330             "ignoring resists display value read for gdafaults file.\n"
00331             "Invalid value (%d), must be one value of 0, 1 or 2.",
00332             want_config[CONFIG_RESISTS]);
00333         want_config[CONFIG_RESISTS] = 0;
00334     }
00335 
00336     /* Make sure the map size os OK */
00337     if (want_config[CONFIG_MAPWIDTH] < 9 || want_config[CONFIG_MAPWIDTH] > MAP_MAX_SIZE) {
00338         LOG(LOG_WARNING, "config.c::load_defaults", "Invalid map width (%d) "
00339             "option in gdefaults2. Valid range is 9 to %d",
00340             want_config[CONFIG_MAPWIDTH], MAP_MAX_SIZE);
00341         want_config[CONFIG_MAPWIDTH] = use_config[CONFIG_MAPWIDTH];
00342     }
00343     if (want_config[CONFIG_MAPHEIGHT] < 9 || want_config[CONFIG_MAPHEIGHT] > MAP_MAX_SIZE) {
00344         LOG(LOG_WARNING, "config.c::load_defaults", "Invalid map height (%d) "
00345             "option in gdefaults2. Valid range is 9 to %d",
00346             want_config[CONFIG_MAPHEIGHT], MAP_MAX_SIZE);
00347         want_config[CONFIG_MAPHEIGHT] = use_config[CONFIG_MAPHEIGHT];
00348     }
00349 
00350     /* Now copy over the values just loaded */
00351     for (i=0; i<CONFIG_NUMS; i++) {
00352         use_config[i] = want_config[i];
00353     }
00354 
00355     image_size = DEFAULT_IMAGE_SIZE * use_config[CONFIG_ICONSCALE] / 100;
00356     map_image_size = DEFAULT_IMAGE_SIZE * use_config[CONFIG_MAPSCALE] / 100;
00357     map_image_half_size = DEFAULT_IMAGE_SIZE * use_config[CONFIG_MAPSCALE] / 200;
00358     /*inv_list.show_icon = use_config[CONFIG_SHOWICON];*/
00359 }
00360 
00365 void save_defaults(void)
00366 {
00367     char path[MAX_BUF],buf[MAX_BUF];
00368     FILE *fp;
00369     int i;
00370 
00371     snprintf(path, sizeof(path), "%s/.crossfire/gdefaults2", getenv("HOME"));
00372     if (make_path_to_file(path)==-1) {
00373         LOG(LOG_ERROR, "config.c::save_defaults","Could not create %s", path);
00374         return;
00375     }
00376     if ((fp=fopen(path,"w"))==NULL) {
00377         LOG(LOG_ERROR, "config.c::save_defaults", "Could not open %s", path);
00378         return;
00379     }
00380     fprintf(fp,"# crossfire-client-gtk2 automatically generates this file.\n");
00381     fprintf(fp,"# Manual editing is allowed, but the client may be a bit\n");
00382     fprintf(fp,"# finicky about the keys and values.  Comparisons are case\n");
00383     fprintf(fp,"# sensitive.  'True' and 'False' are the proper case, but\n");
00384     fprintf(fp,"# have been replaced with 1 and 0 respectively.\n#\n");
00385     fprintf(fp,"server: %s\n", server);
00386     fprintf(fp,"theme: %s\n", theme);
00387     fprintf(fp,"faceset: %s\n", face_info.want_faceset);
00388     fprintf(fp,"window_layout: %s\n", window_xml_file);
00389     /*
00390      * This isn't quite as good as before, as instead of saving things as
00391      * 'True' or 'False', it is just 1 or 0.  However, for the most part, the
00392      * user isn't going to be editing the file directly.
00393      */
00394     for (i=1; i < CONFIG_NUMS; i++) {
00395         fprintf(fp,"%s: %d\n", config_names[i], want_config[i]);
00396     }
00397 
00398     fclose(fp);
00399     snprintf(buf, sizeof(buf), "Defaults saved to %s",path);
00400     draw_info(buf,NDI_BLUE);
00401 }
00402 
00407 void config_init(GtkWidget *window_root)
00408 {
00409     static int has_init=0;
00410     GladeXML *xml_tree;
00411     GtkWidget *widget;
00412     int count, i;
00413 
00414     has_init=1;
00415 
00416     config_window = glade_xml_get_widget(dialog_xml, "config_window");
00417     xml_tree = glade_get_widget_tree(GTK_WIDGET(config_window));
00418 
00419     config_spinbutton_cwindow =
00420         glade_xml_get_widget(xml_tree, "config_spinbutton_cwindow");
00421     config_button_echo =
00422         glade_xml_get_widget(xml_tree, "config_button_echo");
00423     config_button_fasttcp =
00424         glade_xml_get_widget(xml_tree, "config_button_fasttcp");
00425     config_button_grad_color =
00426         glade_xml_get_widget(xml_tree, "config_button_grad_color");
00427     config_button_foodbeep =
00428         glade_xml_get_widget(xml_tree, "config_button_foodbeep");
00429     config_button_sound =
00430         glade_xml_get_widget(xml_tree, "config_button_sound");
00431     config_button_cache =
00432         glade_xml_get_widget(xml_tree, "config_button_cache");
00433     config_button_download =
00434         glade_xml_get_widget(xml_tree, "config_button_download");
00435     config_button_fog =
00436         glade_xml_get_widget(xml_tree, "config_button_fog");
00437     config_button_smoothing =
00438         glade_xml_get_widget(xml_tree, "config_button_smoothing");
00439     config_spinbutton_iconscale =
00440         glade_xml_get_widget(xml_tree, "config_spinbutton_iconscale");
00441     config_spinbutton_mapscale =
00442         glade_xml_get_widget(xml_tree, "config_spinbutton_mapscale");
00443     config_spinbutton_mapwidth =
00444         glade_xml_get_widget(xml_tree, "config_spinbutton_mapwidth");
00445     config_spinbutton_mapheight =
00446         glade_xml_get_widget(xml_tree, "config_spinbutton_mapheight");
00447     config_combobox_displaymode =
00448         glade_xml_get_widget(xml_tree, "config_combobox_displaymode");
00449     config_combobox_faceset =
00450         glade_xml_get_widget(xml_tree, "config_combobox_faceset");
00451     config_combobox_lighting =
00452         glade_xml_get_widget(xml_tree, "config_combobox_lighting");
00453     config_combobox_theme =
00454         glade_xml_get_widget(xml_tree, "config_combobox_theme");
00455     config_combobox_glade =
00456         glade_xml_get_widget(xml_tree, "config_combobox_glade");
00457 
00458     widget = glade_xml_get_widget(xml_tree, "config_button_save");
00459     g_signal_connect ((gpointer) widget, "clicked",
00460         G_CALLBACK (on_config_button_save_clicked), NULL);
00461 
00462     widget = glade_xml_get_widget(xml_tree, "config_button_apply");
00463     g_signal_connect ((gpointer) widget, "clicked",
00464         G_CALLBACK (on_config_button_apply_clicked), NULL);
00465 
00466     widget = glade_xml_get_widget(xml_tree, "config_button_close");
00467     g_signal_connect ((gpointer) widget, "clicked",
00468         G_CALLBACK (on_config_button_close_clicked), NULL);
00469 
00470     /*
00471      * Display mode combo box setup.  First, remove all entries, then populate
00472      * with what options are available based on what was compiled in.
00473      */
00474     count =  gtk_tree_model_iter_n_children(
00475                     gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_displaymode)), NULL);
00476     for (i=0; i < count; i++)
00477         gtk_combo_box_remove_text(GTK_COMBO_BOX(config_combobox_displaymode), 0);
00478 
00479 #ifdef HAVE_OPENGL
00480         gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_displaymode),  "OpenGL");
00481 #endif
00482 
00483 #ifdef HAVE_SDL
00484         gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_displaymode),  "SDL");
00485 #endif
00486         gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_displaymode),  "Pixmap");
00487 
00488 }
00489 
00500 static int scandir_theme_filter(const struct dirent *d)
00501 {
00502     if (d->d_name[0] == '.') return 0;
00503     return 1;
00504 }
00505 
00517 static int scandir_glade_filter(const struct dirent *d)
00518 {
00519     char *token = NULL;
00520     char *extok = NULL;
00521     char delim[] = ".";
00522     char exten[] = "glade";
00523     char parse[MAX_BUF] = "";
00524 
00525     strncpy(parse, d->d_name, MAX_BUF);
00526     token = strtok(parse, delim);
00527     while (token) {
00528         extok = token;
00529         token = strtok(NULL, delim);
00530     }
00531     if (extok && strncmp(exten, extok, strlen(exten)) == 0) {
00532         if (strncmp(parse, DIALOG_XML_FILENAME, strlen(parse)) == 0)
00533             return 0;
00534         return 1;
00535     }
00536     return 0;
00537 }
00538 
00565 static void fill_combobox_from_datadir(GtkWidget *combobox, char *active,
00566     uint64 want_none, char *subdir, int (*scandir_filter) ())
00567 {
00568     int             count, i;
00569     GtkTreeModel    *model;
00570     gchar           *buf;
00571     GtkTreeIter     iter;
00572 
00573     model = gtk_combo_box_get_model(GTK_COMBO_BOX(combobox));
00574     count =  gtk_tree_model_iter_n_children(model, NULL);
00575     /*
00576      * If count is 0, the combo box control has not been initialized yet, so
00577      * fill it with the appropriate selections now.
00578      */
00579     if (count == 0) {
00580         char path[MAX_BUF];
00581         struct dirent **files;
00582         int done_none=0;
00583 
00584         snprintf(path, MAX_BUF, "%s/%s", CF_DATADIR, subdir);
00585 
00586         count = scandir(path, &files, *scandir_filter, alphasort);
00587         LOG(LOG_DEBUG, "config.c::fill_combobox_from_datadir",
00588             "found %d files in %s\n", count, path);
00589 
00590         for (i=0; i<count; i++) {
00591             /*
00592              * If a 'None' entry is desired, and if an entry that falls after
00593              * 'None' is found, and, if 'None' has not already been added,
00594              * insert it now.
00595              */
00596             if (!done_none && want_none &&
00597                 strcmp(files[i]->d_name, "None") > 0) {
00598                     gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "None");
00599                     done_none=1;
00600             }
00601 
00602             gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), files[i]->d_name);
00603         }
00604         /* Set this for below */
00605         count =  gtk_tree_model_iter_n_children(model, NULL);
00606     }
00607     /*
00608      * The block belows causes the matching combobox item to be selected.  Set
00609      * it irregardless of whether this is the first time this is run or not.
00610      */
00611     for (i=0; i < count; i++) {
00612         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
00613             LOG(LOG_ERROR, "config.c::fill_combobox_from_datadir",
00614                 "Unable to get combo box iter\n");
00615             break;
00616         }
00617         gtk_tree_model_get(model, &iter, 0, &buf, -1);
00618         if (!strcasecmp(active, buf)) {
00619             gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), i);
00620             g_free(buf);
00621             break;
00622         }
00623         g_free(buf);
00624     }
00625 }
00626 
00627 /*
00628  * Setup_config_window sets the buttons, combos, etc, to the state that matches
00629  * the want_config[] values.
00630  */
00631 static void setup_config_window(void)
00632 {
00633     int count, i;
00634     GtkTreeModel    *model;
00635     gchar   *buf;
00636     GtkTreeIter iter;
00637 
00638     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_echo),
00639                                  want_config[CONFIG_ECHO]);
00640 
00641     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_fasttcp),
00642                                  want_config[CONFIG_FASTTCP]);
00643 
00644     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_grad_color),
00645                                  want_config[CONFIG_GRAD_COLOR]);
00646 
00647     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_foodbeep),
00648                                  want_config[CONFIG_FOODBEEP]);
00649 
00650     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_sound),
00651                                  want_config[CONFIG_SOUND]);
00652 
00653     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_cache),
00654                                  want_config[CONFIG_CACHE]);
00655 
00656     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_download),
00657                                  want_config[CONFIG_DOWNLOAD]);
00658 
00659     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_fog),
00660                                  want_config[CONFIG_FOGWAR]);
00661 
00662     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_smoothing),
00663                                  want_config[CONFIG_SMOOTH]);
00664 
00665     gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_cwindow),
00666                               (float)want_config[CONFIG_CWINDOW]);
00667 
00668     gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_iconscale),
00669                               (float)want_config[CONFIG_ICONSCALE]);
00670 
00671     gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_mapscale),
00672                               (float)want_config[CONFIG_MAPSCALE]);
00673 
00674     gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_mapwidth),
00675                               (float)want_config[CONFIG_MAPWIDTH]);
00676 
00677     gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_mapheight),
00678                               (float)want_config[CONFIG_MAPHEIGHT]);
00679     /*
00680      * Face set combo box setup.
00681      * Remove all the entries currently in the combo box
00682      */
00683     model = gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_faceset));
00684     count =  gtk_tree_model_iter_n_children(model, NULL);
00685 
00686     for (i=0; i < count; i++)
00687         gtk_combo_box_remove_text(GTK_COMBO_BOX(config_combobox_faceset), 0);
00688 
00689     /* If we have real faceset info from the server, use it */
00690     if (face_info.have_faceset_info) {
00691         for (i=0; i<MAX_FACE_SETS; i++)
00692             if (face_info.facesets[i].fullname)
00693                 gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_faceset),
00694                           face_info.facesets[i].fullname);
00695     } else {
00696         gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_faceset),  "Standard");
00697         gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_faceset),  "Classic");
00698     }
00699     count =  gtk_tree_model_iter_n_children(model, NULL);
00700 
00701     for (i=0; i < count; i++) {
00702         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
00703             LOG(LOG_ERROR, "config.c::setup_config_window",
00704                 "Unable to get faceset iter\n");
00705             break;
00706         }
00707         gtk_tree_model_get(model, &iter, 0, &buf, -1);
00708 
00709         if (face_info.want_faceset && !strcasecmp(face_info.want_faceset, buf)) {
00710             gtk_combo_box_set_active(GTK_COMBO_BOX(config_combobox_faceset), i);
00711             g_free(buf);
00712             break;
00713         }
00714         g_free(buf);
00715     }
00716 
00717     if (sizeof(display_modes) < want_config[CONFIG_DISPLAYMODE]) {
00718         LOG(LOG_ERROR, "config.c::setup_config_window",
00719             "Player display mode not in display_modes range\n");
00720     } else {
00721         /*
00722          * We want to set up the boxes to match current settings for things
00723          * like displaymode.
00724          */
00725         model = gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_displaymode));
00726         count =  gtk_tree_model_iter_n_children(model, NULL);
00727         for (i=0; i < count; i++) {
00728             if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
00729                 LOG(LOG_ERROR, "config.c::setup_config_window",
00730                     "Unable to get faceset iter\n");
00731                 break;
00732             }
00733             gtk_tree_model_get(model, &iter, 0, &buf, -1);
00734             if (!strcasecmp(display_modes[want_config[CONFIG_DISPLAYMODE]], buf)) {
00735                 gtk_combo_box_set_active(GTK_COMBO_BOX(config_combobox_displaymode), i);
00736                 g_free(buf);
00737                 break;
00738             }
00739             g_free(buf);
00740         }
00741     }
00742 
00743     /*
00744      * We want to set up the boxes to match current settings for things like
00745      * lighting.  A bit of a hack to hardcode the strings below.
00746      */
00747     model = gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_lighting));
00748     count =  gtk_tree_model_iter_n_children(model, NULL);
00749     for (i=0; i < count; i++) {
00750         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
00751             LOG(LOG_ERROR, "config.c::setup_config_window",
00752                 "Unable to get lighting iter\n");
00753             break;
00754         }
00755         gtk_tree_model_get(model, &iter, 0, &buf, -1);
00756         if ((want_config[CONFIG_LIGHTING] == CFG_LT_TILE && !strcasecmp(buf, "Per Tile")) ||
00757             (want_config[CONFIG_LIGHTING] == CFG_LT_PIXEL && !strcasecmp(buf, "Fast Per Pixel")) ||
00758             (want_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST && !strcasecmp(buf, "Best Per Pixel")) ||
00759             (want_config[CONFIG_LIGHTING] == CFG_LT_NONE && !strcasecmp(buf, "None"))) {
00760                 gtk_combo_box_set_active(GTK_COMBO_BOX(config_combobox_lighting), i);
00761                 g_free(buf);
00762                 break;
00763         }
00764         g_free(buf);
00765     }
00766     /*
00767      * Use the filenames of files found in the themes subdirectory of the
00768      * crossfire-client data directory as the selectable items in a combo box
00769      * selector control.  Specify the current default, and that a "None" entry
00770      * needs to be added also.
00771      */
00772     fill_combobox_from_datadir(config_combobox_theme, theme, 1, themedir,
00773         &scandir_theme_filter);
00774     /*
00775      * Use the filenames of files found in the glade-gtk subdirectory of the
00776      * crossfire-client data directory as the selectable items in a combo box
00777      * selector control.  Specify the current default, and that no "None" entry
00778      * is desired.
00779      */
00780     fill_combobox_from_datadir(config_combobox_glade, window_xml_file, 0,
00781         gladedir, &scandir_glade_filter);
00782 }
00783 
00784 #define IS_DIFFERENT(TYPE) (want_config[TYPE] != use_config[TYPE])
00785 
00791 static void read_config_window(void)
00792 {
00793     gchar   *buf;
00794 
00795     want_config[CONFIG_ECHO] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_echo));
00796     want_config[CONFIG_FASTTCP] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_fasttcp));
00797     want_config[CONFIG_GRAD_COLOR] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_grad_color));
00798     want_config[CONFIG_FOODBEEP] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_foodbeep));
00799     want_config[CONFIG_SOUND] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_sound));
00800     want_config[CONFIG_CACHE] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_cache));
00801     want_config[CONFIG_DOWNLOAD] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_download));
00802     want_config[CONFIG_FOGWAR] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_fog));
00803     want_config[CONFIG_SMOOTH] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_smoothing));
00804     want_config[CONFIG_CWINDOW] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_cwindow));
00805     want_config[CONFIG_ICONSCALE] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_iconscale));
00806     want_config[CONFIG_MAPSCALE] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_mapscale));
00807     want_config[CONFIG_MAPWIDTH] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_mapwidth));
00808     want_config[CONFIG_MAPHEIGHT] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_mapheight));
00809 
00810     buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_faceset));
00811 
00812     /*
00813      * We may be able to eliminate the extra strdup/free, but I'm not 100% sure
00814      * that we are guaranteed that glib won't implement them through its
00815      * own/different malloc library.
00816      */
00817     if (buf) {
00818         free(face_info.want_faceset);
00819         face_info.want_faceset = strdup_local(buf);
00820         g_free(buf);
00821     }
00822 
00823     buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_displaymode));
00824     if (buf) {
00825         if (!strcasecmp(buf,"OpenGL")) want_config[CONFIG_DISPLAYMODE] = CFG_DM_OPENGL;
00826         else if (!strcasecmp(buf,"SDL")) want_config[CONFIG_DISPLAYMODE] = CFG_DM_SDL;
00827         else if (!strcasecmp(buf,"Pixmap")) want_config[CONFIG_DISPLAYMODE] = CFG_DM_PIXMAP;
00828         g_free(buf);
00829     }
00830 
00831     buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_lighting));
00832     if (buf) {
00833         if (!strcasecmp(buf, "Per Tile")) want_config[CONFIG_LIGHTING] = CFG_LT_TILE;
00834         else if (!strcasecmp(buf, "Fast Per Pixel")) want_config[CONFIG_LIGHTING] = CFG_LT_PIXEL;
00835         else if (!strcasecmp(buf, "Best Per Pixel")) want_config[CONFIG_LIGHTING] = CFG_LT_PIXEL_BEST;
00836         else if (!strcasecmp(buf, "None")) want_config[CONFIG_LIGHTING] = CFG_LT_NONE;
00837         if  (want_config[CONFIG_LIGHTING] != CFG_LT_NONE) {
00838             want_config[CONFIG_DARKNESS] = 1;
00839             use_config[CONFIG_DARKNESS] = 1;
00840         }
00841 
00842         g_free(buf);
00843     }
00844     /*
00845      * Load up the theme.  A problem is we don't really know if theme has been
00846      * allocated or is just pointing as a static string.  So we lose a few
00847      * bytes and just don't free the data.
00848      */
00849     buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_theme));
00850     if (buf) {
00851         if (strcmp(buf, theme)) {
00852             theme = buf;
00853             load_theme(TRUE);
00854         } else {
00855             g_free(buf);
00856         }
00857     } else {
00858         theme = "None";
00859     }
00860     /*
00861      * Load up the layout and set the combobox control to point to the loaded
00862      * default value.
00863      */
00864     buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_glade));
00865     if (buf && strcmp(buf, window_xml_file)) {
00866         strncpy(window_xml_file, buf, MAX_BUF);
00867     }
00868     /*
00869      * Some values can take effect right now, others not.  Code below handles
00870      * these cases - largely grabbed from gtk/config.c
00871      */
00872     if (IS_DIFFERENT(CONFIG_SOUND)) {
00873         int tmp;
00874         if (want_config[CONFIG_SOUND]) {
00875             tmp = init_sounds();
00876             if (csocket.fd)
00877                 cs_print_string(csocket.fd, "setup sound %d", tmp >= 0);
00878         } else {
00879             if (csocket.fd)
00880                 cs_print_string(csocket.fd, "setup sound 0");
00881         }
00882         use_config[CONFIG_SOUND] = want_config[CONFIG_SOUND];
00883     }
00884     if (IS_DIFFERENT(CONFIG_FASTTCP)) {
00885 #ifdef TCP_NODELAY
00886 #ifndef WIN32
00887         int q = want_config[CONFIG_FASTTCP];
00888 
00889         if (csocket.fd && setsockopt(csocket.fd, SOL_TCP, TCP_NODELAY, &q, sizeof(q)) == -1)
00890             perror("TCP_NODELAY");
00891 #else
00892         int q = want_config[CONFIG_FASTTCP];
00893 
00894         if (csocket.fd && setsockopt(csocket.fd, SOL_TCP, TCP_NODELAY, ( const char* )&q, sizeof(q)) == -1)
00895             perror("TCP_NODELAY");
00896 #endif
00897 #endif
00898         use_config[CONFIG_FASTTCP] = want_config[CONFIG_FASTTCP];
00899     }
00900 
00901     if (IS_DIFFERENT(CONFIG_LIGHTING)) {
00902 #ifdef HAVE_SDL
00903         if (use_config[CONFIG_DISPLAYMODE]==CFG_DM_SDL)
00904             /* This is done to make the 'lightmap' in the proper format */
00905             init_SDL( NULL, 1);
00906 #endif
00907     }
00908     /*
00909      * Nothing to do, but we can switch immediately without problems.  do force
00910      * a redraw
00911      */
00912     if (IS_DIFFERENT(CONFIG_GRAD_COLOR)) {
00913         use_config[CONFIG_GRAD_COLOR] = want_config[CONFIG_GRAD_COLOR];
00914         draw_stats(TRUE);
00915     }
00916 }
00917 
00925 void
00926 on_config_button_save_clicked          (GtkButton       *button,
00927                                         gpointer         user_data)
00928 {
00929     read_config_window();
00930     save_defaults();
00931 }
00932 
00940 void
00941 on_config_button_apply_clicked         (GtkButton       *button,
00942                                         gpointer         user_data)
00943 {
00944     read_config_window();
00945 }
00946 
00954 void
00955 on_config_button_close_clicked         (GtkButton       *button,
00956                                         gpointer         user_data)
00957 {
00958     gtk_widget_hide(config_window);
00959 }
00960 
00967 void
00968 on_configure_activate                 (GtkMenuItem     *menuitem,
00969                                         gpointer         user_data)
00970 {
00971     gtk_widget_show(config_window);
00972     setup_config_window();
00973 }
00974 
00981 void save_winpos(void)
00982 {
00983     char savename[MAX_BUF];
00984     char buf[MAX_BUF];
00985     char *cp;
00986     FILE *save;
00987     int  x,y,w,h,wx,wy;
00988     extern  GtkWidget *window_root;
00989     GList *pane_list, *list_loop;
00990     GladeXML *xml_tree;
00991     GtkWidget *widget;
00992 
00993     /*
00994      * Truncate window_xml_file to remove a .extension if one exists, so that
00995      * the window positions file can be created with a different .extension.
00996      * this helps keep the length of the file name more reasonable.
00997      */
00998     strncpy(buf, window_xml_file, MAX_BUF);
00999     cp = strrchr(buf, '.');
01000     if (cp)
01001       cp[0] = 0;
01002 
01003     snprintf(savename, sizeof(savename), "%s/.crossfire/%s.pos", getenv("HOME"), buf);
01004     if (!(save = fopen(savename, "w"))) {
01005         snprintf(buf, sizeof(buf), "Cannot open %s - window positions not saved!", savename);
01006         draw_info(buf, NDI_RED);
01007         return;
01008     }
01009     get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h);
01010     fprintf(save,"window_root: +%d+%dx%dx%d\n", wx, wy, w, h);
01011 
01012     xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root));
01013     /*
01014      * Iterate through all widgets whose names begin with hpaned_* and vpaned_*
01015      * to save the widget name and the position of the pane divider.  Widgets
01016      * are dynamically found and processed so long as the .glade file designer
01017      * adheres to the naming conventions that Glade Designer uses.
01018      */
01019     pane_list = glade_xml_get_widget_prefix(xml_tree, "hpaned_");
01020     for (list_loop = pane_list; list_loop; list_loop = g_list_next(list_loop)) {
01021         widget = list_loop->data;
01022         fprintf(save, "%s: %d\n", glade_get_widget_name(widget),
01023             gtk_paned_get_position(GTK_PANED(widget)));
01024     }
01025     g_list_free(pane_list);
01026 
01027     pane_list = glade_xml_get_widget_prefix(xml_tree, "vpaned_");
01028     for (list_loop = pane_list; list_loop; list_loop = g_list_next(list_loop)) {
01029         widget = list_loop->data;
01030         fprintf(save, "%s: %d\n", glade_get_widget_name(widget),
01031             gtk_paned_get_position(GTK_PANED(widget)));
01032     }
01033     g_list_free(pane_list);
01034 
01035     fclose(save);
01036     snprintf(buf, sizeof(buf), "Window positions saved to %s", savename);
01037     draw_info(buf, NDI_BLUE);
01038 }
01039 
01047 void
01048 on_save_window_position_activate       (GtkMenuItem     *menuitem,
01049                                         gpointer         user_data)
01050 {
01051     save_winpos();
01052     /*
01053      * The following prevents multiple saves per menu activation.
01054      */
01055     g_signal_stop_emission_by_name(GTK_OBJECT(menuitem), "activate");
01056 }
01057 
01064 void load_window_positions(GtkWidget *window_root)
01065 {
01066     char loadname[MAX_BUF];
01067     char buf[MAX_BUF];
01068     char *cp;
01069     GladeXML *xml_tree;
01070     GtkWidget *widget;
01071     FILE    *load;
01072 
01073     /*
01074      * Truncate window_xml_file to remove a .extension if one exists, so that
01075      * the window positions file can be created with a different .extension.
01076      * this helps keep the length of the file name more reasonable.
01077      */
01078     strncpy(buf, window_xml_file, MAX_BUF);
01079     cp = strrchr(buf, '.');
01080     if (cp)
01081       cp[0] = 0;
01082 
01083     snprintf(loadname, sizeof(loadname), "%s/.crossfire/%s.pos", getenv("HOME"), buf);
01084     if (!(load = fopen(loadname, "r"))) {
01085         snprintf(buf, sizeof(buf),
01086             "Cannot open %s: Using default window positions.", loadname);
01087         draw_info(buf, NDI_RED);
01088         return;
01089     }
01090     else {
01091         snprintf(buf, sizeof(buf), "Loading window positions from %s", loadname);
01092         draw_info(buf, NDI_RED);
01093     }
01094 
01095     xml_tree = glade_get_widget_tree(GTK_WIDGET (window_root));
01096 
01097     while(fgets(buf, MAX_BUF-1, load)!=NULL) {
01098         if ((cp=strchr(buf,':'))!=NULL) {
01099             *cp=0;
01100             cp++;
01101             while(isspace(*cp)) cp++;
01102 
01103             if (!strcmp(buf,"window_root")) {
01104                 int x,y,w,h;
01105 
01106                 if (sscanf(cp,"+%d+%dx%dx%d", &x, &y, &w, &h) == 4) {
01107                     gtk_window_set_default_size (GTK_WINDOW(window_root), w, h);
01108                     gtk_window_move(GTK_WINDOW(window_root), x, y);
01109 
01110                 } else {
01111                     LOG(LOG_ERROR, "config.c::load_window_positions",
01112                         "Window size %s corrupt\n", cp);
01113                 }
01114             } else if (strstr(buf,"paned_")) {
01115                 /*
01116                  * The save names are a re-sizeable pane, but check to be sure
01117                  * it is a valid widget name if only to prevent sending a
01118                  * generic error to stderr if it does not exist in the current
01119                  * layout.
01120                  */
01121                 widget = glade_xml_get_widget(xml_tree, buf);
01122                 if (widget) {
01123                     gtk_paned_set_position(GTK_PANED(widget), atoi(cp));
01124                 } else {
01125                     LOG(LOG_INFO, "config.c::load_window_positions", "%s in "
01126                         "%s not found in this UI layout.\n", buf, loadname);
01127                 }
01128             } else {
01129                 LOG(LOG_ERROR, "config.c::load_window_positions",
01130                     "Found unknown line %s in %s\n", buf, loadname);
01131             }
01132         }
01133     }
01134     fclose(load);
01135 }