Crossfire Client, Branches  R11627
config.c
Go to the documentation of this file.
1 const char * const rcsid_gtk2_config_c =
2  "$Id: config.c 11627 2009-04-04 16:55:25Z lalo $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2005,2007 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire@metalforge.org
23 */
24 
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33 
34 #include <gtk/gtk.h>
35 #include <glade/glade.h>
36 #include <ctype.h>
37 
38 #include "client.h"
39 
40 #include "main.h"
41 #include "image.h"
42 #include "gtk2proto.h"
43 
44 #include <dirent.h>
45 
46 #ifdef MINGW
47 int alphasort(const struct dirent **a, const struct dirent **b)
48 {
49  return strcoll((*a)->d_name, (*b)->d_name);
50 }
51 
52 int scandir(const char *dir, struct dirent ***namelist,
53  int (*select)(const struct dirent *),
54  int (*compar)(const struct dirent **, const struct dirent **)) {
55  DIR *d;
56  struct dirent *entry;
57  register int i=0;
58  size_t entrysize;
59 
60  if((d = opendir(dir)) == NULL)
61  return -1;
62 
63  *namelist = NULL;
64  while ((entry = readdir(d)) != NULL)
65  {
66  if (select == NULL || (select != NULL && (*select)(entry)))
67  {
68  *namelist = (struct dirent **)realloc((void *)(*namelist),
69  (size_t)((i + 1) * sizeof(struct dirent *)));
70  if (*namelist == NULL) return -1;
71  entrysize = sizeof(struct dirent) - sizeof(entry->d_name) + strlen(entry->d_name) + 1;
72  (*namelist)[i] = (struct dirent *)malloc(entrysize);
73  if ((*namelist)[i] == NULL) return -1;
74  memcpy((*namelist)[i], entry, entrysize);
75  i++;
76  }
77  }
78  if (closedir(d)) return -1;
79  if (i == 0) return -1;
80  if (compar != NULL)
81  qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *),
82  (int(*)(const void*, const void*))compar);
83 
84  return i;
85 }
86 #endif
87 
96 
97 /* This is the string names that correspond to the numberic id's in client.h */
98 
99 static char *theme = "Standard";
100 static char *themedir = "themes";
101 static char *gladedir = "glade-gtk2";
102 
103 static const char * const display_modes[] = {"Pixmap", "SDL", "OpenGL"};
104 
116 void load_theme(int reload)
117 {
118  char path[MAX_BUF];
119  int i;
120  static char **default_files=NULL;
121 
122  /*
123  * We should only be called with reload once at startup. at that time,
124  * store away the default rc files so we can restore them later.
125  */
126  if (!reload) {
127  char **tmp;
128 
129  /*
130  * GTK man page says copy of this data should be made, so lets go and
131  * do that.
132  */
133  tmp = gtk_rc_get_default_files();
134  i=0;
135  while (tmp[i]) {
136  i++;
137  }
138  default_files = malloc(sizeof(char*) * (i+1));
139  i=0;
140  while (tmp[i]) {
141  default_files[i] = strdup(tmp[i]);
142  i++;
143  }
144  default_files[i] = NULL;
145  }
146 
147  if (!strcmp(theme,"None")) {
148  /*
149  * If this is default and initial run, nothing to do. If it not
150  * initial run, we need to reset the search path.
151  */
152  if (reload) {
153  gtk_rc_set_default_files(default_files);
154  }
155  } else {
156  snprintf(path, MAX_BUF, "%s/%s/%s", CF_DATADIR, themedir, theme);
157  /*
158  * Check for existence of file. Unfortunately, at initial run time,
159  * the window may not be realized, so we can't print this error to the
160  * user directly.
161  */
162  if (access(path, R_OK) == -1) {
163  LOG(LOG_ERROR, "config.c::load_theme",
164  "Unable to find theme file %s", path);
165  return;
166  }
167  /*
168  * We need to reset the search path, otherwise the effects are
169  * additive. In practice, we could manipulate pointer so it is just
170  * one call to gtk_rc_set_default_files(), but that is probably more
171  * complicated than just doing these two calls here.
172  */
173  gtk_rc_set_default_files(default_files);
174  gtk_rc_add_default_file(path);
175  }
176 
177  if (reload) {
178  /*
179  * Reload data - so we need to force GTK to reparse and rebind all the
180  * widget data. Then, we need to call our own functions to reparse the
181  * custom widget handling we do.
182  */
183  gtk_rc_reparse_all_for_settings(gtk_settings_get_for_screen(gdk_screen_get_default()), TRUE);
184  gtk_rc_reset_styles(gtk_settings_get_for_screen(gdk_screen_get_default()));
185  info_get_styles();
190  /*
191  * Set the inv_updated to force a redraw - otherwise it will not
192  * necessarily bind the lists with the new widgets.
193  */
194  cpl.ob->inv_updated = 1;
195  cpl.below->inv_updated = 1;
196  draw_lists();
197 
198  draw_stats(TRUE);
200  }
201 }
202 
207 void load_defaults(void)
208 {
209  char path[MAX_BUF],inbuf[MAX_BUF],*cp;
210  FILE *fp;
211  int i, val;
212 
213  /* Copy over the want values to use values now */
214  for (i=0; i<CONFIG_NUMS; i++) {
215  use_config[i] = want_config[i];
216  }
217 
218  snprintf(path, sizeof(path), "%s/.crossfire/gdefaults2", getenv("HOME"));
219  if ((fp=fopen(path,"r"))==NULL) return;
220  while (fgets(inbuf, MAX_BUF-1, fp)) {
221  inbuf[MAX_BUF-1]='\0';
222  inbuf[strlen(inbuf)-1]='\0'; /* kill newline */
223 
224  if (inbuf[0]=='#') continue;
225  /* Skip any setting line that does not contain a colon character */
226  if (!(cp=strchr(inbuf,':'))) continue;
227  *cp='\0';
228  cp+=2; /* colon, space, then value */
229 
230  val = -1;
231  if (isdigit(*cp)) val=atoi(cp);
232  else if (!strcmp(cp,"True")) val = TRUE;
233  else if (!strcmp(cp,"False")) val = FALSE;
234 
235  for (i=1; i<CONFIG_NUMS; i++) {
236  if (!strcmp(config_names[i], inbuf)) {
237  if (val == -1) {
238  LOG(LOG_WARNING, "config.c::load_defaults",
239  "Invalid value/line: %s: %s", inbuf, cp);
240  } else {
241  want_config[i] = val;
242  }
243  break; /* Found a match - won't find another */
244  }
245  }
246  /* We found a match in the loop above, so do not do anything more */
247  if (i < CONFIG_NUMS) continue;
248 
249  /*
250  * Legacy - now use the map_width and map_height values Don't do sanity
251  * checking - that will be done below
252  */
253  if (!strcmp(inbuf,"mapsize")) {
254  if (sscanf(cp,"%hdx%hd", &want_config[CONFIG_MAPWIDTH], &want_config[CONFIG_MAPHEIGHT])!=2) {
255  LOG(LOG_WARNING, "config.c::load_defaults",
256  "Malformed mapsize option in gdefaults2. Ignoring");
257  }
258  }
259  else if (!strcmp(inbuf, "server")) {
260  server = strdup_local(cp); /* memory leak ! */
261  continue;
262  }
263  else if (!strcmp(inbuf, "theme")) {
264  theme = strdup_local(cp); /* memory leak ! */
265  continue;
266  }
267  else if (!strcmp(inbuf, "window_layout")) {
268  strncpy(window_xml_file, cp, MAX_BUF-1);
269  continue;
270  }
271  else if (!strcmp(inbuf, "nopopups")) {
272  /* Changed name from nopopups to popups, so inverse value */
273  want_config[CONFIG_POPUPS] = !val;
274  continue;
275  }
276  else if (!strcmp(inbuf, "nosplash")) {
277  want_config[CONFIG_SPLASH] = !val;
278  continue;
279  }
280  else if (!strcmp(inbuf, "splash")) {
281  want_config[CONFIG_SPLASH] = val;
282  continue;
283  }
284  else if (!strcmp(inbuf, "faceset")) {
285  face_info.want_faceset = strdup_local(cp); /* memory leak ! */
286  continue;
287  }
288  /* legacy, as this is now just saved as 'lighting' */
289  else if (!strcmp(inbuf, "per_tile_lighting")) {
291  }
292  else if (!strcmp(inbuf, "per_pixel_lighting")) {
294  }
295  else if (!strcmp(inbuf, "resists")) {
296  if (val) want_config[CONFIG_RESISTS] = val;
297  }
298  else if (!strcmp(inbuf, "sdl")) {
300  }
301  else LOG(LOG_WARNING, "config.c::load_defaults",
302  "Unknown line in gdefaults2: %s %s", inbuf, cp);
303  }
304  fclose(fp);
305  /*
306  * Make sure some of the values entered are sane - since a user can edit
307  * the defaults file directly, they could put bogus values in
308  */
310  LOG(LOG_WARNING, "config.c::load_defaults",
311  "Ignoring iconscale value read from gdefaults2 file.\n"
312  "Invalid iconscale range (%d), valid range for -iconscale "
313  "is 25 through 200", want_config[CONFIG_ICONSCALE]);
315  }
317  LOG(LOG_WARNING, "config.c::load_defaults",
318  "ignoring mapscale value read for gdefaults2 file.\n"
319  "Invalid mapscale range (%d), valid range for -iconscale "
320  "is 25 through 200", want_config[CONFIG_MAPSCALE]);
322  }
324  LOG(LOG_WARNING, "config.c::load_defaults",
325  "No lighting mechanism selected - will not use darkness code");
327  }
328  if (want_config[CONFIG_RESISTS] > 2) {
329  LOG(LOG_WARNING, "config.c::load_defaults",
330  "ignoring resists display value read for gdafaults file.\n"
331  "Invalid value (%d), must be one value of 0, 1 or 2.",
334  }
335 
336  /* Make sure the map size os OK */
338  LOG(LOG_WARNING, "config.c::load_defaults", "Invalid map width (%d) "
339  "option in gdefaults2. Valid range is 9 to %d",
342  }
344  LOG(LOG_WARNING, "config.c::load_defaults", "Invalid map height (%d) "
345  "option in gdefaults2. Valid range is 9 to %d",
348  }
349 
350  /* Now copy over the values just loaded */
351  for (i=0; i<CONFIG_NUMS; i++) {
352  use_config[i] = want_config[i];
353  }
354 
358  /*inv_list.show_icon = use_config[CONFIG_SHOWICON];*/
359 }
360 
365 void save_defaults(void)
366 {
367  char path[MAX_BUF],buf[MAX_BUF];
368  FILE *fp;
369  int i;
370 
371  snprintf(path, sizeof(path), "%s/.crossfire/gdefaults2", getenv("HOME"));
372  if (make_path_to_file(path)==-1) {
373  LOG(LOG_ERROR, "config.c::save_defaults","Could not create %s", path);
374  return;
375  }
376  if ((fp=fopen(path,"w"))==NULL) {
377  LOG(LOG_ERROR, "config.c::save_defaults", "Could not open %s", path);
378  return;
379  }
380  fprintf(fp,"# crossfire-client-gtk2 automatically generates this file.\n");
381  fprintf(fp,"# Manual editing is allowed, but the client may be a bit\n");
382  fprintf(fp,"# finicky about the keys and values. Comparisons are case\n");
383  fprintf(fp,"# sensitive. 'True' and 'False' are the proper case, but\n");
384  fprintf(fp,"# have been replaced with 1 and 0 respectively.\n#\n");
385  fprintf(fp,"server: %s\n", server);
386  fprintf(fp,"theme: %s\n", theme);
387  fprintf(fp,"faceset: %s\n", face_info.want_faceset);
388  fprintf(fp,"window_layout: %s\n", window_xml_file);
389  /*
390  * This isn't quite as good as before, as instead of saving things as
391  * 'True' or 'False', it is just 1 or 0. However, for the most part, the
392  * user isn't going to be editing the file directly.
393  */
394  for (i=1; i < CONFIG_NUMS; i++) {
395  fprintf(fp,"%s: %d\n", config_names[i], want_config[i]);
396  }
397 
398  fclose(fp);
399  snprintf(buf, sizeof(buf), "Defaults saved to %s",path);
400  draw_info(buf,NDI_BLUE);
401 }
402 
407 void config_init(GtkWidget *window_root)
408 {
409  static int has_init=0;
410  GladeXML *xml_tree;
411  GtkWidget *widget;
412  int count, i;
413 
414  has_init=1;
415 
416  config_window = glade_xml_get_widget(dialog_xml, "config_window");
417  xml_tree = glade_get_widget_tree(GTK_WIDGET(config_window));
418 
419  config_spinbutton_cwindow =
420  glade_xml_get_widget(xml_tree, "config_spinbutton_cwindow");
421  config_button_echo =
422  glade_xml_get_widget(xml_tree, "config_button_echo");
423  config_button_fasttcp =
424  glade_xml_get_widget(xml_tree, "config_button_fasttcp");
425  config_button_grad_color =
426  glade_xml_get_widget(xml_tree, "config_button_grad_color");
427  config_button_foodbeep =
428  glade_xml_get_widget(xml_tree, "config_button_foodbeep");
429  config_button_sound =
430  glade_xml_get_widget(xml_tree, "config_button_sound");
431  config_button_cache =
432  glade_xml_get_widget(xml_tree, "config_button_cache");
433  config_button_download =
434  glade_xml_get_widget(xml_tree, "config_button_download");
435  config_button_fog =
436  glade_xml_get_widget(xml_tree, "config_button_fog");
437  config_button_smoothing =
438  glade_xml_get_widget(xml_tree, "config_button_smoothing");
439  config_spinbutton_iconscale =
440  glade_xml_get_widget(xml_tree, "config_spinbutton_iconscale");
441  config_spinbutton_mapscale =
442  glade_xml_get_widget(xml_tree, "config_spinbutton_mapscale");
443  config_spinbutton_mapwidth =
444  glade_xml_get_widget(xml_tree, "config_spinbutton_mapwidth");
445  config_spinbutton_mapheight =
446  glade_xml_get_widget(xml_tree, "config_spinbutton_mapheight");
447  config_combobox_displaymode =
448  glade_xml_get_widget(xml_tree, "config_combobox_displaymode");
449  config_combobox_faceset =
450  glade_xml_get_widget(xml_tree, "config_combobox_faceset");
451  config_combobox_lighting =
452  glade_xml_get_widget(xml_tree, "config_combobox_lighting");
453  config_combobox_theme =
454  glade_xml_get_widget(xml_tree, "config_combobox_theme");
455  config_combobox_glade =
456  glade_xml_get_widget(xml_tree, "config_combobox_glade");
457 
458  widget = glade_xml_get_widget(xml_tree, "config_button_save");
459  g_signal_connect ((gpointer) widget, "clicked",
460  G_CALLBACK (on_config_button_save_clicked), NULL);
461 
462  widget = glade_xml_get_widget(xml_tree, "config_button_apply");
463  g_signal_connect ((gpointer) widget, "clicked",
464  G_CALLBACK (on_config_button_apply_clicked), NULL);
465 
466  widget = glade_xml_get_widget(xml_tree, "config_button_close");
467  g_signal_connect ((gpointer) widget, "clicked",
468  G_CALLBACK (on_config_button_close_clicked), NULL);
469 
470  /*
471  * Display mode combo box setup. First, remove all entries, then populate
472  * with what options are available based on what was compiled in.
473  */
474  count = gtk_tree_model_iter_n_children(
475  gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_displaymode)), NULL);
476  for (i=0; i < count; i++)
477  gtk_combo_box_remove_text(GTK_COMBO_BOX(config_combobox_displaymode), 0);
478 
479 #ifdef HAVE_OPENGL
480  gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_displaymode), "OpenGL");
481 #endif
482 
483 #ifdef HAVE_SDL
484  gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_displaymode), "SDL");
485 #endif
486  gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_displaymode), "Pixmap");
487 
488 }
489 
500 static int scandir_theme_filter(const struct dirent *d)
501 {
502  if (d->d_name[0] == '.') return 0;
503  return 1;
504 }
505 
517 static int scandir_glade_filter(const struct dirent *d)
518 {
519  char *token = NULL;
520  char *extok = NULL;
521  char delim[] = ".";
522  char exten[] = "glade";
523  char parse[MAX_BUF] = "";
524 
525  strncpy(parse, d->d_name, MAX_BUF);
526  token = strtok(parse, delim);
527  while (token) {
528  extok = token;
529  token = strtok(NULL, delim);
530  }
531  if (extok && strncmp(exten, extok, strlen(exten)) == 0) {
532  if (strncmp(parse, DIALOG_XML_FILENAME, strlen(parse)) == 0)
533  return 0;
534  return 1;
535  }
536  return 0;
537 }
538 
565 static void fill_combobox_from_datadir(GtkWidget *combobox, char *active,
566  uint64 want_none, char *subdir, int (*scandir_filter) ())
567 {
568  int count, i;
569  GtkTreeModel *model;
570  gchar *buf;
571  GtkTreeIter iter;
572 
573  model = gtk_combo_box_get_model(GTK_COMBO_BOX(combobox));
574  count = gtk_tree_model_iter_n_children(model, NULL);
575  /*
576  * If count is 0, the combo box control has not been initialized yet, so
577  * fill it with the appropriate selections now.
578  */
579  if (count == 0) {
580  char path[MAX_BUF];
581  struct dirent **files;
582  int done_none=0;
583 
584  snprintf(path, MAX_BUF, "%s/%s", CF_DATADIR, subdir);
585 
586  count = scandir(path, &files, *scandir_filter, alphasort);
587  LOG(LOG_DEBUG, "config.c::fill_combobox_from_datadir",
588  "found %d files in %s\n", count, path);
589 
590  for (i=0; i<count; i++) {
591  /*
592  * If a 'None' entry is desired, and if an entry that falls after
593  * 'None' is found, and, if 'None' has not already been added,
594  * insert it now.
595  */
596  if (!done_none && want_none &&
597  strcmp(files[i]->d_name, "None") > 0) {
598  gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), "None");
599  done_none=1;
600  }
601 
602  gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), files[i]->d_name);
603  }
604  /* Set this for below */
605  count = gtk_tree_model_iter_n_children(model, NULL);
606  }
607  /*
608  * The block belows causes the matching combobox item to be selected. Set
609  * it irregardless of whether this is the first time this is run or not.
610  */
611  for (i=0; i < count; i++) {
612  if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
613  LOG(LOG_ERROR, "config.c::fill_combobox_from_datadir",
614  "Unable to get combo box iter\n");
615  break;
616  }
617  gtk_tree_model_get(model, &iter, 0, &buf, -1);
618  if (!strcasecmp(active, buf)) {
619  gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), i);
620  g_free(buf);
621  break;
622  }
623  g_free(buf);
624  }
625 }
626 
627 /*
628  * Setup_config_window sets the buttons, combos, etc, to the state that matches
629  * the want_config[] values.
630  */
631 static void setup_config_window(void)
632 {
633  int count, i;
634  GtkTreeModel *model;
635  gchar *buf;
636  GtkTreeIter iter;
637 
638  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_echo),
640 
641  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_fasttcp),
643 
644  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_grad_color),
646 
647  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_foodbeep),
649 
650  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_sound),
652 
653  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_cache),
655 
656  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_download),
658 
659  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_fog),
661 
662  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(config_button_smoothing),
664 
665  gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_cwindow),
666  (float)want_config[CONFIG_CWINDOW]);
667 
668  gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_iconscale),
669  (float)want_config[CONFIG_ICONSCALE]);
670 
671  gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_mapscale),
672  (float)want_config[CONFIG_MAPSCALE]);
673 
674  gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_mapwidth),
675  (float)want_config[CONFIG_MAPWIDTH]);
676 
677  gtk_spin_button_set_value(GTK_SPIN_BUTTON(config_spinbutton_mapheight),
678  (float)want_config[CONFIG_MAPHEIGHT]);
679  /*
680  * Face set combo box setup.
681  * Remove all the entries currently in the combo box
682  */
683  model = gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_faceset));
684  count = gtk_tree_model_iter_n_children(model, NULL);
685 
686  for (i=0; i < count; i++)
687  gtk_combo_box_remove_text(GTK_COMBO_BOX(config_combobox_faceset), 0);
688 
689  /* If we have real faceset info from the server, use it */
691  for (i=0; i<MAX_FACE_SETS; i++)
692  if (face_info.facesets[i].fullname)
693  gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_faceset),
695  } else {
696  gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_faceset), "Standard");
697  gtk_combo_box_append_text(GTK_COMBO_BOX(config_combobox_faceset), "Classic");
698  }
699  count = gtk_tree_model_iter_n_children(model, NULL);
700 
701  for (i=0; i < count; i++) {
702  if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
703  LOG(LOG_ERROR, "config.c::setup_config_window",
704  "Unable to get faceset iter\n");
705  break;
706  }
707  gtk_tree_model_get(model, &iter, 0, &buf, -1);
708 
709  if (face_info.want_faceset && !strcasecmp(face_info.want_faceset, buf)) {
710  gtk_combo_box_set_active(GTK_COMBO_BOX(config_combobox_faceset), i);
711  g_free(buf);
712  break;
713  }
714  g_free(buf);
715  }
716 
717  if (sizeof(display_modes) < want_config[CONFIG_DISPLAYMODE]) {
718  LOG(LOG_ERROR, "config.c::setup_config_window",
719  "Player display mode not in display_modes range\n");
720  } else {
721  /*
722  * We want to set up the boxes to match current settings for things
723  * like displaymode.
724  */
725  model = gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_displaymode));
726  count = gtk_tree_model_iter_n_children(model, NULL);
727  for (i=0; i < count; i++) {
728  if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
729  LOG(LOG_ERROR, "config.c::setup_config_window",
730  "Unable to get faceset iter\n");
731  break;
732  }
733  gtk_tree_model_get(model, &iter, 0, &buf, -1);
734  if (!strcasecmp(display_modes[want_config[CONFIG_DISPLAYMODE]], buf)) {
735  gtk_combo_box_set_active(GTK_COMBO_BOX(config_combobox_displaymode), i);
736  g_free(buf);
737  break;
738  }
739  g_free(buf);
740  }
741  }
742 
743  /*
744  * We want to set up the boxes to match current settings for things like
745  * lighting. A bit of a hack to hardcode the strings below.
746  */
747  model = gtk_combo_box_get_model(GTK_COMBO_BOX(config_combobox_lighting));
748  count = gtk_tree_model_iter_n_children(model, NULL);
749  for (i=0; i < count; i++) {
750  if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
751  LOG(LOG_ERROR, "config.c::setup_config_window",
752  "Unable to get lighting iter\n");
753  break;
754  }
755  gtk_tree_model_get(model, &iter, 0, &buf, -1);
756  if ((want_config[CONFIG_LIGHTING] == CFG_LT_TILE && !strcasecmp(buf, "Per Tile")) ||
757  (want_config[CONFIG_LIGHTING] == CFG_LT_PIXEL && !strcasecmp(buf, "Fast Per Pixel")) ||
758  (want_config[CONFIG_LIGHTING] == CFG_LT_PIXEL_BEST && !strcasecmp(buf, "Best Per Pixel")) ||
759  (want_config[CONFIG_LIGHTING] == CFG_LT_NONE && !strcasecmp(buf, "None"))) {
760  gtk_combo_box_set_active(GTK_COMBO_BOX(config_combobox_lighting), i);
761  g_free(buf);
762  break;
763  }
764  g_free(buf);
765  }
766  /*
767  * Use the filenames of files found in the themes subdirectory of the
768  * crossfire-client data directory as the selectable items in a combo box
769  * selector control. Specify the current default, and that a "None" entry
770  * needs to be added also.
771  */
772  fill_combobox_from_datadir(config_combobox_theme, theme, 1, themedir,
774  /*
775  * Use the filenames of files found in the glade-gtk subdirectory of the
776  * crossfire-client data directory as the selectable items in a combo box
777  * selector control. Specify the current default, and that no "None" entry
778  * is desired.
779  */
780  fill_combobox_from_datadir(config_combobox_glade, window_xml_file, 0,
781  gladedir, &scandir_glade_filter);
782 }
783 
784 #define IS_DIFFERENT(TYPE) (want_config[TYPE] != use_config[TYPE])
785 
791 static void read_config_window(void)
792 {
793  gchar *buf;
794 
795  want_config[CONFIG_ECHO] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_echo));
796  want_config[CONFIG_FASTTCP] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_fasttcp));
797  want_config[CONFIG_GRAD_COLOR] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_grad_color));
798  want_config[CONFIG_FOODBEEP] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_foodbeep));
799  want_config[CONFIG_SOUND] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_sound));
800  want_config[CONFIG_CACHE] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_cache));
801  want_config[CONFIG_DOWNLOAD] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_download));
802  want_config[CONFIG_FOGWAR] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_fog));
803  want_config[CONFIG_SMOOTH] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(config_button_smoothing));
804  want_config[CONFIG_CWINDOW] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_cwindow));
805  want_config[CONFIG_ICONSCALE] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_iconscale));
806  want_config[CONFIG_MAPSCALE] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_mapscale));
807  want_config[CONFIG_MAPWIDTH] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_mapwidth));
808  want_config[CONFIG_MAPHEIGHT] = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(config_spinbutton_mapheight));
809 
810  buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_faceset));
811 
812  /*
813  * We may be able to eliminate the extra strdup/free, but I'm not 100% sure
814  * that we are guaranteed that glib won't implement them through its
815  * own/different malloc library.
816  */
817  if (buf) {
818  free(face_info.want_faceset);
820  g_free(buf);
821  }
822 
823  buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_displaymode));
824  if (buf) {
825  if (!strcasecmp(buf,"OpenGL")) want_config[CONFIG_DISPLAYMODE] = CFG_DM_OPENGL;
826  else if (!strcasecmp(buf,"SDL")) want_config[CONFIG_DISPLAYMODE] = CFG_DM_SDL;
827  else if (!strcasecmp(buf,"Pixmap")) want_config[CONFIG_DISPLAYMODE] = CFG_DM_PIXMAP;
828  g_free(buf);
829  }
830 
831  buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_lighting));
832  if (buf) {
833  if (!strcasecmp(buf, "Per Tile")) want_config[CONFIG_LIGHTING] = CFG_LT_TILE;
834  else if (!strcasecmp(buf, "Fast Per Pixel")) want_config[CONFIG_LIGHTING] = CFG_LT_PIXEL;
835  else if (!strcasecmp(buf, "Best Per Pixel")) want_config[CONFIG_LIGHTING] = CFG_LT_PIXEL_BEST;
836  else if (!strcasecmp(buf, "None")) want_config[CONFIG_LIGHTING] = CFG_LT_NONE;
840  }
841 
842  g_free(buf);
843  }
844  /*
845  * Load up the theme. A problem is we don't really know if theme has been
846  * allocated or is just pointing as a static string. So we lose a few
847  * bytes and just don't free the data.
848  */
849  buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_theme));
850  if (buf) {
851  if (strcmp(buf, theme)) {
852  theme = buf;
853  load_theme(TRUE);
854  } else {
855  g_free(buf);
856  }
857  } else {
858  theme = "None";
859  }
860  /*
861  * Load up the layout and set the combobox control to point to the loaded
862  * default value.
863  */
864  buf = gtk_combo_box_get_active_text(GTK_COMBO_BOX(config_combobox_glade));
865  if (buf && strcmp(buf, window_xml_file)) {
866  strncpy(window_xml_file, buf, MAX_BUF);
867  }
868  /*
869  * Some values can take effect right now, others not. Code below handles
870  * these cases - largely grabbed from gtk/config.c
871  */
872  if (IS_DIFFERENT(CONFIG_SOUND)) {
873  int tmp;
874  if (want_config[CONFIG_SOUND]) {
875  tmp = init_sounds();
876  if (csocket.fd)
877  cs_print_string(csocket.fd, "setup sound %d", tmp >= 0);
878  } else {
879  if (csocket.fd)
880  cs_print_string(csocket.fd, "setup sound 0");
881  }
883  }
885 #ifdef TCP_NODELAY
886 #ifndef WIN32
887  int q = want_config[CONFIG_FASTTCP];
888 
889  if (csocket.fd && setsockopt(csocket.fd, SOL_TCP, TCP_NODELAY, &q, sizeof(q)) == -1)
890  perror("TCP_NODELAY");
891 #else
892  int q = want_config[CONFIG_FASTTCP];
893 
894  if (csocket.fd && setsockopt(csocket.fd, SOL_TCP, TCP_NODELAY, ( const char* )&q, sizeof(q)) == -1)
895  perror("TCP_NODELAY");
896 #endif
897 #endif
899  }
900 
902 #ifdef HAVE_SDL
904  /* This is done to make the 'lightmap' in the proper format */
905  init_SDL( NULL, 1);
906 #endif
907  }
908  /*
909  * Nothing to do, but we can switch immediately without problems. do force
910  * a redraw
911  */
914  draw_stats(TRUE);
915  }
916 }
917 
925 void
926 on_config_button_save_clicked (GtkButton *button,
927  gpointer user_data)
928 {
930  save_defaults();
931 }
932 
940 void
942  gpointer user_data)
943 {
945 }
946 
954 void
956  gpointer user_data)
957 {
958  gtk_widget_hide(config_window);
959 }
960 
967 void
968 on_configure_activate (GtkMenuItem *menuitem,
969  gpointer user_data)
970 {
971  gtk_widget_show(config_window);
973 }
974 
981 void save_winpos(void)
982 {
983  char savename[MAX_BUF];
984  char buf[MAX_BUF];
985  char *cp;
986  FILE *save;
987  int x,y,w,h,wx,wy;
988  extern GtkWidget *window_root;
989  GList *pane_list, *list_loop;
990  GladeXML *xml_tree;
991  GtkWidget *widget;
992 
993  /*
994  * Truncate window_xml_file to remove a .extension if one exists, so that
995  * the window positions file can be created with a different .extension.
996  * this helps keep the length of the file name more reasonable.
997  */
998  strncpy(buf, window_xml_file, MAX_BUF);
999  cp = strrchr(buf, '.');
1000  if (cp)
1001  cp[0] = 0;
1002 
1003  snprintf(savename, sizeof(savename), "%s/.crossfire/%s.pos", getenv("HOME"), buf);
1004  if (!(save = fopen(savename, "w"))) {
1005  snprintf(buf, sizeof(buf), "Cannot open %s - window positions not saved!", savename);
1006  draw_info(buf, NDI_RED);
1007  return;
1008  }
1009  get_window_coord(window_root, &x,&y, &wx,&wy,&w,&h);
1010  fprintf(save,"window_root: +%d+%dx%dx%d\n", wx, wy, w, h);
1011 
1012  xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root));
1013  /*
1014  * Iterate through all widgets whose names begin with hpaned_* and vpaned_*
1015  * to save the widget name and the position of the pane divider. Widgets
1016  * are dynamically found and processed so long as the .glade file designer
1017  * adheres to the naming conventions that Glade Designer uses.
1018  */
1019  pane_list = glade_xml_get_widget_prefix(xml_tree, "hpaned_");
1020  for (list_loop = pane_list; list_loop; list_loop = g_list_next(list_loop)) {
1021  widget = list_loop->data;
1022  fprintf(save, "%s: %d\n", glade_get_widget_name(widget),
1023  gtk_paned_get_position(GTK_PANED(widget)));
1024  }
1025  g_list_free(pane_list);
1026 
1027  pane_list = glade_xml_get_widget_prefix(xml_tree, "vpaned_");
1028  for (list_loop = pane_list; list_loop; list_loop = g_list_next(list_loop)) {
1029  widget = list_loop->data;
1030  fprintf(save, "%s: %d\n", glade_get_widget_name(widget),
1031  gtk_paned_get_position(GTK_PANED(widget)));
1032  }
1033  g_list_free(pane_list);
1034 
1035  fclose(save);
1036  snprintf(buf, sizeof(buf), "Window positions saved to %s", savename);
1037  draw_info(buf, NDI_BLUE);
1038 }
1039 
1047 void
1048 on_save_window_position_activate (GtkMenuItem *menuitem,
1049  gpointer user_data)
1050 {
1051  save_winpos();
1052  /*
1053  * The following prevents multiple saves per menu activation.
1054  */
1055  g_signal_stop_emission_by_name(GTK_OBJECT(menuitem), "activate");
1056 }
1057 
1065 {
1066  char loadname[MAX_BUF];
1067  char buf[MAX_BUF];
1068  char *cp;
1069  GladeXML *xml_tree;
1070  GtkWidget *widget;
1071  FILE *load;
1072 
1073  /*
1074  * Truncate window_xml_file to remove a .extension if one exists, so that
1075  * the window positions file can be created with a different .extension.
1076  * this helps keep the length of the file name more reasonable.
1077  */
1078  strncpy(buf, window_xml_file, MAX_BUF);
1079  cp = strrchr(buf, '.');
1080  if (cp)
1081  cp[0] = 0;
1082 
1083  snprintf(loadname, sizeof(loadname), "%s/.crossfire/%s.pos", getenv("HOME"), buf);
1084  if (!(load = fopen(loadname, "r"))) {
1085  snprintf(buf, sizeof(buf),
1086  "Cannot open %s: Using default window positions.", loadname);
1087  draw_info(buf, NDI_RED);
1088  return;
1089  }
1090  else {
1091  snprintf(buf, sizeof(buf), "Loading window positions from %s", loadname);
1092  draw_info(buf, NDI_RED);
1093  }
1094 
1095  xml_tree = glade_get_widget_tree(GTK_WIDGET (window_root));
1096 
1097  while(fgets(buf, MAX_BUF-1, load)!=NULL) {
1098  if ((cp=strchr(buf,':'))!=NULL) {
1099  *cp=0;
1100  cp++;
1101  while(isspace(*cp)) cp++;
1102 
1103  if (!strcmp(buf,"window_root")) {
1104  int x,y,w,h;
1105 
1106  if (sscanf(cp,"+%d+%dx%dx%d", &x, &y, &w, &h) == 4) {
1107  gtk_window_set_default_size (GTK_WINDOW(window_root), w, h);
1108  gtk_window_move(GTK_WINDOW(window_root), x, y);
1109 
1110  } else {
1111  LOG(LOG_ERROR, "config.c::load_window_positions",
1112  "Window size %s corrupt\n", cp);
1113  }
1114  } else if (strstr(buf,"paned_")) {
1115  /*
1116  * The save names are a re-sizeable pane, but check to be sure
1117  * it is a valid widget name if only to prevent sending a
1118  * generic error to stderr if it does not exist in the current
1119  * layout.
1120  */
1121  widget = glade_xml_get_widget(xml_tree, buf);
1122  if (widget) {
1123  gtk_paned_set_position(GTK_PANED(widget), atoi(cp));
1124  } else {
1125  LOG(LOG_INFO, "config.c::load_window_positions", "%s in "
1126  "%s not found in this UI layout.\n", buf, loadname);
1127  }
1128  } else {
1129  LOG(LOG_ERROR, "config.c::load_window_positions",
1130  "Found unknown line %s in %s\n", buf, loadname);
1131  }
1132  }
1133  }
1134  fclose(load);
1135 }
GtkWidget * config_button_cache
Definition: config.c:88
void update_spell_information(void)
Definition: spells.c:119
#define SOL_TCP
Definition: client-types.h:124
void info_get_styles(void)
Definition: info.c:165
void load_window_positions(GtkWidget *window_root)
Definition: config.c:1064
void init_SDL(GtkWidget *sdl_window, int just_lightmap)
void spell_get_styles(void)
Definition: spells.c:72
#define DEFAULT_IMAGE_SIZE
Definition: gx11.c:114
static char * gladedir
Definition: config.c:101
GtkWidget * config_spinbutton_cwindow
Definition: config.c:88
GtkWidget * config_button_sound
Definition: config.c:88
#define CONFIG_POPUPS
Definition: client.h:160
GtkWidget * config_button_fasttcp
Definition: config.c:88
#define CONFIG_ECHO
Definition: client.h:153
GtkWidget * config_combobox_lighting
Definition: config.c:88
GtkWidget * config_button_smoothing
Definition: config.c:88
char window_xml_file[MAX_BUF]
Definition: main.c:98
item * ob
Definition: client.h:272
#define IS_DIFFERENT(TYPE)
Definition: config.c:784
void config_init(GtkWidget *window_root)
Definition: config.c:407
GtkWidget * config_spinbutton_mapwidth
Definition: config.c:88
#define MAP_MAX_SIZE
Definition: client.h:447
static void fill_combobox_from_datadir(GtkWidget *combobox, char *active, uint64 want_none, char *subdir, int(*scandir_filter)())
Definition: config.c:565
ClientSocket csocket
Definition: client.c:78
GtkWidget * config_button_download
Definition: config.c:88
void save_winpos(void)
Definition: config.c:981
FaceSets facesets[MAX_FACE_SETS]
Definition: client.h:355
int make_path_to_file(char *filename)
Definition: misc.c:94
Face_Information face_info
Definition: image.c:167
void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy, int *w, int *h)
Definition: gx11.c:4789
int map_image_half_size
Definition: gx11.c:117
#define CONFIG_SPLASH
Definition: client.h:178
#define CONFIG_FOGWAR
Definition: client.h:157
sint16 want_config[CONFIG_NUMS]
Definition: init.c:50
#define CONFIG_MAPSCALE
Definition: client.h:159
uint16 inv_updated
Definition: item.h:70
GtkWidget * config_spinbutton_mapscale
Definition: config.c:88
char * server
Definition: client.c:56
#define CFG_LT_PIXEL_BEST
Definition: client.h:189
int init_sounds(void)
Definition: sound.c:60
void on_configure_activate(GtkMenuItem *menuitem, gpointer user_data)
Definition: config.c:968
GladeXML * dialog_xml
Definition: main.c:57
#define CFG_DM_SDL
Definition: client.h:195
void draw_stats(int redraw)
Definition: gx11.c:2015
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
#define NDI_RED
Definition: newclient.h:204
#define TRUE
Definition: client-types.h:71
#define CONFIG_CWINDOW
Definition: client.h:155
#define CONFIG_FOODBEEP
Definition: client.h:172
int map_image_size
Definition: gx11.c:117
GtkWidget * window_root
Definition: main.c:56
#define CONFIG_NUMS
Definition: client.h:183
int cs_print_string(int fd, const char *str,...)
Definition: newsocket.c:259
void draw_message_window(int redraw)
Definition: gx11.c:2458
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
static char * theme
Definition: config.c:99
#define CFG_LT_TILE
Definition: client.h:187
void on_config_button_close_clicked(GtkButton *button, gpointer user_data)
Definition: config.c:955
GtkWidget * config_button_grad_color
Definition: config.c:88
GtkWidget * config_combobox_theme
Definition: config.c:88
item * below
Definition: client.h:273
#define CFG_LT_PIXEL
Definition: client.h:188
void draw_lists(void)
Definition: inventory.c:1029
Client_Player cpl
Definition: client.c:77
#define CONFIG_GRAD_COLOR
Definition: client.h:175
static char * themedir
Definition: config.c:100
GtkWidget * config_combobox_glade
Definition: config.c:88
#define CONFIG_CACHE
Definition: client.h:156
#define CONFIG_FASTTCP
Definition: client.h:154
int fd
Definition: client.h:97
#define DIALOG_XML_FILENAME
Definition: main.h:42
GtkWidget * config_window
Definition: config.c:88
GtkWidget * config_spinbutton_mapheight
Definition: config.c:88
void save_defaults(void)
Definition: config.c:848
#define CONFIG_SMOOTH
Definition: client.h:177
GtkWidget * config_combobox_faceset
Definition: config.c:88
void stats_get_styles(void)
Definition: stats.c:139
#define CFG_LT_NONE
Definition: client.h:186
#define MAX_BUF
Definition: client-types.h:128
#define CFG_DM_PIXMAP
Definition: client.h:194
#define CONFIG_MAPWIDTH
Definition: client.h:170
GtkWidget * config_combobox_displaymode
Definition: config.c:88
GtkWidget * config_button_foodbeep
Definition: config.c:88
#define MAX_FACE_SETS
Definition: client.h:315
#define CONFIG_ICONSCALE
Definition: client.h:158
const char *const rcsid_gtk2_config_c
Definition: config.c:1
char * strdup_local(const char *str)
Definition: misc.c:125
static int has_init
Definition: spells.c:63
#define CONFIG_MAPHEIGHT
Definition: client.h:171
#define CONFIG_DOWNLOAD
Definition: client.h:152
#define CONFIG_RESISTS
Definition: client.h:176
static void setup_config_window(void)
Definition: config.c:631
void load_theme(int reload)
Definition: config.c:116
char * fullname
Definition: client.h:333
GtkWidget * config_button_echo
Definition: config.c:88
void draw_info(const char *str, int color)
Definition: gx11.c:1773
static int scandir_theme_filter(const struct dirent *d)
Definition: config.c:500
GtkWidget * config_spinbutton_iconscale
Definition: config.c:88
void load_defaults(void)
Definition: config.c:710
#define CFG_DM_OPENGL
Definition: client.h:196
void inventory_get_styles(void)
Definition: inventory.c:394
#define CONFIG_DISPLAYMODE
Definition: client.h:161
void on_save_window_position_activate(GtkMenuItem *menuitem, gpointer user_data)
Definition: config.c:1048
static const char *const display_modes[]
Definition: config.c:103
#define CONFIG_LIGHTING
Definition: client.h:168
static void read_config_window(void)
Definition: config.c:791
#define FALSE
Definition: client-types.h:68
void on_config_button_save_clicked(GtkButton *button, gpointer user_data)
Definition: config.c:926
int image_size
Definition: gx11.c:116
const char *const config_names[CONFIG_NUMS]
Definition: init.c:40
#define CONFIG_DARKNESS
Definition: client.h:173
void on_config_button_apply_clicked(GtkButton *button, gpointer user_data)
Definition: config.c:941
static int scandir_glade_filter(const struct dirent *d)
Definition: config.c:517
GtkWidget * config_button_fog
Definition: config.c:88
#define NDI_BLUE
Definition: newclient.h:206
#define CONFIG_SOUND
Definition: client.h:164