pcb 4.1.1
An interactive printed circuit board layout editor.
|
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 }