pcb 4.1.1
An interactive printed circuit board layout editor.

gui-command-window.c

Go to the documentation of this file.
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 }