pcb 4.1.1
An interactive printed circuit board layout editor.
|
00001 00055 #ifdef HAVE_CONFIG_H 00056 #include "config.h" 00057 #endif 00058 00059 #include "gui.h" 00060 #include <gdk/gdkkeysyms.h> 00061 00062 #include "crosshair.h" 00063 00064 #ifdef HAVE_LIBDMALLOC 00065 #include <dmalloc.h> 00066 #endif 00067 00068 static GtkWidget *command_window; 00069 static GtkWidget *combo_vbox; 00070 static GList *history_list; 00071 static gchar *command_entered; 00072 static GMainLoop *loop; 00073 00074 00082 static gchar *command_ref_text[] = { 00083 N_("Common commands easily accessible via the gui may not be included here.\n"), 00084 "\n", 00085 N_("In user commands below, 'size' values may be absolute or relative\n" 00086 "if preceded by a '+' or '-'. Where 'units' are indicated, use \n" 00087 "'mil' or 'mm' otherwise PCB internal units will be used.\n"), 00088 "\n", 00089 "<b>changesize(target, size, units)\n", 00090 "\ttarget = {selectedlines | selectedpins | selectedvias | selectedpads \n" 00091 "\t\t\t| selectedtexts | selectednames | selectedelements | selected}\n", 00092 "\n", 00093 "<b>changedrillsize(target, size, units)\n", 00094 "\ttarget = {selectedpins | selectedvias | selectedobjects | selected}\n", 00095 "\n", 00096 "<b>changeclearsize(target, size, units)\n", 00097 "\ttarget = {selectedpins | selectedpads | selectedvias | selectedlines\n" 00098 "\t\t\t| selectedarcs | selectedobjects | selected}\n", 00099 N_("\tChanges the clearance of objects.\n"), 00100 "\n", 00101 "<b>setvalue(target, size, units)\n", 00102 "\ttarget = {grid | zoom | line | textscale | viadrillinghole\n" 00103 "\t\t\t| viadrillinghole | via}\n", 00104 N_("\tChanges values. Omit 'units' for 'grid' and 'zoom'.\n"), 00105 "\n", 00106 "<b>changejoin(target)\n", 00107 "\ttarget = {object | selectedlines | selectedarcs | selected}\n", 00108 N_("\tChanges the join (clearance through polygons) of objects.\n"), 00109 "\n", 00110 "<b>changesquare(target)\n", 00111 "<b>setsquare(target)\n", 00112 "<b>clearsquare(target)\n", 00113 "\ttarget = {object | selectedelements | selectedpins | selected}\n", 00114 N_("\tToggles, sets, or clears the square flag of objects.\n"), 00115 "\n", 00116 "<b>changeoctagon(target)\n", 00117 "<b>setoctagon(target)\n", 00118 "<b>clearoctagon(target)\n", 00119 "\ttarget = {object | selectedelements | selectedpins selectedvias | selected}\n", 00120 N_("\tToggles, sets, or clears the octagon flag of objects.\n"), 00121 "\n", 00122 "<b>changehole(target)\n", 00123 "\ttarget = {object | selectedvias | selected}\n", 00124 N_("\tChanges the hole flag of objects.\n"), 00125 "\n", 00126 "<b>flip(target)\n", 00127 "\ttarget = {object | selectedelements | selected}\n", 00128 N_("\tFlip elements to the opposite side of the board.\n"), 00129 "\n", 00130 "<b>setthermal(target, style)\n", 00131 "\ttarget = {object | selectedpins | selectedvias | selected}\n", 00132 "\tstyle = {0 | 1 | 2 | 3 | 4 | 5}\n\n", 00133 N_("\tSet or clear a thermal (on the current layer) to pins or vias.\n" 00134 "\tIf 'style' is omitted, the layout's default style is taken. Setting\n" 00135 "\tthermals to style 0 (zero) turns the thermals off.\n"), 00136 "\n", 00137 "<b>loadvendorfrom(filename)\n", 00138 "<b>unloadvendor()\n", 00139 "\ttarget = [filename]\n", 00140 N_("\tLoad a vendor file. If 'filename' is omitted, pop up a file select dialog.\n"), 00141 }; 00142 00143 00155 static void 00156 command_history_add (gchar * cmd) 00157 { 00158 GList *list; 00159 gchar *s; 00160 gint i; 00161 00162 if (!cmd || !*cmd) 00163 return; 00164 00165 /* Check for a duplicate command. If found, move it to the 00166 | top of the list and similarly modify the combo box strings. 00167 */ 00168 for (i = 0, list = history_list; list; list = list->next, ++i) 00169 { 00170 s = (gchar *) list->data; 00171 if (!strcmp (cmd, s)) 00172 { 00173 history_list = g_list_remove (history_list, s); 00174 history_list = g_list_prepend (history_list, s); 00175 gtk_combo_box_remove_text (GTK_COMBO_BOX 00176 (ghidgui->command_combo_box), i); 00177 gtk_combo_box_prepend_text (GTK_COMBO_BOX 00178 (ghidgui->command_combo_box), s); 00179 return; 00180 } 00181 } 00182 00183 /* Not a duplicate, so put first in history list and combo box text list. 00184 */ 00185 s = g_strdup (cmd); 00186 history_list = g_list_prepend (history_list, s); 00187 gtk_combo_box_prepend_text (GTK_COMBO_BOX (ghidgui->command_combo_box), s); 00188 00189 /* And keep the lists trimmed! 00190 */ 00191 if (g_list_length (history_list) > ghidgui->history_size) 00192 { 00193 s = (gchar *) g_list_nth_data (history_list, ghidgui->history_size); 00194 history_list = g_list_remove (history_list, s); 00195 gtk_combo_box_remove_text (GTK_COMBO_BOX (ghidgui->command_combo_box), 00196 ghidgui->history_size); 00197 g_free (s); 00198 } 00199 } 00200 00201 00212 static void 00213 command_entry_activate_cb (GtkWidget * widget, gpointer data) 00214 { 00215 gchar *command; 00216 00217 command = 00218 g_strdup (ghid_entry_get_text (GTK_WIDGET (ghidgui->command_entry))); 00219 gtk_entry_set_text (ghidgui->command_entry, ""); 00220 00221 if (*command) 00222 command_history_add (command); 00223 00224 if (ghidgui->use_command_window) 00225 { 00226 hid_parse_command (command); 00227 g_free (command); 00228 } 00229 else 00230 { 00231 if (loop && g_main_loop_is_running (loop)) /* should always be */ 00232 g_main_loop_quit (loop); 00233 command_entered = command; /* Caller will free it */ 00234 } 00235 } 00236 00237 00252 static void 00253 command_combo_box_entry_create (void) 00254 { 00255 ghidgui->command_combo_box = gtk_combo_box_entry_new_text (); 00256 ghidgui->command_entry = 00257 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (ghidgui->command_combo_box))); 00258 00259 gtk_entry_set_width_chars (ghidgui->command_entry, 40); 00260 gtk_entry_set_activates_default (ghidgui->command_entry, TRUE); 00261 00262 g_signal_connect (G_OBJECT (ghidgui->command_entry), "activate", 00263 G_CALLBACK (command_entry_activate_cb), NULL); 00264 00265 g_object_ref (G_OBJECT (ghidgui->command_combo_box)); /* so can move it */ 00266 } 00267 00268 00269 static void 00270 command_window_disconnect_combobox () 00271 { 00272 if (command_window) 00273 { 00274 gtk_container_remove (GTK_CONTAINER (combo_vbox), /* Float it */ 00275 ghidgui->command_combo_box); 00276 } 00277 combo_vbox = NULL; 00278 } 00279 00280 00281 static void 00282 command_window_close_cb (GtkWidget * widget, gpointer data) 00283 { 00284 command_window_disconnect_combobox(); 00285 gtk_widget_destroy (command_window); 00286 } 00287 00288 00289 static gboolean 00290 command_window_delete_event_cb (GtkWidget * widget, GdkEvent * event, 00291 gpointer data) 00292 { 00293 command_window_disconnect_combobox(); 00294 return FALSE; 00295 } 00296 00297 00298 static void 00299 command_destroy_cb (GtkWidget * widget, gpointer data) 00300 { 00301 command_window = NULL; 00302 } 00303 00304 00305 static gboolean 00306 command_escape_cb (GtkWidget * widget, GdkEventKey * kev, gpointer data) 00307 { 00308 gint ksym = kev->keyval; 00309 00310 if (ksym != GDK_Escape) 00311 return FALSE; 00312 00313 if (command_window) { 00314 command_window_disconnect_combobox(); 00315 gtk_widget_destroy (command_window); 00316 } 00317 00318 if (loop && g_main_loop_is_running (loop)) /* should always be */ 00319 g_main_loop_quit (loop); 00320 command_entered = NULL; /* We are aborting */ 00321 00322 return TRUE; 00323 } 00324 00325 00331 void 00332 ghid_command_use_command_window_sync (void) 00333 { 00334 /* The combo box will be NULL and not living anywhere until the 00335 | first command entry. 00336 */ 00337 if (!ghidgui->command_combo_box) 00338 return; 00339 00340 if (ghidgui->use_command_window) 00341 gtk_container_remove (GTK_CONTAINER (ghidgui->status_line_hbox), 00342 ghidgui->command_combo_box); 00343 else 00344 { 00345 /* Destroy the window (if it's up) which floats the command_combo_box 00346 | so we can pack it back into the status line hbox. If the window 00347 | wasn't up, the command_combo_box was already floating. 00348 */ 00349 if(command_window) command_window_close_cb (NULL, NULL); 00350 gtk_widget_hide (ghidgui->command_combo_box); 00351 gtk_box_pack_start (GTK_BOX (ghidgui->status_line_hbox), 00352 ghidgui->command_combo_box, FALSE, FALSE, 0); 00353 } 00354 } 00355 00356 00361 void 00362 ghid_command_window_show (gboolean raise) 00363 { 00364 GtkWidget *vbox, *vbox1, *hbox, *button, *expander, *text; 00365 gint i; 00366 00367 if (command_window) 00368 { 00369 if (raise) 00370 gtk_window_present (GTK_WINDOW(command_window)); 00371 return; 00372 } 00373 command_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); 00374 g_signal_connect (G_OBJECT (command_window), "destroy", 00375 G_CALLBACK (command_destroy_cb), NULL); 00376 g_signal_connect (G_OBJECT (command_window), "delete-event", 00377 G_CALLBACK (command_window_delete_event_cb), NULL); 00378 g_signal_connect (G_OBJECT (command_window), "key_press_event", 00379 G_CALLBACK (command_escape_cb), NULL); 00380 gtk_window_set_title (GTK_WINDOW (command_window), _("PCB Command Entry")); 00381 gtk_window_set_wmclass (GTK_WINDOW (command_window), "PCB_Command", "PCB"); 00382 gtk_window_set_resizable (GTK_WINDOW (command_window), FALSE); 00383 00384 vbox = gtk_vbox_new (FALSE, 0); 00385 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); 00386 gtk_container_add (GTK_CONTAINER (command_window), vbox); 00387 00388 if (!ghidgui->command_combo_box) 00389 command_combo_box_entry_create (); 00390 00391 gtk_box_pack_start (GTK_BOX (vbox), ghidgui->command_combo_box, 00392 FALSE, FALSE, 0); 00393 combo_vbox = vbox; 00394 00395 /* Make the command reference scrolled text view. Use high level 00396 | utility functions in gui-utils.c 00397 */ 00398 expander = gtk_expander_new (_("Command Reference")); 00399 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 2); 00400 vbox1 = gtk_vbox_new (FALSE, 0); 00401 gtk_container_add (GTK_CONTAINER (expander), vbox1); 00402 gtk_widget_set_size_request (vbox1, -1, 350); 00403 00404 text = ghid_scrolled_text_view (vbox1, NULL, 00405 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); 00406 for (i = 0; i < sizeof (command_ref_text) / sizeof (gchar *); ++i) 00407 ghid_text_view_append (text, _(command_ref_text[i])); 00408 00409 /* The command window close button. 00410 */ 00411 hbox = gtk_hbutton_box_new (); 00412 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END); 00413 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 3); 00414 button = gtk_button_new_from_stock (GTK_STOCK_CLOSE); 00415 g_signal_connect (G_OBJECT (button), "clicked", 00416 G_CALLBACK (command_window_close_cb), NULL); 00417 gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); 00418 00419 gtk_widget_show_all (command_window); 00420 } 00421 00422 00430 gchar * 00431 ghid_command_entry_get (gchar * prompt, gchar * command) 00432 { 00433 gchar *s; 00434 gint escape_sig_id; 00435 GHidPort *out = &ghid_port; 00436 00437 /* If this is the first user command entry, we have to create the 00438 | command_combo_box and pack it into the status_line_hbox. 00439 */ 00440 if (!ghidgui->command_combo_box) 00441 { 00442 command_combo_box_entry_create (); 00443 gtk_box_pack_start (GTK_BOX (ghidgui->status_line_hbox), 00444 ghidgui->command_combo_box, FALSE, FALSE, 0); 00445 } 00446 00447 /* Make the prompt bold and set the label before showing the combo to 00448 | avoid window resizing wider. 00449 */ 00450 s = g_strdup_printf ("<b>%s</b>", prompt ? prompt : ""); 00451 ghid_status_line_set_text (s); 00452 g_free (s); 00453 00454 /* Flag so output drawing area won't try to get focus away from us and 00455 | so resetting the status line label can be blocked when resize 00456 | callbacks are invokded from the resize caused by showing the combo box. 00457 */ 00458 ghidgui->command_entry_status_line_active = TRUE; 00459 00460 gtk_entry_set_text (ghidgui->command_entry, command ? command : ""); 00461 gtk_widget_show_all (ghidgui->command_combo_box); 00462 00463 /* Remove the top window accel group so keys intended for the entry 00464 | don't get intercepted by the menu system. Set the interface 00465 | insensitive so all the user can do is enter a command, grab focus 00466 | and connect a handler to look for the escape key. 00467 */ 00468 ghid_remove_accel_groups (GTK_WINDOW (gport->top_window), ghidgui); 00469 ghid_interface_input_signals_disconnect (); 00470 ghid_interface_set_sensitive (FALSE); 00471 gtk_widget_grab_focus (GTK_WIDGET (ghidgui->command_entry)); 00472 escape_sig_id = g_signal_connect (G_OBJECT (ghidgui->command_entry), 00473 "key_press_event", 00474 G_CALLBACK (command_escape_cb), NULL); 00475 00476 loop = g_main_loop_new (NULL, FALSE); 00477 g_main_loop_run (loop); 00478 00479 g_main_loop_unref (loop); 00480 loop = NULL; 00481 00482 ghidgui->command_entry_status_line_active = FALSE; 00483 00484 /* Restore the damage we did before entering the loop. 00485 */ 00486 g_signal_handler_disconnect (ghidgui->command_entry, escape_sig_id); 00487 ghid_interface_input_signals_connect (); 00488 ghid_interface_set_sensitive (TRUE); 00489 ghid_install_accel_groups (GTK_WINDOW (gport->top_window), ghidgui); 00490 00491 /* Restore the status line label and give focus back to the drawing area 00492 */ 00493 gtk_widget_hide (ghidgui->command_combo_box); 00494 gtk_widget_grab_focus (out->drawing_area); 00495 00496 return command_entered; 00497 } 00498 00499 00500 void 00501 ghid_handle_user_command (gboolean raise) 00502 { 00503 char *command; 00504 static char *previous = NULL; 00505 00506 if (ghidgui->use_command_window) 00507 ghid_command_window_show (raise); 00508 else 00509 { 00510 command = ghid_command_entry_get (_("Enter command:"), 00511 (Settings.SaveLastCommand && previous) ? previous : (gchar *)""); 00512 if (command != NULL) 00513 { 00514 /* copy new comand line to save buffer */ 00515 g_free (previous); 00516 previous = g_strdup (command); 00517 hid_parse_command (command); 00518 g_free (command); 00519 } 00520 } 00521 ghid_window_set_name_label (PCB->Name); 00522 ghid_set_status_line_label (); 00523 }