pcb 4.1.1
An interactive printed circuit board layout editor.

gui-config.c

Go to the documentation of this file.
00001 /*
00002  *                            COPYRIGHT
00003  *
00004  *  PCB, interactive printed circuit board design
00005  *  Copyright (C) 1994,1995,1996 Thomas Nau
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License along
00018  *  with this program; if not, write to the Free Software Foundation, Inc.,
00019  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  */
00022 
00023 /* This file written by Bill Wilson for the PCB Gtk port.
00024 */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #include "config.h"
00028 #endif
00029 
00030 
00031 #ifdef HAVE_STDLIB_H
00032 #include <stdlib.h>
00033 #endif
00034 
00035 #include "gui.h"
00036 #include "hid.h"
00037 #include "../hidint.h"
00038 #include "gtkhid.h"
00039 
00040 #include "global.h"
00041 #include "action.h"
00042 #include "change.h"
00043 #include "file.h"
00044 #include "error.h"
00045 #include "draw.h"
00046 #include "misc.h" /* MKDIR() */
00047 #include "pcb-printf.h"
00048 #include "set.h"
00049 
00050 #if 0
00051 #include <locale.h>
00052 #endif
00053 #ifdef HAVE_LIBDMALLOC
00054 #include <dmalloc.h>
00055 #endif
00056 
00057 extern int      MoveLayerAction(int argc, char **argv, int x, int y);
00058 /* This is defined in main.c */
00059 void save_increments (const Increments *mm, const Increments *mil);
00060 
00061 enum ConfigType
00062 {
00063   CONFIG_Boolean,
00064   CONFIG_Integer,
00065   CONFIG_Coord,
00066   CONFIG_Real,
00067   CONFIG_String,
00068   CONFIG_Unused
00069 };
00070 
00071 typedef struct
00072 {
00073   gchar *name;
00074   enum ConfigType type;
00075   void *value;
00076 }
00077 ConfigAttribute;
00078 
00079 
00080 enum ColorTypes
00081 {
00082   MISC_COLOR,
00083   MISC_SELECTED_COLOR,
00084   LAYER_COLOR,
00085   LAYER_SELECTED_COLOR
00086 };
00087 
00088 typedef struct
00089 {
00090   HID_Attribute *attributes;
00091   enum ColorTypes type;
00092   GdkColor color;
00093   gboolean color_is_mapped;
00094 }
00095 ConfigColor;
00096 
00097 static GList *config_color_list, *lib_newlib_list;
00098 
00099 static gchar *lib_newlib_config, *board_size_override;
00100 
00101 
00102 static gchar *color_file;
00103 
00104 extern void ghid_set_special_colors (HID_Attribute * ha);
00105 
00106 
00107 #define PCB_CONFIG_DIR  ".pcb"
00108 #define PCB_CONFIG_FILE "preferences"
00109 #define PCB_COLORS_DIR  "colors"
00110 
00111 static gchar *config_dir, *color_dir;
00112 
00113   /* CONFIG_Unused types are expected to be found in main_attribute_list and
00114      |  will be assigned the type found there.  NULL value pointers here are
00115      |  also expected to be assigned values from the main_attribute_list.
00116    */
00117   /* PinoutFont also not used anymore */
00118 
00119 static ConfigAttribute config_attributes[] = {
00120   {"gui-compact-horizontal", CONFIG_Boolean, &_ghidgui.compact_horizontal},
00121   {"gui-compact-vertical", CONFIG_Boolean, &_ghidgui.compact_vertical},
00122   {"use-command-window", CONFIG_Boolean, &_ghidgui.use_command_window},
00123   {"save-in-tmp", CONFIG_Unused, NULL},
00124   {"save-metric-only", CONFIG_Unused, NULL},
00125   {"grid-units", CONFIG_Unused, NULL},
00126   {"grid", CONFIG_Unused, NULL},
00127 
00128   {"grid-increment-mm", CONFIG_Unused, NULL},
00129   {"line-increment-mm", CONFIG_Unused, NULL},
00130   {"size-increment-mm", CONFIG_Unused, NULL},
00131   {"clear-increment-mm", CONFIG_Unused, NULL},
00132   {"grid-increment-mil", CONFIG_Unused, NULL},
00133   {"line-increment-mil", CONFIG_Unused, NULL},
00134   {"size-increment-mil", CONFIG_Unused, NULL},
00135   {"clear-increment-mil", CONFIG_Unused, NULL},
00136 
00137   {"history-size", CONFIG_Integer, &_ghidgui.history_size},
00138   {"top-window-width", CONFIG_Integer, &_ghidgui.top_window_width},
00139   {"top-window-height", CONFIG_Integer, &_ghidgui.top_window_height},
00140   {"log-window-width", CONFIG_Integer, &_ghidgui.log_window_width},
00141   {"log-window-height", CONFIG_Integer, &_ghidgui.log_window_height},
00142   {"drc-window-width", CONFIG_Integer, &_ghidgui.drc_window_width},
00143   {"drc-window-height", CONFIG_Integer, &_ghidgui.drc_window_height},
00144   {"library-window-width", CONFIG_Integer, &_ghidgui.library_window_width},
00145   {"library-window-height", CONFIG_Integer, &_ghidgui.library_window_height},
00146   {"netlist-window-width", CONFIG_Integer, &_ghidgui.netlist_window_width},
00147   {"netlist-window-height", CONFIG_Integer, &_ghidgui.netlist_window_height},
00148   {"keyref-window-width", CONFIG_Integer, &_ghidgui.keyref_window_width},
00149   {"keyref-window-height", CONFIG_Integer, &_ghidgui.keyref_window_height},
00150   {"text-scale", CONFIG_Unused, NULL},
00151   {"via-thickness", CONFIG_Unused, NULL},
00152   {"via-drilling-hole", CONFIG_Unused, NULL},
00153   {"backup-interval", CONFIG_Unused, NULL},
00154   {"line-thickness", CONFIG_Unused, NULL},
00155   {"rat-thickness", CONFIG_Unused, NULL},
00156   {"bloat", CONFIG_Unused, NULL},
00157   {"shrink", CONFIG_Unused, NULL},
00158   {"min-width", CONFIG_Unused, NULL},
00159   {"min-silk", CONFIG_Unused, NULL},
00160   {"min-drill", CONFIG_Unused, NULL},
00161   {"min-ring", CONFIG_Unused, NULL},
00162   {"default-PCB-width", CONFIG_Unused, NULL},
00163   {"default-PCB-height", CONFIG_Unused, NULL},
00164 
00165   {"groups", CONFIG_Unused, NULL},
00166   {"route-styles", CONFIG_Unused, NULL},
00167   {"library-newlib", CONFIG_String, &lib_newlib_config},
00168   {"color-file", CONFIG_String, &color_file},
00169 /* FIXME: construct layer-names- in a list */
00170   {"layer-name-1", CONFIG_Unused, NULL},
00171   {"layer-name-2", CONFIG_Unused, NULL},
00172   {"layer-name-3", CONFIG_Unused, NULL},
00173   {"layer-name-4", CONFIG_Unused, NULL},
00174   {"layer-name-5", CONFIG_Unused, NULL},
00175   {"layer-name-6", CONFIG_Unused, NULL},
00176   {"layer-name-7", CONFIG_Unused, NULL},
00177   {"layer-name-8", CONFIG_Unused, NULL},
00178 };
00179 
00180 static gboolean
00181 dup_core_string (gchar ** dst, const gchar * src)
00182 {
00183   if (dst == NULL || (*dst == NULL && src == NULL))
00184     return FALSE;
00185 
00186   if (*dst != NULL && src != NULL && strcmp (*dst, src) == 0)
00187     return FALSE;
00188 
00189   free (*dst);
00190   *dst = (src == NULL) ? NULL : strdup (src);
00191 
00192   return TRUE;
00193 }
00194 
00195 static FILE *
00196 config_file_open (gchar * mode)
00197 {
00198   FILE *f;
00199   gchar *homedir, *fname;
00200 
00201 
00202   homedir = (gchar *) g_get_home_dir ();
00203   if (!homedir)
00204     {
00205       g_message ("config_file_open: Can't get home directory!");
00206       return NULL;
00207     }
00208 
00209   if (!config_dir)
00210     {
00211       config_dir =
00212         g_build_path (G_DIR_SEPARATOR_S, homedir, PCB_CONFIG_DIR, NULL);
00213       if (!g_file_test (config_dir, G_FILE_TEST_IS_DIR)
00214           && MKDIR (config_dir, 0755) < 0)
00215         {
00216           g_message ("config_file_open: Can't make \"%s\" directory!",
00217                      config_dir);
00218           g_free (config_dir);
00219           config_dir = NULL;
00220           return NULL;
00221         }
00222     }
00223 
00224   if (!color_dir)               /* Convenient to make the color dir here */
00225     {
00226       color_dir =
00227         g_build_path (G_DIR_SEPARATOR_S, config_dir, PCB_COLORS_DIR, NULL);
00228       if (!g_file_test (color_dir, G_FILE_TEST_IS_DIR))
00229         {
00230           if (MKDIR (color_dir, 0755) < 0)
00231             {
00232               g_message ("config_file_open: Can't make \"%s\" directory!",
00233                          color_dir);
00234               g_free (color_dir);
00235               color_dir = NULL;
00236             }
00237           fname = g_build_path (G_DIR_SEPARATOR_S,
00238                                 color_dir, "Default", NULL);
00239           dup_string (&color_file, fname);
00240           g_free (fname);
00241         }
00242     }
00243 
00244   fname = g_build_path (G_DIR_SEPARATOR_S, config_dir, PCB_CONFIG_FILE, NULL);
00245   f = fopen (fname, mode);
00246 
00247   g_free (fname);
00248   return f;
00249 }
00250 
00251 static ConfigAttribute *
00252 lookup_config_attribute (gchar * name, gboolean if_null_value)
00253 {
00254   ConfigAttribute *ca;
00255 
00256   for (ca = &config_attributes[0];
00257        ca < &config_attributes[0] + G_N_ELEMENTS (config_attributes); ++ca)
00258     {
00259       if (name && (!strcmp (name, ca->name)))
00260         {
00261           if (ca->value && if_null_value)
00262             break;
00263           return ca;
00264         }
00265     }
00266   return NULL;
00267 }
00268 
00269 void
00270 ghid_config_init (void)
00271 {
00272   HID_AttrNode *ha;
00273   HID_Attribute *a;
00274   ConfigAttribute *ca, dummy_attribute;
00275   ConfigColor *cc;
00276   gint len;
00277 
00278   ghidgui->n_mode_button_columns = 3;
00279   ghidgui->small_label_markup = TRUE;
00280   ghidgui->history_size = 5;
00281   dup_string (&color_file, "");
00282 
00283   for (ha = hid_attr_nodes; ha; ha = ha->next)
00284     {
00285       for (a = ha->attributes; a < ha->attributes + ha->n; ++a)
00286         {
00287           if (!a->value)
00288             continue;
00289           if ((ca = lookup_config_attribute (a->name, TRUE)) == NULL)
00290             ca = &dummy_attribute;
00291           ca->value = a->value; /* Typically &Setting.xxx */
00292           ca->type = CONFIG_Unused;
00293           switch (a->type)
00294             {
00295             case HID_Boolean:
00296               *(char *) a->value = a->default_val.int_value;
00297               ca->type = CONFIG_Boolean;
00298               break;
00299             case HID_Integer:
00300               *(int *) a->value = a->default_val.int_value;
00301               ca->type = CONFIG_Integer;
00302               break;
00303             case HID_Coord:
00304               *(Coord *) a->value = a->default_val.coord_value;
00305               ca->type = CONFIG_Coord;
00306               break;
00307             case HID_Real:
00308               *(double *) a->value = a->default_val.real_value;
00309               ca->type = CONFIG_Real;
00310               break;
00311 
00312             case HID_String:
00313               if (!a->name)
00314                 break;
00315               *(char **) a->value = g_strdup (a->default_val.str_value);
00316               ca->type = CONFIG_String;
00317 
00318               len = strlen (a->name);
00319               if (len < 7 || strstr (a->name, "color") == NULL)
00320                 break;
00321 
00322               cc = g_new0 (ConfigColor, 1);
00323               cc->attributes = a;
00324 
00325               if (!strncmp (a->name, "layer-color", 11))
00326                 cc->type = LAYER_COLOR;
00327               else if (!strncmp (a->name, "layer-selected-color", 20))
00328                 cc->type = LAYER_SELECTED_COLOR;
00329               else if (!strncmp (a->name + len - 14, "selected-color", 14))
00330                 cc->type = MISC_SELECTED_COLOR;
00331               else
00332                 cc->type = MISC_COLOR;
00333 
00334               config_color_list = g_list_append (config_color_list, cc);
00335               break;
00336 
00337             case HID_Enum:
00338             case HID_Unit:
00339               *(int *) a->value = a->default_val.int_value;
00340               break;
00341 
00342             case HID_Label:
00343             case HID_Mixed:
00344             case HID_Path:
00345               break;
00346             default:
00347               abort ();
00348             }
00349           }
00350   }
00351 }
00352 
00353 static gint
00354 parse_option_line (gchar * line, gchar ** option_result, gchar ** arg_result)
00355 {
00356   gchar *s, *ss, *option = NULL, *arg = NULL;
00357   gint argc = 1;
00358 
00359   if (option_result)
00360     *option_result = NULL;
00361   if (arg_result)
00362     *arg_result = NULL;
00363 
00364   s = line;
00365   while (*s == ' ' || *s == '\t')
00366     ++s;
00367   if (!*s || *s == '\n' || *s == '#' || *s == '[')
00368     return 0;
00369   if ((ss = strchr (s, '\n')) != NULL)
00370     *ss = '\0';
00371 
00372   if ((ss = strchr (s, ' ')) != NULL) {
00373     option = malloc(ss - s + 1);
00374     if (option == NULL)
00375       return 0;
00376     memcpy(option, s, ss - s);
00377     option[ss - s] = '\0';
00378 
00379     arg = strdup(ss + 1);
00380     if (arg == NULL) {
00381       free(option);
00382       return 0;
00383     }
00384   } else {
00385     option = strdup(s);
00386     if (option == NULL)
00387       return 0;
00388 
00389     arg = strdup("");
00390     if (arg == NULL) {
00391       free(option);
00392       return 0;
00393     }
00394   }
00395 
00396   s = option;                   /* Strip trailing ':' or '=' */
00397   while (*s && *s != ':' && *s != '=')
00398     ++s;
00399   *s = '\0';
00400 
00401   s = arg;                      /* Strip leading ':', '=', and whitespace */
00402   while (*s == ' ' || *s == '\t' || *s == ':' || *s == '=' || *s == '"')
00403     ++s;
00404   if ((ss = strchr (s, '"')) != NULL)
00405     *ss = '\0';
00406 
00407   if (option_result)
00408     *option_result = g_strdup (option);
00409   if (arg_result && *s)
00410     {
00411       *arg_result = g_strdup (s);
00412       ++argc;
00413     }
00414 
00415   free(arg);
00416   free(option);
00417   return argc;
00418 }
00419 
00420 static gboolean
00421 set_config_attribute (gchar * option, gchar * arg)
00422 {
00423   ConfigAttribute *ca;
00424 #if 0
00425   struct lconv *lc;
00426   gchar locale_point, *comma_point, *period_point;
00427 
00428   /* Until LC_NUMERIC is totally resolved, check if we need to decimal
00429      |  point convert.  Ultimately, data files will be POSIX and gui
00430      |  presentation (hence the config file reals) will be in the users locale.
00431    */
00432   lc = localeconv ();
00433   locale_point = *lc->decimal_point;
00434 #endif
00435 
00436   if ((ca = lookup_config_attribute (option, FALSE)) == NULL)
00437     return FALSE;
00438   switch (ca->type)
00439     {
00440     case CONFIG_Boolean:
00441       *(gchar *) ca->value = (gchar) atoi (arg);
00442       break;
00443 
00444     case CONFIG_Integer:
00445       *(gint *) ca->value = atoi (arg);
00446       break;
00447 
00448     case CONFIG_Real:
00449       /* Hopefully temporary locale decimal point check:
00450        */
00451 #if 0
00452       comma_point = strrchr (arg, ',');
00453       period_point = strrchr (arg, '.');
00454       if (comma_point && *comma_point != locale_point)
00455         *comma_point = locale_point;
00456       else if (period_point && *period_point != locale_point)
00457         *period_point = locale_point;
00458 #endif
00459       *(double *) ca->value = atof (arg);
00460       break;
00461 
00462     case CONFIG_String:
00463       dup_string ((gchar **) ca->value, arg ? arg : (gchar *)"");
00464       break;
00465     case CONFIG_Coord:
00466       *(Coord *) ca->value = GetValue (arg, NULL, NULL);
00467       break;
00468     default:
00469       break;
00470     }
00471   return TRUE;
00472 }
00473 
00474 static void
00475 config_file_read (void)
00476 {
00477   FILE *f;
00478   char *buf = NULL;
00479   size_t n = 0;
00480   gchar *option, *arg;
00481 
00482   if ((f = config_file_open ("r")) == NULL)
00483     return;
00484 
00485   while (getline (&buf, &n, f) != -1)
00486     {
00487       if (parse_option_line (buf, &option, &arg) > 0)
00488         set_config_attribute (option, arg);
00489       g_free (option);
00490       g_free (arg);
00491     }
00492 
00493   free (buf);
00494   fclose (f);
00495 }
00496 
00497 static void
00498 config_colors_write (gchar * path)
00499 {
00500   FILE *f;
00501   GList *list;
00502   HID_Attribute *ha;
00503   ConfigColor *cc;
00504 
00505   if ((f = fopen (path, "w")) == NULL)
00506     return;
00507   for (list = config_color_list; list; list = list->next)
00508     {
00509       cc = (ConfigColor *) list->data;
00510       ha = cc->attributes;
00511       fprintf (f, "%s =\t%s\n", ha->name, *(char **) ha->value);
00512     }
00513   fclose (f);
00514 }
00515 
00516 static gboolean
00517 config_colors_read (gchar * path)
00518 {
00519   FILE *f;
00520   GList *list;
00521   ConfigColor *cc;
00522   HID_Attribute *ha;
00523   gchar *s, buf[512], option[64], arg[512];
00524 
00525   if (!path || !*path || (f = fopen (path, "r")) == NULL)
00526     return FALSE;
00527 
00528   while (fgets (buf, sizeof (buf), f))
00529     {
00530       sscanf (buf, "%63s %511[^\n]", option, arg);
00531       s = option;               /* Strip trailing ':' or '=' */
00532       while (*s && *s != ':' && *s != '=')
00533         ++s;
00534       *s = '\0';
00535       s = arg;                  /* Strip leading ':', '=', and whitespace */
00536       while (*s == ' ' || *s == '\t' || *s == ':' || *s == '=')
00537         ++s;
00538 
00539       for (list = config_color_list; list; list = list->next)
00540         {
00541           cc = (ConfigColor *) list->data;
00542           ha = cc->attributes;
00543           if (!strcmp (option, ha->name))
00544             {
00545               *(char **) ha->value = g_strdup (s);
00546               cc->color_is_mapped = FALSE;
00547               ghid_set_special_colors (ha);
00548               break;
00549             }
00550         }
00551     }
00552   fclose (f);
00553 
00554   return TRUE;
00555 }
00556 
00557 static gchar *
00558 expand_dir (gchar * dir)
00559 {
00560   gchar *s;
00561 
00562   if (*dir == '~')
00563     s = g_build_filename ((gchar *) g_get_home_dir (), dir + 1, NULL);
00564   else
00565     s = g_strdup (dir);
00566   return s;
00567 }
00568 
00569 static void
00570 add_to_paths_list (GList ** list, gchar * path_string)
00571 {
00572   gchar *p, *paths;
00573 
00574   paths = g_strdup (path_string);
00575   for (p = strtok (paths, PCB_PATH_DELIMETER); p && *p; p = strtok (NULL, PCB_PATH_DELIMETER))
00576     *list = g_list_prepend (*list, expand_dir (p));
00577   g_free (paths);
00578 }
00579 
00580   /* Parse command line code borrowed from hid/common/hidinit.c
00581    */
00582 static void
00583 parse_optionv (gint * argc, gchar *** argv, gboolean from_cmd_line)
00584 {
00585   HID_AttrNode *ha;
00586   HID_Attribute *a;
00587   const Unit *unit;
00588   gchar *ep;
00589   gint e, ok, offset;
00590   gboolean matched = FALSE;
00591 
00592   offset = from_cmd_line ? 2 : 0;
00593 
00594   while (*argc
00595          && (((*argv)[0][0] == '-' && (*argv)[0][1] == '-')
00596              || !from_cmd_line))
00597     {
00598       for (ha = hid_attr_nodes; ha; ha = ha->next)
00599         {
00600           for (a = ha->attributes; a < ha->attributes + ha->n; ++a)
00601             {
00602               if (!a->name || strcmp ((*argv)[0] + offset, a->name))
00603                 continue;
00604               switch (a->type)
00605                 {
00606                 case HID_Label:
00607                   break;
00608                 case HID_Integer:
00609                   if (a->value)
00610                     *(int *) a->value = strtol ((*argv)[1], 0, 0);
00611                   else
00612                     a->default_val.int_value = strtol ((*argv)[1], 0, 0);
00613                   (*argc)--;
00614                   (*argv)++;
00615                   break;
00616                 case HID_Coord:
00617                   if (a->value)
00618                     *(Coord *) a->value = GetValue ((*argv)[1], 0, 0);
00619                   else
00620                     a->default_val.coord_value = GetValue ((*argv)[1], 0, 0);
00621                   (*argc)--;
00622                   (*argv)++;
00623                   break;
00624                 case HID_Real:
00625                   if (a->value)
00626                     *(double *) a->value = strtod ((*argv)[1], 0);
00627                   else
00628                     a->default_val.real_value = strtod ((*argv)[1], 0);
00629                   (*argc)--;
00630                   (*argv)++;
00631                   break;
00632                 case HID_String:
00633                   if (a->value)
00634                     *(char **) a->value = g_strdup((*argv)[1]);
00635                   else
00636                     a->default_val.str_value = g_strdup((*argv)[1]);
00637                   (*argc)--;
00638                   (*argv)++;
00639                   break;
00640                 case HID_Boolean:
00641                   if (a->value)
00642                     *(char *) a->value = 1;
00643                   else
00644                     a->default_val.int_value = 1;
00645                   break;
00646                 case HID_Mixed:
00647                   a->default_val.real_value = strtod ((*argv)[1], &ep);
00648                   goto do_enum;
00649                 case HID_Enum:
00650                   ep = (*argv)[1];
00651                 do_enum:
00652                   ok = 0;
00653                   for (e = 0; a->enumerations[e]; e++)
00654                     if (strcmp (a->enumerations[e], ep) == 0)
00655                       {
00656                         ok = 1;
00657                         a->default_val.int_value = e;
00658                         a->default_val.str_value = ep;
00659                         break;
00660                       }
00661                   if (!ok)
00662                     {
00663                       fprintf (stderr,
00664                                "ERROR:  \"%s\" is an unknown value for the --%s option\n",
00665                                (*argv)[1], a->name);
00666                       exit (1);
00667                     }
00668                   (*argc)--;
00669                   (*argv)++;
00670                   break;
00671                 case HID_Path:
00672                   abort ();
00673                   a->default_val.str_value = (*argv)[1];
00674                   (*argc)--;
00675                   (*argv)++;
00676                   break;
00677                 case HID_Unit:
00678                   unit = get_unit_struct ((*argv)[1]);
00679                   if (unit == NULL)
00680                     {
00681                       fprintf (stderr,
00682                                "ERROR:  unit \"%s\" is unknown to pcb (option --%s)\n",
00683                                (*argv)[1], a->name);
00684                       exit (1);
00685                     }
00686                   a->default_val.int_value = unit->index;
00687                   a->default_val.str_value = unit->suffix;
00688                   (*argc)--;
00689                   (*argv)++;
00690                   break;
00691                 }
00692               (*argc)--;
00693               (*argv)++;
00694               ha = 0;
00695               goto got_match;
00696             }
00697           if (a < ha->attributes + ha->n)
00698             matched = TRUE;
00699         }
00700       if (!matched)
00701         {
00702           if (from_cmd_line)
00703             {
00704               fprintf (stderr, "unrecognized option: %s\n", (*argv)[0]);
00705               exit (1);
00706             }
00707           else
00708 //                              ghid_log("unrecognized option: %s\n", (*argv)[0]);
00709             fprintf (stderr, "unrecognized option: %s\n", (*argv)[0]);
00710         }
00711     got_match:;
00712     }
00713   (*argc)++;
00714   (*argv)--;
00715 }
00716 
00717 static void
00718 load_rc_file (gchar * path)
00719 {
00720   FILE *f;
00721   gchar buf[1024], *av[2], **argv;
00722   gint argc;
00723 
00724   f = fopen (path, "r");
00725   if (!f)
00726     return;
00727 
00728   if (Settings.verbose)
00729     printf ("Loading pcbrc file: %s\n", path);
00730   while (fgets (buf, sizeof (buf), f))
00731     {
00732       argv = &(av[0]);
00733       if ((argc = parse_option_line (buf, &av[0], &av[1])) > 0)
00734         parse_optionv (&argc, &argv, FALSE);
00735       g_free (av[0]);
00736       g_free (av[1]);
00737     }
00738   fclose (f);
00739 }
00740 
00741 static void
00742 load_rc_files (void)
00743 {
00744   gchar *path;
00745 
00746   load_rc_file ("/etc/pcbrc");
00747   load_rc_file ("/usr/local/etc/pcbrc");
00748 
00749   path = g_build_filename (pcblibdir, "pcbrc", NULL);
00750   load_rc_file (path);
00751   g_free (path);
00752 
00753   path = g_build_filename ((gchar *) g_get_home_dir (), ".pcb/pcbrc", NULL);
00754   load_rc_file (path);
00755   g_free (path);
00756 
00757   load_rc_file ("pcbrc");
00758 }
00759 
00760 
00761 void
00762 ghid_config_files_read (gint * argc, gchar *** argv)
00763 {
00764   GList *list;
00765   gchar *str, *dir;
00766   gint width, height;
00767 
00768   ghidgui = &_ghidgui;
00769 
00770   ghid_config_init ();
00771   load_rc_files ();
00772   config_file_read ();
00773   config_colors_read (color_file);
00774   (*argc)--;
00775   (*argv)++;
00776   parse_optionv (argc, argv, TRUE);
00777 
00778   if (board_size_override
00779       && sscanf (board_size_override, "%dx%d", &width, &height) == 2)
00780     {
00781       Settings.MaxWidth = TO_PCB_UNITS (width);
00782       Settings.MaxHeight = TO_PCB_UNITS (height);
00783     }
00784 
00785   if (lib_newlib_config && *lib_newlib_config)
00786     add_to_paths_list (&lib_newlib_list, lib_newlib_config);
00787 
00788   for (list = lib_newlib_list; list; list = list->next)
00789     {
00790       str = Settings.LibraryTree;
00791       dir = expand_dir ((gchar *) list->data);
00792       Settings.LibraryTree = g_strconcat (str, PCB_PATH_DELIMETER, dir, NULL);
00793       g_free (dir);
00794       g_free (str);
00795     }
00796 }
00797 
00798 void
00799 ghid_config_files_write (void)
00800 {
00801   FILE *f;
00802   ConfigAttribute *ca;
00803 
00804   if (!ghidgui->config_modified || (f = config_file_open ("w")) == NULL)
00805     return;
00806 
00807   fprintf (f, "### PCB configuration file. ###\n");
00808 
00809   for (ca = &config_attributes[0];
00810        ca < &config_attributes[0] + G_N_ELEMENTS (config_attributes); ++ca)
00811     {
00812       switch (ca->type)
00813         {
00814         case CONFIG_Boolean:
00815           fprintf (f, "%s = %d\n", ca->name, (gint) * (gchar *) ca->value);
00816           break;
00817 
00818         case CONFIG_Integer:
00819           fprintf (f, "%s = %d\n", ca->name, *(gint *) ca->value);
00820           break;
00821 
00822         case CONFIG_Real:
00823           fprintf (f, "%s = %f\n", ca->name, *(double *) ca->value);
00824           break;
00825 
00826         case CONFIG_Coord:
00827           pcb_fprintf (f, "%s = %$mS\n", ca->name, *(Coord *) ca->value);
00828           break;
00829 
00830         case CONFIG_String:
00831           if (*(char **) ca->value == NULL)
00832             fprintf (f, "# %s = NULL\n", ca->name);
00833           else
00834             fprintf (f, "%s = %s\n", ca->name, *(char **) ca->value);
00835           break;
00836         default:
00837           break;
00838         }
00839     }
00840   fclose (f);
00841 
00842   ghidgui->config_modified = FALSE;
00843 }
00844 
00845 /* =================== OK, now the gui stuff ======================
00846 */
00847 static GtkWidget *config_window;
00848 
00849   /* -------------- The General config page ----------------
00850    */
00851 
00852 static void
00853 config_command_window_toggle_cb (GtkToggleButton * button, gpointer data)
00854 {
00855   gboolean active = gtk_toggle_button_get_active (button);
00856   static gboolean holdoff;
00857 
00858   if (holdoff)
00859     return;
00860 
00861   /* Can't toggle into command window mode if the status line command
00862      |  entry is active.
00863    */
00864   if (ghidgui->command_entry_status_line_active)
00865     {
00866       holdoff = TRUE;
00867       gtk_toggle_button_set_active (button, FALSE);
00868       holdoff = FALSE;
00869       return;
00870     }
00871   ghidgui->use_command_window = active;
00872   ghid_command_use_command_window_sync ();
00873 }
00874 
00875 
00876 static void
00877 config_compact_horizontal_toggle_cb (GtkToggleButton * button, gpointer data)
00878 {
00879   gboolean active = gtk_toggle_button_get_active (button);
00880 
00881   ghidgui->compact_horizontal = active;
00882   ghid_set_status_line_label ();
00883   ghidgui->config_modified = TRUE;
00884 }
00885 
00886 static void
00887 config_compact_vertical_toggle_cb (GtkToggleButton * button, gpointer data)
00888 {
00889   gboolean active = gtk_toggle_button_get_active (button);
00890 
00891   ghidgui->compact_vertical = active;
00892   ghid_pack_mode_buttons();
00893   ghidgui->config_modified = TRUE;
00894 }
00895 
00896 static void
00897 config_general_toggle_cb (GtkToggleButton * button, void * setting)
00898 {
00899   *(gint *)setting = gtk_toggle_button_get_active (button);
00900   ghidgui->config_modified = TRUE;
00901 }
00902 
00903 static void
00904 config_backup_spin_button_cb (GtkSpinButton * spin_button, gpointer data)
00905 {
00906   Settings.BackupInterval = gtk_spin_button_get_value_as_int (spin_button);
00907   EnableAutosave ();
00908   ghidgui->config_modified = TRUE;
00909 }
00910 
00911 static void
00912 config_history_spin_button_cb (GtkSpinButton * spin_button, gpointer data)
00913 {
00914   ghidgui->history_size = gtk_spin_button_get_value_as_int (spin_button);
00915   ghidgui->config_modified = TRUE;
00916 }
00917 
00918 static void
00919 config_general_tab_create (GtkWidget * tab_vbox)
00920 {
00921   GtkWidget *vbox;
00922 
00923   gtk_container_set_border_width (GTK_CONTAINER (tab_vbox), 6);
00924 
00925   vbox = ghid_category_vbox (tab_vbox, _("Enables"), 4, 2, TRUE, TRUE);
00926 
00927   ghid_check_button_connected (vbox, NULL, ghidgui->use_command_window,
00928                                TRUE, FALSE, FALSE, 2,
00929                                config_command_window_toggle_cb, NULL,
00930                                _("Use separate window for command entry"));
00931 
00932   ghid_check_button_connected (vbox, NULL, ghidgui->compact_horizontal,
00933                                TRUE, FALSE, FALSE, 2,
00934                                config_compact_horizontal_toggle_cb, NULL,
00935                                _("Alternate window layout to allow smaller horizontal size"));
00936 
00937   ghid_check_button_connected (vbox, NULL, ghidgui->compact_vertical,
00938                                TRUE, FALSE, FALSE, 2,
00939                                config_compact_vertical_toggle_cb, NULL,
00940                                _("Alternate window layout to allow smaller vertical size"));
00941 
00942   vbox = ghid_category_vbox (tab_vbox, _("Backups"), 4, 2, TRUE, TRUE);
00943   ghid_check_button_connected (vbox, NULL, Settings.SaveInTMP,
00944                                TRUE, FALSE, FALSE, 2,
00945                                config_general_toggle_cb, &Settings.SaveInTMP,
00946                                _("If layout is modified at exit, save into PCB.%i.save"));
00947   ghid_spin_button (vbox, NULL, Settings.BackupInterval, 0.0, 60 * 60, 60.0,
00948                     600.0, 0, 0, config_backup_spin_button_cb, NULL, FALSE,
00949                     _("Seconds between auto backups\n"
00950                       "(set to zero to disable auto backups)"));
00951 
00952   vbox = ghid_category_vbox (tab_vbox, _("Misc"), 4, 2, TRUE, TRUE);
00953   ghid_spin_button (vbox, NULL, ghidgui->history_size,
00954                     5.0, 25.0, 1.0, 1.0, 0, 0,
00955                     config_history_spin_button_cb, NULL, FALSE,
00956                     _("Number of commands to remember in the history list"));
00957 
00958   ghid_check_button_connected (vbox, NULL, Settings.SaveMetricOnly,
00959                                TRUE, FALSE, FALSE, 2,
00960                                config_general_toggle_cb, &Settings.SaveMetricOnly,
00961                                _("Use only metric units when saving pcb files"));
00962 }
00963 
00964 
00965 static void
00966 config_general_apply (void)
00967 {
00968   /* save the settings */
00969   ghid_config_files_write ();
00970 }
00971 
00972 
00973   /* -------------- The Sizes config page ----------------
00974    */
00975 
00976 static GtkWidget *config_sizes_vbox,
00977   *config_sizes_tab_vbox, *config_text_spin_button;
00978 
00979 static GtkWidget *use_board_size_default_button,
00980   *use_drc_sizes_default_button,
00981   *use_increments_default_button;
00982 
00983 static Coord new_board_width, new_board_height;
00984 
00985 static void
00986 config_sizes_apply (void)
00987 {
00988   gboolean active;
00989 
00990   active =
00991     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
00992                                   (use_board_size_default_button));
00993   if (active)
00994     {
00995       Settings.MaxWidth = new_board_width;
00996       Settings.MaxHeight = new_board_height;
00997       ghidgui->config_modified = TRUE;
00998     }
00999 
01000   active =
01001     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
01002                                   (use_drc_sizes_default_button));
01003   if (active)
01004     {
01005       Settings.Bloat = PCB->Bloat;
01006       Settings.Shrink = PCB->Shrink;
01007       Settings.minWid = PCB->minWid;
01008       Settings.minSlk = PCB->minSlk;
01009       Settings.IsleArea = PCB->IsleArea;
01010       Settings.minDrill = PCB->minDrill;
01011       Settings.minRing = PCB->minRing;
01012       ghidgui->config_modified = TRUE;
01013     }
01014 
01015   active =
01016     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
01017                                   (use_increments_default_button));
01018   if (active)
01019     {
01020       save_increments (get_increments_struct (METRIC),
01021                        get_increments_struct (IMPERIAL));
01022       ghidgui->config_modified = TRUE;
01023     }
01024 
01025   if (PCB->MaxWidth != new_board_width || PCB->MaxHeight != new_board_height)
01026     ChangePCBSize (new_board_width, new_board_height);
01027 }
01028 
01029 static void
01030 text_spin_button_cb (GtkSpinButton * spin, void * dst)
01031 {
01032   *(gint *)dst = gtk_spin_button_get_value_as_int (spin);
01033   ghidgui->config_modified = TRUE;
01034   ghid_set_status_line_label ();
01035 }
01036 
01037 static void
01038 coord_entry_cb (GHidCoordEntry * ce, void * dst)
01039 {
01040   *(Coord *) dst = ghid_coord_entry_get_value (ce);
01041   ghidgui->config_modified = TRUE;
01042 }
01043 
01044 static void
01045 config_sizes_tab_create (GtkWidget * tab_vbox)
01046 {
01047   GtkWidget *table, *vbox, *hbox;
01048 
01049   /* Need a vbox we can destroy if user changes grid units.
01050    */
01051   if (!config_sizes_vbox)
01052     {
01053       vbox = gtk_vbox_new (FALSE, 0);
01054       gtk_box_pack_start (GTK_BOX (tab_vbox), vbox, FALSE, FALSE, 0);
01055       gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
01056       config_sizes_vbox = vbox;
01057       config_sizes_tab_vbox = tab_vbox;
01058     }
01059 
01060   /* ---- Board Size ---- */
01061   vbox = ghid_category_vbox (config_sizes_vbox, _("Board Size"),
01062                              4, 2, TRUE, TRUE);
01063   hbox = gtk_hbox_new (FALSE, 0);
01064   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
01065   table = gtk_table_new (2, 2, FALSE);
01066   gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
01067   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
01068   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
01069 
01070   new_board_width = PCB->MaxWidth;
01071   new_board_height = PCB->MaxHeight;
01072   ghid_table_coord_entry (table, 0, 0, NULL,
01073                           PCB->MaxWidth, MIN_SIZE, MAX_COORD,
01074                           CE_LARGE, 0, coord_entry_cb,
01075                           &new_board_width, FALSE, _("Width"));
01076 
01077   ghid_table_coord_entry (table, 1, 0, NULL,
01078                           PCB->MaxHeight, MIN_SIZE, MAX_COORD,
01079                           CE_LARGE, 0, coord_entry_cb,
01080                           &new_board_height, FALSE, _("Height"));
01081   ghid_check_button_connected (vbox, &use_board_size_default_button, FALSE,
01082                                TRUE, FALSE, FALSE, 0, NULL, NULL,
01083                                _("Use this board size as the default for new layouts"));
01084 
01085   /* ---- Text Scale ---- */
01086   vbox = ghid_category_vbox (config_sizes_vbox, _("Text Scale"),
01087                              4, 2, TRUE, TRUE);
01088   hbox = gtk_hbox_new (FALSE, 0);
01089   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
01090   table = gtk_table_new (4, 2, FALSE);
01091   gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
01092   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
01093   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
01094 
01095   ghid_table_spin_button (table, 0, 0, &config_text_spin_button,
01096                           Settings.TextScale,
01097                           MIN_TEXTSCALE, MAX_TEXTSCALE,
01098                           10.0, 10.0,
01099                           0, 0, text_spin_button_cb,
01100                           &Settings.TextScale, FALSE, "%");
01101 
01102 
01103   /* ---- DRC Sizes ---- */
01104   vbox = ghid_category_vbox (config_sizes_vbox, _("Design Rule Checking"),
01105                              4, 2, TRUE, TRUE);
01106   hbox = gtk_hbox_new (FALSE, 0);
01107   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
01108   table = gtk_table_new (4, 2, FALSE);
01109   gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
01110   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
01111   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
01112 
01113   ghid_table_coord_entry (table, 0, 0, NULL,
01114                           PCB->Bloat, MIN_DRC_VALUE, MAX_DRC_VALUE,
01115                           CE_SMALL, 0, coord_entry_cb,
01116                           &PCB->Bloat, FALSE,
01117                           _("Minimum copper spacing"));
01118 
01119   ghid_table_coord_entry (table, 1, 0, NULL,
01120                           PCB->minWid, MIN_DRC_VALUE, MAX_DRC_VALUE,
01121                           CE_SMALL, 0, coord_entry_cb,
01122                           &PCB->minWid, FALSE,
01123                           _("Minimum copper width"));
01124 
01125   ghid_table_coord_entry (table, 2, 0, NULL,
01126                           PCB->Shrink, MIN_DRC_VALUE, MAX_DRC_VALUE,
01127                           CE_SMALL, 0, coord_entry_cb,
01128                           &PCB->Shrink, FALSE,
01129                           _("Minimum touching copper overlap"));
01130 
01131   ghid_table_coord_entry (table, 3, 0, NULL,
01132                           PCB->minSlk, MIN_DRC_VALUE, MAX_DRC_VALUE,
01133                           CE_SMALL, 0, coord_entry_cb,
01134                           &PCB->minSlk, FALSE,
01135                           _("Minimum silk width"));
01136 
01137   ghid_table_coord_entry (table, 4, 0, NULL,
01138                           PCB->minDrill, MIN_DRC_VALUE, MAX_DRC_VALUE,
01139                           CE_SMALL, 0, coord_entry_cb,
01140                           &PCB->minDrill, FALSE,
01141                           _("Minimum drill diameter"));
01142 
01143   ghid_table_coord_entry (table, 5, 0, NULL,
01144                           PCB->minRing, MIN_DRC_VALUE, MAX_DRC_VALUE,
01145                           CE_SMALL, 0, coord_entry_cb,
01146                           &PCB->minRing, FALSE,
01147                           _("Minimum annular ring"));
01148 
01149   ghid_check_button_connected (vbox, &use_drc_sizes_default_button, FALSE,
01150                                TRUE, FALSE, FALSE, 0, NULL, NULL,
01151                                _
01152                                ("Use DRC values as the default for new layouts"));
01153 
01154   gtk_widget_show_all (config_sizes_vbox);
01155 }
01156 
01157 
01158   /* -------------- The Increments config page ----------------
01159    */
01160   /* Increment/decrement values are kept in mil and mm units and not in
01161      |  PCB units.
01162    */
01163 static GtkWidget *config_increments_vbox, *config_increments_tab_vbox;
01164 
01165 static void
01166 increment_spin_button_cb (GHidCoordEntry * ce, void * dst)
01167 {
01168   *(Coord *)dst = ghid_coord_entry_get_value (ce);
01169   ghidgui->config_modified = TRUE;
01170 }
01171 
01172 static void
01173 config_increments_tab_create (GtkWidget * tab_vbox)
01174 {
01175   Increments *incr_mm  = get_increments_struct (METRIC);
01176   Increments *incr_mil = get_increments_struct (IMPERIAL);
01177   GtkWidget *vbox, *hbox, *table;
01178 
01179   /* Need a vbox we can destroy if user changes grid units.
01180    */
01181   if (!config_increments_vbox)
01182     {
01183       vbox = gtk_vbox_new (FALSE, 0);
01184       gtk_box_pack_start (GTK_BOX (tab_vbox), vbox, FALSE, FALSE, 0);
01185       gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
01186       config_increments_vbox = vbox;
01187       config_increments_tab_vbox = tab_vbox;
01188     }
01189 
01190 #define INCR_ENTRY(row, name, family, type, msg)        \
01191   gtk_table_attach_defaults (GTK_TABLE (table), \
01192                              gtk_label_new (name),      \
01193                              0, 1, row, row + 1);       \
01194   ghid_table_coord_entry (table, row, 1, NULL,  \
01195                           incr_##family->type,  \
01196                           incr_##family->type##_min,    \
01197                           incr_##family->type##_max,    \
01198                           CE_SMALL, 0, increment_spin_button_cb,        \
01199                           &incr_##family->type, FALSE,  \
01200                           msg)
01201   
01202   /* ---- Metric Settings ---- */
01203   vbox = ghid_category_vbox (config_increments_vbox,
01204                              _("Metric Increment Settings"), 4, 2, TRUE, TRUE);
01205   hbox = gtk_hbox_new (FALSE, 0);
01206   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
01207   table = gtk_table_new (4, 3, FALSE);
01208   gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
01209   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
01210   gtk_table_set_row_spacings (GTK_TABLE (table), 18);
01211 
01212   INCR_ENTRY (0, _("Grid:"), mm, grid,
01213               _("For 'g' and '<shift>g' grid change actions"));
01214   INCR_ENTRY (1, _("Size:"), mm, size,
01215               _("For 's' and '<shift>s' size change actions on lines,\n"
01216                 "pads, pins and text.\n"
01217                 "Use '<ctrl>s' and '<shift><ctrl>s' for drill holes."));
01218   INCR_ENTRY (2, _("Line:"), mm, line,
01219               _("For 'l' and '<shift>l' routing line width change actions"));
01220   INCR_ENTRY (3, _("Clear:"), mm, clear,
01221               _("For 'k' and '<shift>k' line clearance inside polygon size\n"
01222                 "change actions"));
01223 
01224   vbox = ghid_category_vbox (config_increments_vbox,
01225                              _("Imperial Increment Settings"), 4, 2, TRUE, TRUE);
01226   hbox = gtk_hbox_new (FALSE, 0);
01227   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
01228   table = gtk_table_new (4, 3, FALSE);
01229   gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
01230   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
01231   gtk_table_set_row_spacings (GTK_TABLE (table), 18);
01232 
01233   INCR_ENTRY (0, _("Grid:"), mil, grid,
01234               _("For 'g' and '<shift>g' grid change actions"));
01235   INCR_ENTRY (1, _("Size:"), mil, size,
01236               _("For 's' and '<shift>s' size change actions on lines,\n"
01237                 "pads, pins and text.\n"
01238                 "Use '<ctrl>s' and '<shift><ctrl>s' for drill holes."));
01239   INCR_ENTRY (2, _("Line:"), mil, line,
01240               _("For 'l' and '<shift>l' routing line width change actions"));
01241   INCR_ENTRY (3, _("Clear:"), mil, clear,
01242               _("For 'k' and '<shift>k' line clearance inside polygon size\n"
01243                 "change actions"));
01244 #undef INCR_ENTRY
01245 
01246   vbox = ghid_category_vbox (config_increments_vbox,
01247                              _("Save as Default"), 4, 2, TRUE, TRUE);
01248   ghid_check_button_connected (vbox, &use_increments_default_button, FALSE,
01249                                TRUE, FALSE, FALSE, 0, NULL, NULL,
01250                                _("Use values as the default for new layouts"));
01251 
01252   gtk_widget_show_all (config_increments_vbox);
01253 }
01254 
01255   /* -------------- The Library config page ----------------
01256    */
01257 static GtkWidget *library_newlib_entry;
01258 
01259 static void
01260 config_library_apply (void)
01261 {
01262   if (dup_string
01263       (&lib_newlib_config, ghid_entry_get_text (library_newlib_entry)))
01264     ghidgui->config_modified = TRUE;
01265 }
01266 
01267 static void
01268 config_library_tab_create (GtkWidget * tab_vbox)
01269 {
01270   GtkWidget *vbox, *label, *entry;
01271   GString *string;
01272 
01273   gtk_container_set_border_width (GTK_CONTAINER (tab_vbox), 6);
01274   vbox = ghid_category_vbox (tab_vbox, _("Element Directories"),
01275                              4, 2, TRUE, TRUE);
01276   string = g_string_new ("");
01277   g_string_printf (string, _("<small>Enter a \"%s\" "
01278                          "separated list of custom top level\n"
01279                          "element directories.  For example:\n%s\n"
01280                          "Elements should be organized into subdirectories below each\n"
01281                          "top level directory.  Restart program for changes to take effect."
01282                          "</small>"), PCB_PATH_DELIMETER,
01283                          "\t<b>~/gaf/pcb-elements" PCB_PATH_DELIMETER
01284                          "packages" PCB_PATH_DELIMETER "/usr/local/pcb-elements</b>\n");
01285   label = gtk_label_new ("");
01286   gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
01287   gtk_label_set_markup (GTK_LABEL (label), string->str);
01288   g_string_free (string, TRUE);
01289   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
01290   entry = gtk_entry_new ();
01291   library_newlib_entry = entry;
01292   gtk_entry_set_text (GTK_ENTRY (entry), lib_newlib_config);
01293   gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 4);
01294 }
01295 
01296 
01297   /* -------------- The Layers Group config page ----------------
01298    */
01299 static GtkWidget        *config_groups_table, *config_groups_vbox, *config_groups_window;
01300 
01301 static GtkWidget *layer_entry[MAX_LAYER];
01302 static GtkWidget *group_button[MAX_ALL_LAYER][MAX_GROUP];
01303 
01304 #if FIXME
01305 static GtkWidget *use_layer_default_button;
01306 #endif
01307 
01308 static gint config_layer_group[MAX_ALL_LAYER];
01309 
01310 static LayerGroupType layer_groups,     /* Working copy */
01311  *lg_monitor;                   /* Keep track if our working copy */
01312                                                                                 /* needs to be changed (new layout) */
01313 
01314 static gboolean groups_modified, groups_holdoff, layers_applying;
01315 
01316 static gchar *layer_info_text[] = {
01317   N_("<h>Layer Names\n"),
01318   N_("You may enter layer names for the layers drawn on the screen.\n"
01319      "The special 'top side' and 'bottom side' are layers which\n"
01320      "will be printed out, so they must have in their group at least one\n"
01321      "of the other layers that are drawn on the screen.\n"),
01322   "\n",
01323   N_("<h>Layer Groups\n"),
01324   N_("Each layer on the screen may be in its own group which allows the\n"
01325      "maximum number of board layers.  However, for boards with fewer\n"
01326      "layers, you may group layers together which will then print as a\n"
01327      "single layer on a printout.  This allows a visual color distinction\n"
01328      "to be displayed on the screen for signal groups which will print as\n"
01329      "a single layer\n"),
01330   "\n",
01331   N_("For example, for a 4 layer board a useful layer group arrangement\n"
01332      "can be to have 3 screen displayed layers grouped into the same group\n"
01333      "as the 'top side' and 'bottom side' printout layers.  Then\n"
01334      "groups such as signals, ground, and supply traces can be color\n"
01335      "coded on the screen while printing as a single layer.  For this\n"
01336      "you would select buttons and enter names on the Setup page to\n"
01337      "structure four layer groups similar to this:\n"),
01338   "\n",
01339   N_("<b>Group 1:"),
01340   "\n\t",
01341   N_("top"),
01342   "\n\t",
01343   N_("GND-top"),
01344   "\n\t",
01345   N_("Vcc-top"),
01346   "\n\t",
01347   N_("top side"),
01348   "\n",
01349   N_("<b>Group 2:"),
01350   "\n\t",
01351   N_("bottom"),
01352   "\n\t",
01353   N_("GND-bottom"),
01354   "\n\t",
01355   N_("Vcc-bottom"),
01356   "\n\t",
01357   N_("bottom side"),
01358   "\n",
01359   N_("<b>Group 3:"),
01360   "\n\t",
01361   N_("signal1"),
01362   "\n",
01363   N_("<b>Group 4:"),
01364   "\n\t",
01365   N_("signal2"),
01366   "\n"
01367 };
01368 
01369 static void
01370 config_layer_groups_radio_button_cb (GtkToggleButton * button, gpointer data)
01371 {
01372   gint layer = GPOINTER_TO_INT (data) >> 8;
01373   gint group = GPOINTER_TO_INT (data) & 0xff;
01374 
01375   if (!gtk_toggle_button_get_active (button) || groups_holdoff)
01376     return;
01377   config_layer_group[layer] = group;
01378   groups_modified = TRUE;
01379   ghidgui->config_modified = TRUE;
01380 }
01381 
01382   /* Construct a layer group string.  Follow logic in WritePCBDataHeader(),
01383      |  but use g_string functions.
01384    */
01385 static gchar *
01386 make_layer_group_string (LayerGroupType * lg)
01387 {
01388   GString *string;
01389   gint group, entry, layer;
01390 
01391   string = g_string_new ("");
01392 
01393   for (group = 0; group < max_group; group++)
01394     {
01395       if (lg->Number[group] == 0)
01396         continue;
01397       for (entry = 0; entry < lg->Number[group]; entry++)
01398         {
01399           layer = lg->Entries[group][entry];
01400           if (layer == top_silk_layer)
01401             string = g_string_append (string, "c");
01402           else if (layer == bottom_silk_layer)
01403             string = g_string_append (string, "s");
01404           else
01405             g_string_append_printf (string, "%d", layer + 1);
01406 
01407           if (entry != lg->Number[group] - 1)
01408             string = g_string_append (string, ",");
01409         }
01410       if (group != max_group - 1)
01411         string = g_string_append (string, ":");
01412     }
01413   return g_string_free (string, FALSE); /* Don't free string->str */
01414 }
01415 
01416 static void
01417 config_layers_apply (void)
01418 {
01419   LayerType *layer;
01420   gchar *s;
01421   gint group, i;
01422   gint top_group = 0, bottom_group = 0;
01423   gboolean use_as_default = FALSE, layers_modified = FALSE;
01424 
01425 #if FIXME
01426   use_as_default =
01427     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
01428                                   (use_layer_default_button));
01429 #endif
01430 
01431   /* Get each layer name entry and dup if modified into the PCB layer names
01432      |  and, if to use as default, the Settings layer names.
01433    */
01434   for (i = 0; i < max_copper_layer; ++i)
01435     {
01436       layer = &PCB->Data->Layer[i];
01437       s = ghid_entry_get_text (layer_entry[i]);
01438       if (dup_core_string (&layer->Name, s))
01439         layers_modified = TRUE;
01440 /* FIXME */
01441       if (use_as_default && dup_core_string (&Settings.DefaultLayerName[i], s))
01442         ghidgui->config_modified = TRUE;
01443 
01444     }
01445   /* Layer names can be changed from the menus and that can update the
01446      |  config.  So holdoff the loop.
01447    */
01448   layers_applying = TRUE;
01449   if (layers_modified)
01450     ghid_layer_buttons_update ();
01451   layers_applying = FALSE;
01452 
01453   if (groups_modified)          /* If any group radio buttons were toggled. */
01454     {
01455       /* clear all entries and read layer by layer
01456        */
01457       for (group = 0; group < max_group; group++)
01458         layer_groups.Number[group] = 0;
01459 
01460       for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
01461         {
01462           group = config_layer_group[i] - 1;
01463           layer_groups.Entries[group][layer_groups.Number[group]++] = i;
01464 
01465           if (i == top_silk_layer)
01466             top_group = group;
01467           else if (i == bottom_silk_layer)
01468             bottom_group = group;
01469         }
01470 
01471       /* do some cross-checking
01472          |  top-side and bottom-side must be in different groups
01473          |  top-side and bottom-side must not be the only one in the group
01474        */
01475       if (layer_groups.Number[bottom_group] <= 1
01476           || layer_groups.Number[top_group] <= 1)
01477         {
01478           Message (_
01479                    ("Both, 'top side' and 'bottom side' layer must have at least\n"
01480                     "\tone other layer in their group.\n"));
01481           return;
01482         }
01483       else if (bottom_group == top_group)
01484         {
01485           Message (_
01486                    ("The 'top side' and 'bottom side' layers are not allowed\n"
01487                     "\tto be in the same layer group #\n"));
01488           return;
01489         }
01490       PCB->LayerGroups = layer_groups;
01491       ghid_invalidate_all();
01492       groups_modified = FALSE;
01493     }
01494   if (use_as_default)
01495     {
01496       s = make_layer_group_string (&PCB->LayerGroups);
01497       if (dup_core_string (&Settings.Groups, s))
01498         {
01499           ParseGroupString (Settings.Groups, &Settings.LayerGroups, &max_copper_layer);
01500           ghidgui->config_modified = TRUE;
01501         }
01502       g_free (s);
01503     }
01504 }
01505 
01506 static void
01507 config_layer_group_button_state_update (void)
01508 {
01509   gint g, i;
01510 
01511   /* Set button active corresponding to layer group state.
01512    */
01513   groups_holdoff = TRUE;
01514   for (g = 0; g < max_group; g++)
01515     for (i = 0; i < layer_groups.Number[g]; i++)
01516       {
01517 /*                      printf("layer %d in group %d\n", layer_groups.Entries[g][i], g +1); */
01518         config_layer_group[layer_groups.Entries[g][i]] = g + 1;
01519         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
01520                                       (group_button
01521                                        [layer_groups.Entries[g][i]][g]),
01522                                       TRUE);
01523       }
01524   groups_holdoff = FALSE;
01525 }
01526 
01527 static void
01528 layer_name_entry_cb(GtkWidget *entry, gpointer data)
01529 {
01530         gint            i = GPOINTER_TO_INT(data);
01531         LayerType       *layer;
01532         gchar           *name;
01533 
01534         layer = &PCB->Data->Layer[i];
01535         name = ghid_entry_get_text(entry);
01536         if (dup_core_string (&layer->Name, name))
01537                 ghid_layer_buttons_update();
01538 }
01539 
01540 void
01541 ghid_config_groups_changed(void)
01542 {
01543   GtkWidget *vbox, *table, *button, *label, *scrolled_window;
01544   GSList *group;
01545   gchar buf[32], *name;
01546   gint layer, row, i;
01547 
01548   if (!config_groups_vbox)
01549         return;
01550   vbox = config_groups_vbox;
01551 
01552   if (config_groups_table)
01553         gtk_widget_destroy(config_groups_table);
01554   if (config_groups_window)
01555     gtk_widget_destroy(config_groups_window);
01556 
01557   config_groups_window = scrolled_window = 
01558     gtk_scrolled_window_new (NULL, NULL);
01559   gtk_widget_set_size_request (scrolled_window, 34, 408);
01560   gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 3);
01561   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
01562                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
01563   gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
01564   gtk_widget_show (scrolled_window);
01565 
01566 
01567   table = gtk_table_new (max_copper_layer + SILK_LAYER + 1,
01568                          max_group + 1, FALSE);
01569   config_groups_table = table;
01570   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
01571   gtk_scrolled_window_add_with_viewport (
01572         GTK_SCROLLED_WINDOW (scrolled_window), table);
01573   gtk_widget_show (table);
01574 
01575   layer_groups = PCB->LayerGroups;      /* working copy */
01576   lg_monitor = &PCB->LayerGroups;       /* So can know if PCB changes on us */
01577 
01578   label = gtk_label_new (_("Layer group number: "));
01579   gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
01580   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
01581 
01582   for (i = 1; i < max_group + 1; ++i)
01583     {
01584         if (i < 10)
01585                 snprintf (buf, sizeof (buf), "%d   ", i);
01586         else
01587                 snprintf (buf, sizeof (buf), "%d", i);
01588       label = gtk_label_new (buf);
01589       gtk_table_attach_defaults (GTK_TABLE (table), label, i, i + 1, 0, 1);
01590     }
01591 
01592   /* Create a row of radio toggle buttons for layer.  So each layer
01593      |  can have an active radio button set for the group it needs to be in.
01594    */
01595   for (layer = 0; layer < max_copper_layer; ++layer)
01596     {
01597           name = (gchar *) UNKNOWN (PCB->Data->Layer[layer].Name);
01598           row = layer + 1;
01599  
01600           layer_entry[layer] = gtk_entry_new ();
01601           gtk_entry_set_text (GTK_ENTRY (layer_entry[layer]), name);
01602           gtk_table_attach_defaults (GTK_TABLE (table), layer_entry[layer],
01603                                      0, 1, row, row + 1);
01604           g_signal_connect(G_OBJECT(layer_entry[layer]), "activate",
01605                                 G_CALLBACK(layer_name_entry_cb), GINT_TO_POINTER(layer));
01606 
01607       group = NULL;
01608       for (i = 0; i < max_group; ++i)
01609         {
01610           button = gtk_radio_button_new (group);
01611           gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), TRUE);
01612           group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
01613           gtk_table_attach_defaults (GTK_TABLE (table), button,
01614                                      i + 1, i + 2, row, row +1);
01615           g_signal_connect (G_OBJECT (button), "toggled",
01616                             G_CALLBACK (config_layer_groups_radio_button_cb),
01617                             GINT_TO_POINTER ((layer << 8) | (i + 1)));
01618           group_button[layer][i] = button;
01619         }
01620     }
01621 
01622   /* silk layers double as special layer groups 'top side' and 'bottom side'. 
01623     So some special treatment is needed:
01624     1) top and bottom silk cannot be renamed by the user
01625     2) The dialog shows the top side buttons above bottom side button row
01626    */
01627   group = NULL;
01628   layer = top_silk_layer;
01629   label = gtk_label_new ("Top side: ");
01630   row = max_copper_layer + 1;
01631   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
01632   gtk_table_attach_defaults (GTK_TABLE (table), label,
01633                                      0, 1, row, row + 1);
01634   for (i = 0; i < max_group; ++i)
01635         {
01636           button = gtk_radio_button_new (group);
01637           gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), TRUE);
01638           group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
01639           gtk_table_attach_defaults (GTK_TABLE (table), button,
01640                                      i + 1, i + 2, row, row + 1);
01641           g_signal_connect (G_OBJECT (button), "toggled",
01642                             G_CALLBACK (config_layer_groups_radio_button_cb),
01643                             GINT_TO_POINTER ((layer << 8) | (i + 1)));
01644           group_button[layer][i] = button;
01645         }
01646   
01647   group = NULL;
01648   layer = bottom_silk_layer;
01649   label = gtk_label_new ("Bottom side: ");
01650   row = max_copper_layer + 2;
01651   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
01652   gtk_table_attach_defaults (GTK_TABLE (table), label,
01653                                      0, 1, row, row + 1);
01654   for (i = 0; i < max_group; ++i)
01655         {
01656           button = gtk_radio_button_new (group);
01657           gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), TRUE);
01658           group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
01659           gtk_table_attach_defaults (GTK_TABLE (table), button,
01660                                      i + 1, i + 2, row, row + 1);
01661           g_signal_connect (G_OBJECT (button), "toggled",
01662                             G_CALLBACK (config_layer_groups_radio_button_cb),
01663                             GINT_TO_POINTER ((layer << 8) | (i + 1)));
01664           group_button[layer][i] = button;
01665         }
01666 
01667   gtk_widget_show_all(config_groups_vbox);
01668   config_layer_group_button_state_update ();
01669 }
01670 
01671 
01672 static void
01673 edit_layer_button_cb(GtkWidget *widget, gchar *data)
01674 {
01675         gchar   **argv;
01676 
01677         if (PCB->RatDraw || PCB->SilkActive)
01678                 return;
01679 
01680         argv = g_strsplit(data, ",", -1);
01681         MoveLayerAction(2, argv, 0, 0);
01682         g_strfreev(argv);
01683 }
01684 
01685 static void
01686 config_layers_tab_create (GtkWidget * tab_vbox)
01687 {
01688   GtkWidget *tabs, *vbox, *vbox1, *button, *text, *sep;
01689   GtkWidget *hbox, *arrow;
01690   gint i;
01691 
01692   tabs = gtk_notebook_new ();
01693   gtk_box_pack_start (GTK_BOX (tab_vbox), tabs, TRUE, TRUE, 0);
01694 
01695 /* -- Change tab */
01696   vbox = ghid_notebook_page(tabs, _("Change"), 0, 6);
01697   vbox1 = ghid_category_vbox(vbox,
01698                         _("Operations on currently selected layer:"),
01699                         4, 2, TRUE, TRUE);
01700 
01701   button = gtk_button_new();
01702   arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_ETCHED_IN);
01703   gtk_container_add(GTK_CONTAINER(button), arrow);
01704   g_signal_connect(G_OBJECT(button), (gchar *)"clicked",
01705                    G_CALLBACK(edit_layer_button_cb), (gchar *)"c,up");
01706   hbox = gtk_hbox_new(FALSE, 0);
01707   gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
01708   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
01709 
01710   button = gtk_button_new();
01711   arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_ETCHED_IN);
01712   gtk_container_add(GTK_CONTAINER(button), arrow);
01713   g_signal_connect(G_OBJECT(button), (gchar *)"clicked",
01714                    G_CALLBACK(edit_layer_button_cb), (gchar *)"c,down");
01715   hbox = gtk_hbox_new(FALSE, 0);
01716   gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
01717   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
01718 
01719   button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
01720   g_signal_connect(G_OBJECT(button), (gchar *)"clicked",
01721                    G_CALLBACK(edit_layer_button_cb), (gchar *)"c,-1");
01722   hbox = gtk_hbox_new(FALSE, 0);
01723   gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
01724   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
01725 
01726   vbox1 = ghid_category_vbox(vbox,
01727                         _("Add new layer above currently selected layer:"),
01728                         4, 2, TRUE, TRUE);
01729   button = gtk_button_new_from_stock(GTK_STOCK_ADD);
01730   g_signal_connect(G_OBJECT(button), (gchar *)"clicked",
01731                    G_CALLBACK(edit_layer_button_cb), (gchar *)"-1,c");
01732   hbox = gtk_hbox_new(FALSE, 0);
01733   gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
01734   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
01735 
01736 /* -- Groups tab */
01737   vbox = ghid_notebook_page (tabs, _("Groups"), 0, 6);
01738   config_groups_vbox = gtk_vbox_new(FALSE, 0);
01739   gtk_box_pack_start(GTK_BOX(vbox), config_groups_vbox, FALSE, FALSE, 0);
01740   ghid_config_groups_changed();
01741   
01742   sep = gtk_hseparator_new ();
01743   gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 4);
01744 
01745 #if FIXME
01746   ghid_check_button_connected (vbox, &use_layer_default_button, FALSE,
01747                                TRUE, FALSE, FALSE, 8, NULL, NULL,
01748                                ("Use these layer settings as the default for new layouts"));
01749 #endif
01750 
01751 
01752 /* -- Info tab */
01753   vbox = ghid_notebook_page (tabs, C_("tab", "Info"), 0, 6);
01754 
01755   text = ghid_scrolled_text_view (vbox, NULL,
01756                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
01757   for (i = 0; i < sizeof (layer_info_text) / sizeof (gchar *); ++i)
01758     ghid_text_view_append (text, _(layer_info_text[i]));
01759 }
01760 
01761 
01762 void
01763 ghid_config_layer_name_update (gchar * name, gint layer)
01764 {
01765   if (!config_window || layers_applying || !name)
01766     return;
01767   gtk_entry_set_text (GTK_ENTRY (layer_entry[layer]), name);
01768 
01769   /* If we get a config layer name change because a new PCB is loaded
01770      |  or new layout started, need to change our working layer group copy.
01771    */
01772   if (lg_monitor != &PCB->LayerGroups)
01773     {
01774       layer_groups = PCB->LayerGroups;
01775       lg_monitor = &PCB->LayerGroups;
01776       config_layer_group_button_state_update ();
01777       groups_modified = FALSE;
01778     }
01779 }
01780 
01781   /* -------------- The Colors config page ----------------
01782    */
01783 static GtkWidget *config_colors_vbox,
01784   *config_colors_tab_vbox,
01785   *config_colors_save_button,
01786   *config_color_file_label, *config_color_warn_label;
01787 
01788 static void config_colors_tab_create (GtkWidget * tab_vbox);
01789 
01790 static gboolean config_colors_modified;
01791 
01792 static void
01793 config_color_file_set_label (void)
01794 {
01795   gchar *str, *name;
01796 
01797   if (!*color_file)
01798     name = g_strdup ("defaults");
01799   else
01800     name = g_path_get_basename (color_file);
01801 
01802   str = g_strdup_printf (_("Current colors loaded: <b>%s</b>"), name);
01803   gtk_label_set_markup (GTK_LABEL (config_color_file_label), str);
01804   g_free (name);
01805   g_free (str);
01806 }
01807 
01808 static void
01809 config_color_defaults_cb (gpointer data)
01810 {
01811   GList *list;
01812   ConfigColor *cc;
01813   HID_Attribute *ha;
01814 
01815   for (list = config_color_list; list; list = list->next)
01816     {
01817       cc = (ConfigColor *) list->data;
01818       ha = cc->attributes;
01819       dup_core_string ((char **) ha->value, ha->default_val.str_value);
01820       cc->color_is_mapped = FALSE;
01821       ghid_set_special_colors (ha);
01822     }
01823 
01824   dup_string (&color_file, "");
01825   ghidgui->config_modified = TRUE;
01826 
01827   gtk_widget_set_sensitive (config_colors_save_button, FALSE);
01828   gtk_widget_set_sensitive (config_color_warn_label, FALSE);
01829   config_color_file_set_label ();
01830   config_colors_modified = FALSE;
01831 
01832   ghid_layer_buttons_color_update ();
01833 
01834   /* Receate the colors config page to pick up new colors.
01835    */
01836   gtk_widget_destroy (config_colors_vbox);
01837   config_colors_tab_create (config_colors_tab_vbox);
01838 
01839   ghid_invalidate_all();
01840 }
01841 
01842 static void
01843 config_color_load_cb (gpointer data)
01844 {
01845   gchar *path, *dir = g_strdup (color_dir);
01846 
01847   path = ghid_dialog_file_select_open (_("Load Color File"), &dir, NULL);
01848   if (path)
01849     {
01850       config_colors_read (path);
01851       dup_string (&color_file, path);
01852       ghidgui->config_modified = TRUE;
01853 
01854       gtk_widget_set_sensitive (config_colors_save_button, FALSE);
01855       gtk_widget_set_sensitive (config_color_warn_label, FALSE);
01856       config_color_file_set_label ();
01857       config_colors_modified = FALSE;
01858     }
01859   g_free (path);
01860   g_free (dir);
01861 
01862   /* Receate the colors config page to pick up new colors.
01863    */
01864   gtk_widget_destroy (config_colors_vbox);
01865   config_colors_tab_create (config_colors_tab_vbox);
01866 
01867   ghid_layer_buttons_color_update ();
01868   ghid_invalidate_all();
01869 }
01870 
01871 static void
01872 config_color_save_cb (gpointer data)
01873 {
01874   gchar *name, *path, *dir = g_strdup (color_dir);
01875 
01876   path =
01877     ghid_dialog_file_select_save (_("Save Color File"), &dir, NULL, NULL);
01878   if (path)
01879     {
01880       name = g_path_get_basename (path);
01881       if (!strcmp (name, "default"))
01882         ghid_dialog_message (_
01883                              ("Sorry, not overwriting the default color file!"));
01884       else
01885         {
01886           config_colors_write (path);
01887           dup_string (&color_file, path);
01888           ghidgui->config_modified = TRUE;
01889 
01890           gtk_widget_set_sensitive (config_colors_save_button, FALSE);
01891           gtk_widget_set_sensitive (config_color_warn_label, FALSE);
01892           config_color_file_set_label ();
01893           config_colors_modified = FALSE;
01894         }
01895       g_free (name);
01896     }
01897   g_free (path);
01898   g_free (dir);
01899 }
01900 
01901 static void
01902 config_color_set_cb (GtkWidget * button, ConfigColor * cc)
01903 {
01904   GdkColor new_color;
01905   HID_Attribute *ha = cc->attributes;
01906   gchar *str;
01907 
01908   gtk_color_button_get_color (GTK_COLOR_BUTTON (button), &new_color);
01909   str = ghid_get_color_name (&new_color);
01910   ghid_map_color_string (str, &cc->color);
01911   *(char **) ha->value = str;
01912 /*      g_free(str);            Memory leak */
01913 
01914   config_colors_modified = TRUE;
01915   gtk_widget_set_sensitive (config_colors_save_button, TRUE);
01916   gtk_widget_set_sensitive (config_color_warn_label, TRUE);
01917 
01918   ghid_set_special_colors (ha);
01919   ghid_layer_buttons_color_update ();
01920   ghid_invalidate_all();
01921 }
01922 
01923 static void
01924 config_color_button_create (GtkWidget * box, ConfigColor * cc)
01925 {
01926   GtkWidget *button, *hbox, *label;
01927   HID_Attribute *ha = cc->attributes;
01928   gchar *title;
01929 
01930   hbox = gtk_hbox_new (FALSE, 6);
01931   gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
01932 
01933   if (!cc->color_is_mapped)
01934     ghid_map_color_string (*(char **) ha->value, &cc->color);
01935   cc->color_is_mapped = TRUE;
01936 
01937   title = g_strdup_printf (_("PCB %s Color"), ha->name);
01938   button = gtk_color_button_new_with_color (&cc->color);
01939   gtk_color_button_set_title (GTK_COLOR_BUTTON (button), title);
01940   g_free (title);
01941 
01942   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
01943   label = gtk_label_new (ha->name);
01944   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
01945   g_signal_connect (G_OBJECT (button), "color-set",
01946                     G_CALLBACK (config_color_set_cb), cc);
01947 }
01948 
01949 static void
01950 config_colors_tab_create (GtkWidget * tab_vbox)
01951 {
01952   GtkWidget *scrolled_vbox, *vbox, *hbox, *expander, *sep;
01953   GList *list;
01954   ConfigColor *cc;
01955 
01956   vbox = gtk_vbox_new (FALSE, 0);
01957   gtk_box_pack_start (GTK_BOX (tab_vbox), vbox, TRUE, TRUE, 0);
01958   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
01959 
01960   config_colors_vbox = vbox;    /* can be destroyed if color file loaded */
01961   config_colors_tab_vbox = tab_vbox;
01962 
01963   scrolled_vbox = ghid_scrolled_vbox (config_colors_vbox, NULL,
01964                                       GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
01965 
01966   /* ---- Main colors ---- */
01967   expander = gtk_expander_new (_("Main colors"));
01968   gtk_box_pack_start (GTK_BOX (scrolled_vbox), expander, FALSE, FALSE, 2);
01969   vbox = gtk_vbox_new (FALSE, 0);
01970   gtk_container_add (GTK_CONTAINER (expander), vbox);
01971   vbox = ghid_category_vbox (vbox, NULL, 0, 2, TRUE, FALSE);
01972 
01973   for (list = config_color_list; list; list = list->next)
01974     {
01975       cc = (ConfigColor *) list->data;
01976       if (cc->type != MISC_COLOR)
01977         continue;
01978       config_color_button_create (vbox, cc);
01979     }
01980 
01981   /* ---- Layer colors ---- */
01982   expander = gtk_expander_new (_("Layer colors"));
01983   gtk_box_pack_start (GTK_BOX (scrolled_vbox), expander, FALSE, FALSE, 2);
01984   vbox = gtk_vbox_new (FALSE, 0);
01985   gtk_container_add (GTK_CONTAINER (expander), vbox);
01986   vbox = ghid_category_vbox (vbox, NULL, 0, 2, TRUE, FALSE);
01987 
01988   for (list = config_color_list; list; list = list->next)
01989     {
01990       cc = (ConfigColor *) list->data;
01991       if (cc->type != LAYER_COLOR)
01992         continue;
01993       config_color_button_create (vbox, cc);
01994     }
01995 
01996   /* ---- Selected colors ---- */
01997   expander = gtk_expander_new (_("Selected colors"));
01998   gtk_box_pack_start (GTK_BOX (scrolled_vbox), expander, FALSE, FALSE, 2);
01999   vbox = gtk_vbox_new (FALSE, 0);
02000   gtk_container_add (GTK_CONTAINER (expander), vbox);
02001   vbox = ghid_category_vbox (vbox, NULL, 0, 2, TRUE, FALSE);
02002 
02003   for (list = config_color_list; list; list = list->next)
02004     {
02005       cc = (ConfigColor *) list->data;
02006       if (cc->type != MISC_SELECTED_COLOR)
02007         continue;
02008       config_color_button_create (vbox, cc);
02009     }
02010   sep = gtk_hseparator_new ();
02011   gtk_box_pack_start (GTK_BOX (vbox), sep, FALSE, FALSE, 2);
02012   for (list = config_color_list; list; list = list->next)
02013     {
02014       cc = (ConfigColor *) list->data;
02015       if (cc->type != LAYER_SELECTED_COLOR)
02016         continue;
02017       config_color_button_create (vbox, cc);
02018     }
02019 
02020   config_color_warn_label = gtk_label_new ("");
02021   gtk_label_set_use_markup (GTK_LABEL (config_color_warn_label), TRUE);
02022   gtk_label_set_markup (GTK_LABEL (config_color_warn_label),
02023                         _("<b>Warning:</b> unsaved color changes will be lost"
02024                           " at program exit."));
02025   gtk_box_pack_start (GTK_BOX (config_colors_vbox), config_color_warn_label,
02026                       FALSE, FALSE, 4);
02027 
02028   hbox = gtk_hbox_new (FALSE, 0);
02029   gtk_box_pack_start (GTK_BOX (config_colors_vbox), hbox, FALSE, FALSE, 6);
02030 
02031   config_color_file_label = gtk_label_new ("");
02032   gtk_label_set_use_markup (GTK_LABEL (config_color_file_label), TRUE);
02033   config_color_file_set_label ();
02034   gtk_box_pack_start (GTK_BOX (hbox), config_color_file_label,
02035                       FALSE, FALSE, 0);
02036 
02037   ghid_button_connected (hbox, NULL, FALSE, FALSE, FALSE, 4,
02038                          config_color_load_cb, NULL, _("Load"));
02039   ghid_button_connected (hbox, &config_colors_save_button,
02040                          FALSE, FALSE, FALSE, 4,
02041                          config_color_save_cb, NULL, _("Save"));
02042   ghid_button_connected (hbox, NULL, FALSE, FALSE, FALSE, 4,
02043                          config_color_defaults_cb, NULL, _("Defaults"));
02044 
02045   gtk_widget_set_sensitive (config_colors_save_button,
02046                             config_colors_modified);
02047   gtk_widget_set_sensitive (config_color_warn_label, config_colors_modified);
02048   gtk_widget_show_all (config_colors_vbox);
02049 }
02050 
02051 
02052   /* --------------- The main config page -----------------
02053    */
02054 enum
02055 {
02056   CONFIG_NAME_COLUMN,
02057   CONFIG_PAGE_COLUMN,
02058   N_CONFIG_COLUMNS
02059 };
02060 
02061 static GtkNotebook *config_notebook;
02062 
02063 static GtkWidget *
02064 config_page_create (GtkTreeStore * tree, GtkTreeIter * iter,
02065                     GtkNotebook * notebook)
02066 {
02067   GtkWidget *vbox;
02068   gint page;
02069 
02070   vbox = gtk_vbox_new (FALSE, 0);
02071   gtk_notebook_append_page (notebook, vbox, NULL);
02072   page = gtk_notebook_get_n_pages (notebook) - 1;
02073   gtk_tree_store_set (tree, iter, CONFIG_PAGE_COLUMN, page, -1);
02074   return vbox;
02075 }
02076 
02077 void
02078 ghid_config_handle_units_changed (void)
02079 {
02080   gchar *text = pcb_g_strdup_printf ("<b>%s</b>",
02081                                      Settings.grid_unit->in_suffix);
02082   ghid_set_cursor_position_labels ();
02083   gtk_label_set_markup (GTK_LABEL (ghidgui->grid_units_label), text);
02084   g_free (text);
02085 
02086   if (config_sizes_vbox)
02087     {
02088       gtk_widget_destroy (config_sizes_vbox);
02089       config_sizes_vbox = NULL;
02090       config_sizes_tab_create (config_sizes_tab_vbox);
02091     }
02092   if (config_increments_vbox)
02093     {
02094       gtk_widget_destroy (config_increments_vbox);
02095       config_increments_vbox = NULL;
02096       config_increments_tab_create (config_increments_tab_vbox);
02097     }
02098   ghidgui->config_modified = TRUE;
02099 }
02100 
02101 void
02102 ghid_config_text_scale_update (void)
02103 {
02104   if (config_window)
02105     gtk_spin_button_set_value (GTK_SPIN_BUTTON (config_text_spin_button),
02106                                (gdouble) Settings.TextScale);
02107 }
02108 
02109 static void
02110 config_close_cb (gpointer data)
02111 {
02112   /* Config pages may need to check for modified entries, use as default
02113      |  options, etc when the config window is closed.
02114    */
02115   config_sizes_apply ();
02116   config_layers_apply ();
02117   config_library_apply ();
02118   config_general_apply ();
02119 
02120   config_sizes_vbox = NULL;
02121   config_increments_vbox = NULL;
02122 
02123   config_groups_vbox = config_groups_table = NULL;
02124   config_groups_window = NULL;
02125 
02126   gtk_widget_destroy (config_window);
02127   config_window = NULL;
02128 }
02129 
02130 static void
02131 config_destroy_cb (gpointer data)
02132 {
02133   config_sizes_vbox = NULL;
02134   config_increments_vbox = NULL;
02135   config_groups_vbox = config_groups_table = NULL;
02136   config_groups_window = NULL;
02137   gtk_widget_destroy (config_window);
02138   config_window = NULL;
02139 }
02140 
02141 static void
02142 config_selection_changed_cb (GtkTreeSelection * selection, gpointer data)
02143 {
02144   GtkTreeIter iter;
02145   GtkTreeModel *model;
02146   gint page;
02147 
02148   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
02149     return;
02150   gtk_tree_model_get (model, &iter, CONFIG_PAGE_COLUMN, &page, -1);
02151   gtk_notebook_set_current_page (config_notebook, page);
02152 }
02153 
02154 void
02155 ghid_config_window_show (void)
02156 {
02157   GtkWidget *widget, *main_vbox, *vbox, *config_hbox, *hbox;
02158   GtkWidget *scrolled;
02159   GtkWidget *button;
02160   GtkTreeStore *model;
02161   GtkTreeView *treeview;
02162   GtkTreeIter iter;
02163   GtkCellRenderer *renderer;
02164   GtkTreeViewColumn *column;
02165   GtkTreeSelection *select;
02166 
02167   if (config_window)
02168     {
02169       gtk_window_present (GTK_WINDOW (config_window));
02170       return;
02171     }
02172 
02173   config_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
02174   g_signal_connect (G_OBJECT (config_window), "delete_event",
02175                     G_CALLBACK (config_destroy_cb), NULL);
02176 
02177   gtk_window_set_title (GTK_WINDOW (config_window), _("PCB Preferences"));
02178   gtk_window_set_wmclass (GTK_WINDOW (config_window), "Pcb_Conf", "PCB");
02179   gtk_container_set_border_width (GTK_CONTAINER (config_window), 2);
02180 
02181   config_hbox = gtk_hbox_new (FALSE, 4);
02182   gtk_container_add (GTK_CONTAINER (config_window), config_hbox);
02183 
02184   scrolled = gtk_scrolled_window_new (NULL, NULL);
02185   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
02186                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
02187   gtk_box_pack_start (GTK_BOX (config_hbox), scrolled, FALSE, FALSE, 0);
02188 
02189   main_vbox = gtk_vbox_new (FALSE, 4);
02190   gtk_box_pack_start (GTK_BOX (config_hbox), main_vbox, TRUE, TRUE, 0);
02191 
02192   widget = gtk_notebook_new ();
02193   gtk_box_pack_start (GTK_BOX (main_vbox), widget, TRUE, TRUE, 0);
02194   config_notebook = GTK_NOTEBOOK (widget);
02195   gtk_notebook_set_show_tabs (config_notebook, FALSE);
02196 
02197   model = gtk_tree_store_new (N_CONFIG_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
02198 
02199 
02200   /* -- General -- */
02201   gtk_tree_store_append (model, &iter, NULL);
02202   gtk_tree_store_set (model, &iter, CONFIG_NAME_COLUMN, _("General"), -1);
02203   vbox = config_page_create (model, &iter, config_notebook);
02204   config_general_tab_create (vbox);
02205 
02206 
02207   /* -- Sizes -- */
02208   gtk_tree_store_append (model, &iter, NULL);
02209   gtk_tree_store_set (model, &iter, CONFIG_NAME_COLUMN, _("Sizes"), -1);
02210   vbox = config_page_create (model, &iter, config_notebook);
02211   config_sizes_tab_create (vbox);
02212 
02213   /* -- Increments -- */
02214   gtk_tree_store_append (model, &iter, NULL);
02215   gtk_tree_store_set (model, &iter, CONFIG_NAME_COLUMN, _("Increments"), -1);
02216   vbox = config_page_create (model, &iter, config_notebook);
02217   config_increments_tab_create (vbox);
02218 
02219   /* -- Library -- */
02220   gtk_tree_store_append (model, &iter, NULL);
02221   gtk_tree_store_set (model, &iter, CONFIG_NAME_COLUMN, _("Library"), -1);
02222   vbox = config_page_create (model, &iter, config_notebook);
02223   config_library_tab_create (vbox);
02224 
02225   /* -- Layer names and groups -- */
02226   gtk_tree_store_append (model, &iter, NULL);
02227   gtk_tree_store_set (model, &iter, CONFIG_NAME_COLUMN, _("Layers"), -1);
02228   vbox = config_page_create (model, &iter, config_notebook);
02229   config_layers_tab_create (vbox);
02230 
02231 
02232   /* -- Colors -- */
02233   gtk_tree_store_append (model, &iter, NULL);
02234   gtk_tree_store_set (model, &iter, CONFIG_NAME_COLUMN, _("Colors"), -1);
02235   vbox = config_page_create (model, &iter, config_notebook);
02236   config_colors_tab_create (vbox);
02237 
02238 
02239   /* Create the tree view
02240    */
02241   treeview =
02242     GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (model)));
02243   g_object_unref (G_OBJECT (model));    /* Don't need the model anymore */
02244 
02245   renderer = gtk_cell_renderer_text_new ();
02246   column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
02247                                                      "text",
02248                                                      CONFIG_NAME_COLUMN,
02249                                                      NULL);
02250   gtk_tree_view_append_column (treeview, column);
02251   gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (treeview));
02252 
02253 
02254   select = gtk_tree_view_get_selection (treeview);
02255   gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
02256   g_signal_connect (G_OBJECT (select), "changed",
02257                     G_CALLBACK (config_selection_changed_cb), NULL);
02258 
02259 
02260   hbox = gtk_hbutton_box_new ();
02261   gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
02262   gtk_box_set_spacing (GTK_BOX (hbox), 5);
02263   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
02264 
02265   button = gtk_button_new_from_stock (GTK_STOCK_OK);
02266   gtk_widget_set_can_default (button, TRUE);
02267   g_signal_connect (G_OBJECT (button), "clicked",
02268                     G_CALLBACK (config_close_cb), NULL);
02269   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
02270   gtk_widget_grab_default (button);
02271 
02272   gtk_widget_show_all (config_window);
02273 }