Board outline polygon generation
[geda-pcb/pcjc2.git] / src / hid / gtk / gui-top-window.c
blobca4b0694ba96cd9ac5a9068704be3d7716aee02a
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /* #define DEBUG_MENUS */
25 #ifdef DAN_FIXME
26 TODO:
28 - figure out when we need to call this:
29 ghid_set_status_line_label ();
30 Everytime?
32 - the old quit callback had:
34 ghid_config_files_write ();
35 hid_action ("Quit");
37 - what about stuff like this:
39 /* Set to ! because ActionDisplay toggles it */
40 Settings.DrawGrid = !gtk_toggle_action_get_active (action);
41 ghidgui->config_modified = TRUE;
42 hid_actionl ("Display", "Grid", "", NULL);
43 ghid_set_status_line_label ();
46 I NEED TO DO THE STATUS LINE THING. for example shift-alt-v to change the
47 via size. NOte the status line label does not get updated properly until
48 a zoom in/out.
50 #endif
52 /* This file was originally written by Bill Wilson for the PCB Gtk
53 * port. It was later heavily modified by Dan McMahill to provide
54 * user customized menus.
58 /* gui-top-window.c
59 | This handles creation of the top level window and all its widgets.
60 | events for the Output.drawing_area widget are handled in a separate
61 | file gui-output-events.c
63 | Some caveats with menu shorcut keys: Some keys are trapped out by Gtk
64 | and can't be used as shortcuts (eg. '|', TAB, etc). For these cases
65 | we have our own shortcut table and capture the keys and send the events
66 | there in ghid_port_key_press_cb().
70 #ifdef HAVE_CONFIG_H
71 #include "config.h"
72 #endif
74 #include <unistd.h>
76 #include "ghid-layer-selector.h"
77 #include "ghid-route-style-selector.h"
78 #include "gtkhid.h"
79 #include "gui.h"
80 #include "hid.h"
81 #include "../hidint.h"
82 #include "hid/common/hid_resource.h"
84 #include "action.h"
85 #include "autoplace.h"
86 #include "autoroute.h"
87 #include "buffer.h"
88 #include "change.h"
89 #include "copy.h"
90 #include "create.h"
91 #include "crosshair.h"
92 #include "draw.h"
93 #include "error.h"
94 #include "file.h"
95 #include "find.h"
96 #include "gpcb-menu.h"
97 #include "insert.h"
98 #include "line.h"
99 #include "mymem.h"
100 #include "misc.h"
101 #include "move.h"
102 #include "pcb-printf.h"
103 #include "polygon.h"
104 #include "rats.h"
105 #include "remove.h"
106 #include "report.h"
107 #include "resource.h"
108 #include "rotate.h"
109 #include "rubberband.h"
110 #include "search.h"
111 #include "select.h"
112 #include "set.h"
113 #include "undo.h"
114 #include "vendor.h"
115 #include "free_atexit.h"
117 #include "gui-icons-mode-buttons.data"
118 #include "gui-icons-misc.data"
119 #include "gui-trackball.h"
120 #include "snavi.h"
122 #ifdef HAVE_LIBDMALLOC
123 #include <dmalloc.h>
124 #endif
126 static bool ignore_layer_update;
128 static GtkWidget *ghid_load_menus (void);
130 GhidGui _ghidgui, *ghidgui = NULL;
132 GHidPort ghid_port, *gport;
134 static gchar *bg_image_file;
136 static struct { GtkAction *action; const Resource *node; }
137 ghid_hotkey_actions[256];
138 #define N_HOTKEY_ACTIONS \
139 (sizeof (ghid_hotkey_actions) / sizeof (ghid_hotkey_actions[0]))
142 /*! \brief callback for ghid_main_menu_update_toggle_state () */
143 void
144 menu_toggle_update_cb (GtkAction *act, const char *tflag, const char *aflag)
146 if (tflag != NULL)
148 int v = hid_get_flag (tflag);
149 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (act), !!v);
151 if (aflag != NULL)
153 int v = hid_get_flag (aflag);
154 gtk_action_set_sensitive (act, !!v);
158 /*! \brief sync the menu checkboxes with actual pcb state */
159 void
160 ghid_update_toggle_flags ()
162 ghid_main_menu_update_toggle_state (GHID_MAIN_MENU (ghidgui->menu_bar),
163 menu_toggle_update_cb);
166 static void
167 h_adjustment_changed_cb (GtkAdjustment * adj, GhidGui * g)
169 if (g->adjustment_changed_holdoff)
170 return;
172 ghid_port_ranges_changed ();
175 static void
176 v_adjustment_changed_cb (GtkAdjustment * adj, GhidGui * g)
178 if (g->adjustment_changed_holdoff)
179 return;
181 ghid_port_ranges_changed ();
184 /* Save size of top window changes so PCB can restart at its size at exit.
186 static gint
187 top_window_configure_event_cb (GtkWidget * widget, GdkEventConfigure * ev,
188 GHidPort * port)
190 GtkAllocation allocation;
191 gboolean new_w, new_h;
193 gtk_widget_get_allocation (widget, &allocation);
195 new_w = (ghidgui->top_window_width != allocation.width);
196 new_h = (ghidgui->top_window_height != allocation.height);
198 ghidgui->top_window_width = allocation.width;
199 ghidgui->top_window_height = allocation.height;
201 if (new_w || new_h)
202 ghidgui->config_modified = TRUE;
204 return FALSE;
207 static void
208 info_bar_response_cb (GtkInfoBar *info_bar,
209 gint response_id,
210 GhidGui *_gui)
212 gtk_widget_destroy (_gui->info_bar);
213 _gui->info_bar = NULL;
215 if (response_id == GTK_RESPONSE_ACCEPT)
216 RevertPCB ();
219 static void
220 close_file_modified_externally_prompt (void)
222 if (ghidgui->info_bar != NULL)
223 gtk_widget_destroy (ghidgui->info_bar);
224 ghidgui->info_bar = NULL;
227 static void
228 show_file_modified_externally_prompt (void)
230 GtkWidget *button;
231 GtkWidget *button_image;
232 GtkWidget *icon;
233 GtkWidget *label;
234 GtkWidget *content_area;
235 char *file_path_utf8;
236 char *secondary_text;
237 char *markup;
239 close_file_modified_externally_prompt ();
241 ghidgui->info_bar = gtk_info_bar_new ();
243 button = gtk_info_bar_add_button (GTK_INFO_BAR (ghidgui->info_bar),
244 _("Reload"),
245 GTK_RESPONSE_ACCEPT);
246 button_image = gtk_image_new_from_stock (GTK_STOCK_REFRESH,
247 GTK_ICON_SIZE_BUTTON);
248 gtk_button_set_image (GTK_BUTTON (button), button_image);
250 gtk_info_bar_add_button (GTK_INFO_BAR (ghidgui->info_bar),
251 GTK_STOCK_CANCEL,
252 GTK_RESPONSE_CANCEL);
253 gtk_info_bar_set_message_type (GTK_INFO_BAR (ghidgui->info_bar),
254 GTK_MESSAGE_WARNING);
255 gtk_box_pack_start (GTK_BOX (ghidgui->vbox_middle),
256 ghidgui->info_bar, FALSE, FALSE, 0);
257 gtk_box_reorder_child (GTK_BOX (ghidgui->vbox_middle), ghidgui->info_bar, 0);
260 g_signal_connect (ghidgui->info_bar, "response",
261 G_CALLBACK (info_bar_response_cb), ghidgui);
263 file_path_utf8 = g_filename_to_utf8 (PCB->Filename, -1, NULL, NULL, NULL);
265 secondary_text = PCB->Changed ? _("Do you want to drop your changes and reload the file?") :
266 _("Do you want to reload the file?");
268 markup = g_markup_printf_escaped (_("<b>The file %s has changed on disk</b>\n\n%s"),
269 file_path_utf8, secondary_text);
270 g_free (file_path_utf8);
272 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (ghidgui->info_bar));
274 icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
275 GTK_ICON_SIZE_DIALOG);
276 gtk_box_pack_start (GTK_BOX (content_area),
277 icon, FALSE, FALSE, 0);
279 label = gtk_label_new ("");
280 gtk_box_pack_start (GTK_BOX (content_area),
281 label, TRUE, TRUE, 6);
283 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
284 gtk_label_set_markup (GTK_LABEL (label), markup);
285 g_free (markup);
287 gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
289 gtk_widget_show_all (ghidgui->info_bar);
292 static bool
293 check_externally_modified (void)
295 GFile *file;
296 GFileInfo *info;
297 GTimeVal timeval;
299 /* Treat zero time as a flag to indicate we've not got an mtime yet */
300 if (PCB->Filename == NULL ||
301 (ghidgui->our_mtime.tv_sec == 0 &&
302 ghidgui->our_mtime.tv_usec == 0))
303 return false;
305 file = g_file_new_for_path (PCB->Filename);
306 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
307 G_FILE_QUERY_INFO_NONE, NULL, NULL);
308 g_object_unref (file);
310 if (info == NULL ||
311 !g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
312 return false;
314 g_file_info_get_modification_time (info, &timeval); //&ghidgui->last_seen_mtime);
315 g_object_unref (info);
317 /* Ignore when the file on disk is the same age as when we last looked */
318 if (timeval.tv_sec == ghidgui->last_seen_mtime.tv_sec &&
319 timeval.tv_usec == ghidgui->last_seen_mtime.tv_usec)
320 return false;
322 ghidgui->last_seen_mtime = timeval;
324 return (ghidgui->last_seen_mtime.tv_sec > ghidgui->our_mtime.tv_sec) ||
325 (ghidgui->last_seen_mtime.tv_sec == ghidgui->our_mtime.tv_sec &&
326 ghidgui->last_seen_mtime.tv_usec > ghidgui->our_mtime.tv_usec);
329 static gboolean
330 top_window_enter_cb (GtkWidget *widget, GdkEvent *event, GHidPort *port)
332 if (check_externally_modified ())
333 show_file_modified_externally_prompt ();
335 return FALSE;
338 /*! \brief Menu action callback function
339 * \par Function Description
340 * This is the main menu callback function. The callback receives
341 * the original Resource pointer containing the HID actions to be
342 * executed.
344 * All hotkeys go through the menus which means they go through here.
345 * Some, such as tab, are caught by Gtk instead of passed here, so
346 * pcb calls this function directly through ghid_hotkey_cb() for them.
348 * \param [in] The action that was activated
349 * \param [in] The menu resource associated with the action
352 static void
353 ghid_menu_cb (GtkAction *action, const Resource *node)
355 int i;
357 if (action == NULL || node == NULL)
358 return;
360 for (i = 1; i < node->c; i++)
361 if (resource_type (node->v[i]) == 10)
363 #ifdef DEBUG_MENUS
364 printf (" %s\n", node->v[i].value);
365 #endif
366 hid_parse_actions (node->v[i].value);
369 /* Sync gui widgets with pcb state */
370 ghid_update_toggle_flags ();
371 ghid_mode_buttons_update ();
373 /* Sync gui status display with pcb state */
374 AdjustAttachedObjects ();
375 ghid_invalidate_all ();
376 ghid_window_set_name_label (PCB->Name);
377 ghid_set_status_line_label ();
380 /* \brief Accelerator callback for accelerators gtk tries to hide from us */
381 void ghid_hotkey_cb (int which)
383 if (ghid_hotkey_actions[which].action != NULL)
384 ghid_menu_cb (ghid_hotkey_actions[which].action,
385 (gpointer) ghid_hotkey_actions[which].node);
388 static void
389 update_board_mtime_from_disk (void)
391 GFile *file;
392 GFileInfo *info;
394 ghidgui->our_mtime.tv_sec = 0;
395 ghidgui->our_mtime.tv_usec = 0;
396 ghidgui->last_seen_mtime = ghidgui->our_mtime;
398 if (PCB->Filename == NULL)
399 return;
401 file = g_file_new_for_path (PCB->Filename);
402 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
403 G_FILE_QUERY_INFO_NONE, NULL, NULL);
404 g_object_unref (file);
406 if (info == NULL ||
407 !g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
408 return;
410 g_file_info_get_modification_time (info, &ghidgui->our_mtime);
411 g_object_unref (info);
413 ghidgui->last_seen_mtime = ghidgui->our_mtime;
416 /* Sync toggle states that were saved with the layout and notify the
417 | config code to update Settings values it manages.
419 void
420 ghid_sync_with_new_layout (void)
422 pcb_use_route_style (&PCB->RouteStyle[0]);
423 ghid_route_style_selector_select_style
424 (GHID_ROUTE_STYLE_SELECTOR (ghidgui->route_style_selector),
425 &PCB->RouteStyle[0]);
427 ghid_config_handle_units_changed ();
429 ghid_window_set_name_label (PCB->Name);
430 ghid_set_status_line_label ();
431 close_file_modified_externally_prompt ();
432 update_board_mtime_from_disk ();
435 void
436 ghid_notify_save_pcb (const char *filename, bool done)
438 /* Do nothing if it is not the active PCB file that is being saved.
440 if (PCB->Filename == NULL || strcmp (filename, PCB->Filename) != 0)
441 return;
443 if (done)
444 update_board_mtime_from_disk ();
447 void
448 ghid_notify_filename_changed (void)
450 /* Pick up the mtime of the new PCB file */
451 update_board_mtime_from_disk ();
454 /* ---------------------------------------------------------------------------
456 * layer_process()
458 * Takes the index into the layers and produces the text string for
459 * the layer and if the layer is currently visible or not. This is
460 * used by a couple of functions.
463 void
464 layer_process (gchar **color_string, char **text, int *set, int i)
466 int tmp;
467 char *tmps;
468 gchar *tmpc;
470 /* cheap hack to let users pass in NULL for either text or set if
471 * they don't care about the result
474 if (color_string == NULL)
475 color_string = &tmpc;
477 if (text == NULL)
478 text = &tmps;
480 if (set == NULL)
481 set = &tmp;
483 switch (i)
485 case LAYER_BUTTON_SILK:
486 *color_string = Settings.ElementColor;
487 if (Settings.ShowBottomSide)
488 *text = (char *)UNKNOWN (PCB->Data->Layer[bottom_silk_layer].Name);
489 else
490 *text = (char *)UNKNOWN (PCB->Data->Layer[top_silk_layer].Name);
491 *set = PCB->ElementOn;
492 break;
493 case LAYER_BUTTON_RATS:
494 *color_string = Settings.RatColor;
495 *text = _( "rat lines");
496 *set = PCB->RatOn;
497 break;
498 case LAYER_BUTTON_PINS:
499 *color_string = Settings.PinColor;
500 *text = _( "pins/pads");
501 *set = PCB->PinOn;
502 break;
503 case LAYER_BUTTON_VIAS:
504 *color_string = Settings.ViaColor;
505 *text = _( "vias");
506 *set = PCB->ViaOn;
507 break;
508 case LAYER_BUTTON_FARSIDE:
509 *color_string = Settings.InvisibleObjectsColor;
510 *text = _( "far side");
511 *set = PCB->InvisibleObjectsOn;
512 break;
513 case LAYER_BUTTON_MASK:
514 *color_string = Settings.MaskColor;
515 *text = _( "solder mask");
516 *set = TEST_FLAG (SHOWMASKFLAG, PCB);
517 break;
518 default: /* layers */
519 *color_string = Settings.LayerColor[i];
520 *text = (char *)UNKNOWN (PCB->Data->Layer[i].Name);
521 *set = PCB->Data->Layer[i].On;
522 break;
526 /*! \brief Callback for GHidLayerSelector layer selection */
527 static void
528 layer_selector_select_callback (GHidLayerSelector *ls, int layer, gpointer d)
530 ignore_layer_update = true;
531 /* Select Layer */
532 PCB->SilkActive = (layer == LAYER_BUTTON_SILK);
533 PCB->SolderMaskActive = (layer == LAYER_BUTTON_MASK);
534 PCB->RatDraw = (layer == LAYER_BUTTON_RATS);
535 if (layer == LAYER_BUTTON_SILK)
537 PCB->ElementOn = true;
538 hid_action ("LayersChanged");
540 else if (layer == LAYER_BUTTON_RATS)
542 PCB->RatOn = true;
543 hid_action ("LayersChanged");
545 else if (layer == LAYER_BUTTON_MASK)
547 SET_FLAG (SHOWMASKFLAG, PCB);
548 hid_action ("LayersChanged");
550 else if (layer < max_copper_layer)
551 ChangeGroupVisibility (layer, TRUE, true);
553 ignore_layer_update = false;
555 ghid_invalidate_all ();
558 /*! \brief Callback for GHidLayerSelector layer renaming */
559 static void
560 layer_selector_rename_callback (GHidLayerSelector *ls,
561 int layer_id,
562 char *new_name,
563 void *userdata)
565 LayerType *layer = LAYER_PTR (layer_id);
567 /* Check for a legal layer name - for now, allow anything non-empty */
568 if (new_name[0] == '\0')
569 return;
571 /* Don't bother if the name is identical to the current one */
572 if (strcmp (layer->Name, new_name) == 0)
573 return;
575 free (layer->Name);
576 layer->Name = strdup (new_name);
577 ghid_layer_buttons_update ();
578 if (!PCB->Changed)
580 SetChangedFlag (true);
581 ghid_window_set_name_label (PCB->Name);
585 /*! \brief Callback for GHidLayerSelector layer toggling */
586 static void
587 layer_selector_toggle_callback (GHidLayerSelector *ls, int layer, gpointer d)
589 gboolean redraw = FALSE;
590 gboolean active;
591 layer_process (NULL, NULL, &active, layer);
593 active = !active;
594 ignore_layer_update = true;
595 switch (layer)
597 case LAYER_BUTTON_SILK:
598 PCB->ElementOn = active;
599 PCB->Data->SILKLAYER.On = PCB->ElementOn;
600 PCB->Data->BACKSILKLAYER.On = PCB->ElementOn;
601 redraw = 1;
602 break;
603 case LAYER_BUTTON_RATS:
604 PCB->RatOn = active;
605 redraw = 1;
606 break;
607 case LAYER_BUTTON_PINS:
608 PCB->PinOn = active;
609 redraw |= (PCB->Data->ElementN != 0);
610 break;
611 case LAYER_BUTTON_VIAS:
612 PCB->ViaOn = active;
613 redraw |= (PCB->Data->ViaN != 0);
614 break;
615 case LAYER_BUTTON_FARSIDE:
616 PCB->InvisibleObjectsOn = active;
617 PCB->Data->BACKSILKLAYER.On = (active && PCB->ElementOn);
618 PCB->Data->BACKSOLDERMASKLAYER.On = (active && TEST_FLAG (SHOWMASKFLAG, PCB));
619 redraw = TRUE;
620 break;
621 case LAYER_BUTTON_MASK:
622 if (active)
623 SET_FLAG (SHOWMASKFLAG, PCB);
624 else
625 CLEAR_FLAG (SHOWMASKFLAG, PCB);
626 PCB->Data->SOLDERMASKLAYER.On = TEST_FLAG (SHOWMASKFLAG, PCB);
627 PCB->Data->BACKSOLDERMASKLAYER.On = TEST_FLAG (SHOWMASKFLAG, PCB);
628 redraw = TRUE;
629 break;
630 default:
631 /* Flip the visibility */
632 ChangeGroupVisibility (layer, active, false);
633 redraw = TRUE;
634 break;
637 /* Select the next visible layer. (If there is none, this will
638 * select the currently-selected layer, triggering the selection
639 * callback, which will turn the visibility on.) This way we
640 * will never have an invisible layer selected.
642 if (!active)
643 ghid_layer_selector_select_next_visible (ls);
645 ignore_layer_update = false;
647 if (redraw)
648 ghid_invalidate_all();
651 /*! \brief Install menu bar and accelerator groups */
652 void
653 ghid_install_accel_groups (GtkWindow *window, GhidGui *gui)
655 gtk_window_add_accel_group
656 (window, ghid_main_menu_get_accel_group
657 (GHID_MAIN_MENU (gui->menu_bar)));
658 gtk_window_add_accel_group
659 (window, ghid_layer_selector_get_accel_group
660 (GHID_LAYER_SELECTOR (gui->layer_selector)));
661 gtk_window_add_accel_group
662 (window, ghid_route_style_selector_get_accel_group
663 (GHID_ROUTE_STYLE_SELECTOR (gui->route_style_selector)));
666 /*! \brief Remove menu bar and accelerator groups */
667 void
668 ghid_remove_accel_groups (GtkWindow *window, GhidGui *gui)
670 gtk_window_remove_accel_group
671 (window, ghid_main_menu_get_accel_group
672 (GHID_MAIN_MENU (gui->menu_bar)));
673 gtk_window_remove_accel_group
674 (window, ghid_layer_selector_get_accel_group
675 (GHID_LAYER_SELECTOR (gui->layer_selector)));
676 gtk_window_remove_accel_group
677 (window, ghid_route_style_selector_get_accel_group
678 (GHID_ROUTE_STYLE_SELECTOR (gui->route_style_selector)));
681 /* Refreshes the window title bar and sets the PCB name to the
682 * window title bar or to a seperate label
684 void
685 ghid_window_set_name_label (gchar * name)
687 gchar *str;
688 gchar *filename;
690 /* FIXME -- should this happen? It does... */
691 /* This happens if we're calling an exporter from the command line */
692 if (ghidgui == NULL)
693 return;
695 dup_string (&(ghidgui->name_label_string), name);
696 if (!ghidgui->name_label_string || !*ghidgui->name_label_string)
697 ghidgui->name_label_string = g_strdup (_("Unnamed"));
699 if (!PCB->Filename || !*PCB->Filename)
700 filename = g_strdup(_("Unsaved.pcb"));
701 else
702 filename = g_strdup(PCB->Filename);
704 str = g_strdup_printf ("%s%s (%s) - PCB", PCB->Changed ? "*": "",
705 ghidgui->name_label_string, filename);
706 gtk_window_set_title (GTK_WINDOW (gport->top_window), str);
707 g_free (str);
708 g_free (filename);
711 static void
712 grid_units_button_cb (GtkWidget * widget, gpointer data)
714 /* Button only toggles between mm and mil */
715 if (Settings.grid_unit == get_unit_struct ("mm"))
716 hid_actionl ("SetUnits", "mil", NULL);
717 else
718 hid_actionl ("SetUnits", "mm", NULL);
722 * The two following callbacks are used to keep the absolute
723 * and relative cursor labels from growing and shrinking as you
724 * move the cursor around.
726 static void
727 absolute_label_size_req_cb (GtkWidget * widget,
728 GtkRequisition *req, gpointer data)
731 static gint w = 0;
732 if (req->width > w)
733 w = req->width;
734 else
735 req->width = w;
738 static void
739 relative_label_size_req_cb (GtkWidget * widget,
740 GtkRequisition *req, gpointer data)
743 static gint w = 0;
744 if (req->width > w)
745 w = req->width;
746 else
747 req->width = w;
750 static void
751 make_cursor_position_labels (GtkWidget * hbox, GHidPort * port)
753 GtkWidget *frame, *label;
755 /* The grid units button next to the cursor position labels.
757 ghidgui->grid_units_button = gtk_button_new ();
758 label = gtk_label_new ("");
759 gtk_label_set_markup (GTK_LABEL (label),
760 Settings.grid_unit->in_suffix);
761 ghidgui->grid_units_label = label;
762 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
763 gtk_container_add (GTK_CONTAINER (ghidgui->grid_units_button), label);
764 gtk_box_pack_end (GTK_BOX (hbox), ghidgui->grid_units_button, FALSE, TRUE, 0);
765 g_signal_connect (ghidgui->grid_units_button, "clicked",
766 G_CALLBACK (grid_units_button_cb), NULL);
768 /* The absolute cursor position label
770 frame = gtk_frame_new (NULL);
771 gtk_box_pack_end (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
772 gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
773 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
775 label = gtk_label_new ("");
776 gtk_container_add (GTK_CONTAINER (frame), label);
777 ghidgui->cursor_position_absolute_label = label;
778 g_signal_connect (G_OBJECT (label), "size-request",
779 G_CALLBACK (absolute_label_size_req_cb), NULL);
782 /* The relative cursor position label
784 frame = gtk_frame_new (NULL);
785 gtk_box_pack_end (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
786 gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
787 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
788 label = gtk_label_new (" __.__ __.__ ");
789 gtk_container_add (GTK_CONTAINER (frame), label);
790 ghidgui->cursor_position_relative_label = label;
791 g_signal_connect (G_OBJECT (label), "size-request",
792 G_CALLBACK (relative_label_size_req_cb), NULL);
796 /* \brief Add "virtual layers" to a layer selector */
797 static void
798 make_virtual_layer_buttons (GtkWidget *layer_selector)
800 GHidLayerSelector *layersel = GHID_LAYER_SELECTOR (layer_selector);
801 gchar *text;
802 gchar *color_string;
803 gboolean active;
805 layer_process (&color_string, &text, &active, LAYER_BUTTON_SILK);
806 ghid_layer_selector_add_layer (layersel, LAYER_BUTTON_SILK,
807 text, color_string, active, TRUE, FALSE);
808 layer_process (&color_string, &text, &active, LAYER_BUTTON_RATS);
809 ghid_layer_selector_add_layer (layersel, LAYER_BUTTON_RATS,
810 text, color_string, active, TRUE, FALSE);
811 layer_process (&color_string, &text, &active, LAYER_BUTTON_PINS);
812 ghid_layer_selector_add_layer (layersel, LAYER_BUTTON_PINS,
813 text, color_string, active, FALSE, FALSE);
814 layer_process (&color_string, &text, &active, LAYER_BUTTON_VIAS);
815 ghid_layer_selector_add_layer (layersel, LAYER_BUTTON_VIAS,
816 text, color_string, active, FALSE, FALSE);
817 layer_process (&color_string, &text, &active, LAYER_BUTTON_FARSIDE);
818 ghid_layer_selector_add_layer (layersel, LAYER_BUTTON_FARSIDE,
819 text, color_string, active, FALSE, FALSE);
820 layer_process (&color_string, &text, &active, LAYER_BUTTON_MASK);
821 ghid_layer_selector_add_layer (layersel, LAYER_BUTTON_MASK,
822 text, color_string, active, TRUE, FALSE);
825 /*! \brief callback for ghid_layer_selector_update_colors */
826 const gchar *
827 get_layer_color (gint layer)
829 gchar *rv;
830 layer_process (&rv, NULL, NULL, layer);
831 return rv;
834 /*! \brief Update a layer selector's color scheme */
835 void
836 ghid_layer_buttons_color_update (void)
838 ghid_layer_selector_update_colors
839 (GHID_LAYER_SELECTOR (ghidgui->layer_selector), get_layer_color);
840 pcb_colors_from_settings (PCB);
843 /*! \brief Populate a layer selector with all layers Gtk is aware of */
844 static void
845 make_layer_buttons (GtkWidget *layersel)
847 gint i;
848 gchar *text;
849 gchar *color_string;
850 gboolean active = TRUE;
852 for (i = 0; i < max_copper_layer; ++i)
854 layer_process (&color_string, &text, &active, i);
855 ghid_layer_selector_add_layer (GHID_LAYER_SELECTOR (layersel), i,
856 text, color_string, active, TRUE, TRUE);
861 /*! \brief callback for ghid_layer_selector_delete_layers */
862 gboolean
863 get_layer_delete (gint layer)
865 return layer >= max_copper_layer;
868 /*! \brief Synchronize layer selector widget with current PCB state
869 * \par Function Description
870 * Called when user toggles layer visibility or changes drawing layer,
871 * or when layer visibility is changed programatically.
873 void
874 ghid_layer_buttons_update (void)
876 gint layer;
878 if (ignore_layer_update)
879 return;
881 ghid_layer_selector_delete_layers
882 (GHID_LAYER_SELECTOR (ghidgui->layer_selector),
883 get_layer_delete);
884 make_layer_buttons (ghidgui->layer_selector);
885 make_virtual_layer_buttons (ghidgui->layer_selector);
886 ghid_main_menu_install_layer_selector
887 (GHID_MAIN_MENU (ghidgui->menu_bar),
888 GHID_LAYER_SELECTOR (ghidgui->layer_selector));
890 /* Sync selected layer with PCB's state */
891 if (PCB->RatDraw)
892 layer = LAYER_BUTTON_RATS;
893 else if (PCB->SilkActive)
894 layer = LAYER_BUTTON_SILK;
895 else if (PCB->SolderMaskActive)
896 layer = LAYER_BUTTON_MASK;
897 else
898 layer = LayerStack[0];
900 ghid_layer_selector_select_layer
901 (GHID_LAYER_SELECTOR (ghidgui->layer_selector), layer);
904 /*! \brief Called when user clicks OK on route style dialog */
905 static void
906 route_styles_edited_cb (GHidRouteStyleSelector *rss, gboolean save,
907 gpointer data)
909 if (save)
911 g_free (Settings.Routes);
912 Settings.Routes = make_route_string (PCB->RouteStyle, NUM_STYLES);
913 ghidgui->config_modified = TRUE;
914 ghid_config_files_write ();
916 ghid_main_menu_install_route_style_selector
917 (GHID_MAIN_MENU (ghidgui->menu_bar),
918 GHID_ROUTE_STYLE_SELECTOR (ghidgui->route_style_selector));
921 /*! \brief Called when a route style is selected */
922 static void
923 route_style_changed_cb (GHidRouteStyleSelector *rss, RouteStyleType *rst,
924 gpointer data)
926 pcb_use_route_style (rst);
927 ghid_set_status_line_label();
930 /*! \brief Configure the route style selector */
931 void
932 make_route_style_buttons (GHidRouteStyleSelector *rss)
934 int i;
935 for (i = 0; i < NUM_STYLES; ++i)
936 ghid_route_style_selector_add_route_style (rss, &PCB->RouteStyle[i]);
937 g_signal_connect (G_OBJECT (rss), "select_style",
938 G_CALLBACK (route_style_changed_cb), NULL);
939 g_signal_connect (G_OBJECT (rss), "style_edited",
940 G_CALLBACK (route_styles_edited_cb), NULL);
941 ghid_main_menu_install_route_style_selector
942 (GHID_MAIN_MENU (ghidgui->menu_bar),
943 GHID_ROUTE_STYLE_SELECTOR (ghidgui->route_style_selector));
947 * ---------------------------------------------------------------
948 * Mode buttons
950 typedef struct
952 GtkWidget *button;
953 GtkWidget *toolbar_button;
954 guint button_cb_id;
955 guint toolbar_button_cb_id;
956 gchar *name;
957 gint mode;
958 gchar **xpm;
959 gchar *tooltip;
961 ModeButton;
964 static ModeButton mode_buttons[] = {
965 {NULL, NULL, 0, 0, N_("via"), VIA_MODE, via,
966 N_("create vias with <select mouse button>")},
967 {NULL, NULL, 0, 0, N_("line"), LINE_MODE, line,
968 N_("create a line segment, toggle draw modes with '/' or '.'")},
969 {NULL, NULL, 0, 0, N_("arc"), ARC_MODE, arc,
970 N_("create an arc segment")},
971 {NULL, NULL, 0, 0, N_("text"), TEXT_MODE, text,
972 N_("create a text")},
973 {NULL, NULL, 0, 0, N_("rectangle"), RECTANGLE_MODE, rect,
974 N_("create a filled rectangle")},
975 {NULL, NULL, 0, 0, N_("polygon"), POLYGON_MODE, poly,
976 N_("create a polygon, <shift>-P for closing the polygon")},
977 {NULL, NULL, 0, 0, N_("polygonhole"), POLYGONHOLE_MODE, polyhole,
978 N_("create a hole into an existing polygon")},
979 {NULL, NULL, 0, 0, N_("buffer"), PASTEBUFFER_MODE, buf,
980 N_("paste the selection from buffer into the layout")},
981 {NULL, NULL, 0, 0, N_("remove"), REMOVE_MODE, del,
982 N_("remove objects under the cursor")},
983 {NULL, NULL, 0, 0, N_("rotate"), ROTATE_MODE, rot,
984 N_("rotate a selection or object CCW, hold the <shift> key to rotate CW")},
985 {NULL, NULL, 0, 0, N_("insertPoint"), INSERTPOINT_MODE, ins,
986 N_("add points into existing lines and polygons")},
987 {NULL, NULL, 0, 0, N_("thermal"), THERMAL_MODE, thrm,
988 N_("create thermals with <select mouse button>, toggle thermal style with <Shift> <select mouse button>")},
989 {NULL, NULL, 0, 0, N_("select"), ARROW_MODE, sel,
990 N_("select, deselect or move objects or selections")},
991 {NULL, NULL, 0, 0, N_("lock"), LOCK_MODE, lock,
992 N_("lock or unlock an object")}
995 static gint n_mode_buttons = G_N_ELEMENTS (mode_buttons);
997 static void
998 do_set_mode (int mode)
1000 SetMode (mode);
1001 ghid_mode_cursor (mode);
1002 ghidgui->settings_mode = mode;
1005 static void
1006 mode_toolbar_button_toggled_cb (GtkToggleButton *button, ModeButton * mb)
1008 gboolean active = gtk_toggle_button_get_active (button);
1010 if (mb->button != NULL)
1012 g_signal_handler_block (mb->button, mb->button_cb_id);
1013 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mb->button), active);
1014 g_signal_handler_unblock (mb->button, mb->button_cb_id);
1017 if (active)
1018 do_set_mode (mb->mode);
1021 static void
1022 mode_button_toggled_cb (GtkWidget * widget, ModeButton * mb)
1024 gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
1026 if (mb->toolbar_button != NULL)
1028 g_signal_handler_block (mb->toolbar_button, mb->toolbar_button_cb_id);
1029 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mb->toolbar_button), active);
1030 g_signal_handler_unblock (mb->toolbar_button, mb->toolbar_button_cb_id);
1033 if (active)
1034 do_set_mode (mb->mode);
1037 void
1038 ghid_mode_buttons_update (void)
1040 ModeButton *mb;
1041 gint i;
1043 for (i = 0; i < n_mode_buttons; ++i)
1045 mb = &mode_buttons[i];
1046 if (Settings.Mode == mb->mode)
1048 g_signal_handler_block (mb->button, mb->button_cb_id);
1049 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mb->button), TRUE);
1050 g_signal_handler_unblock (mb->button, mb->button_cb_id);
1052 g_signal_handler_block (mb->toolbar_button, mb->toolbar_button_cb_id);
1053 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mb->toolbar_button), TRUE);
1054 g_signal_handler_unblock (mb->toolbar_button, mb->toolbar_button_cb_id);
1055 break;
1060 void
1061 ghid_pack_mode_buttons (void)
1063 if (ghidgui->compact_vertical)
1065 gtk_widget_hide (ghidgui->mode_buttons_frame);
1066 gtk_widget_show_all (ghidgui->mode_toolbar);
1068 else
1070 gtk_widget_hide (ghidgui->mode_toolbar);
1071 gtk_widget_show_all (ghidgui->mode_buttons_frame);
1075 static void
1076 make_mode_buttons_and_toolbar (GtkWidget **mode_frame,
1077 GtkWidget **mode_toolbar)
1079 GtkToolItem *tool_item;
1080 GtkWidget *vbox, *hbox = NULL;
1081 GtkWidget *image;
1082 GdkPixbuf *pixbuf;
1083 GSList *group = NULL;
1084 GSList *toolbar_group = NULL;
1085 ModeButton *mb;
1086 int i;
1088 *mode_toolbar = gtk_toolbar_new ();
1090 *mode_frame = gtk_frame_new (NULL);
1091 vbox = gtk_vbox_new (FALSE, 0);
1092 gtk_container_add (GTK_CONTAINER (*mode_frame), vbox);
1094 for (i = 0; i < n_mode_buttons; ++i)
1096 mb = &mode_buttons[i];
1098 /* Create tool button for mode frame */
1099 mb->button = gtk_radio_button_new (group);
1100 gtk_widget_set_tooltip_text (mb->button, _(mb->tooltip));
1101 gtk_widget_set_name (mb->button, (mb->name));
1102 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (mb->button));
1103 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (mb->button), FALSE);
1105 /* Create tool button for toolbar */
1106 mb->toolbar_button = gtk_radio_button_new (toolbar_group);
1107 gtk_widget_set_tooltip_text (mb->toolbar_button, _(mb->tooltip));
1108 gtk_widget_set_name (mb->toolbar_button, (mb->name));
1109 toolbar_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (mb->toolbar_button));
1110 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (mb->toolbar_button), FALSE);
1112 /* Pack mode-frame button into the frame */
1113 if ((i % ghidgui->n_mode_button_columns) == 0)
1115 hbox = gtk_hbox_new (FALSE, 0);
1116 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1118 gtk_box_pack_start (GTK_BOX (hbox), mb->button, FALSE, FALSE, 0);
1120 /* Create a container for the toolbar button and add that */
1121 tool_item = gtk_tool_item_new ();
1122 gtk_container_add (GTK_CONTAINER (tool_item), mb->toolbar_button);
1123 gtk_toolbar_insert (GTK_TOOLBAR (*mode_toolbar), tool_item, -1);
1125 /* Load the image for the button, create GtkImage widgets for both
1126 * the grid button and the toolbar button, then pack into the buttons
1128 pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) mb->xpm);
1129 image = gtk_image_new_from_pixbuf (pixbuf);
1130 gtk_container_add (GTK_CONTAINER (mb->button), image);
1131 image = gtk_image_new_from_pixbuf (pixbuf);
1132 gtk_container_add (GTK_CONTAINER (mb->toolbar_button), image);
1133 g_object_unref (pixbuf);
1135 if (strcmp (mb->name, "select") == 0)
1137 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mb->button), TRUE);
1138 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mb->toolbar_button), TRUE);
1141 mb->button_cb_id =
1142 g_signal_connect (mb->button, "toggled",
1143 G_CALLBACK (mode_button_toggled_cb), mb);
1144 mb->toolbar_button_cb_id =
1145 g_signal_connect (mb->toolbar_button, "toggled",
1146 G_CALLBACK (mode_toolbar_button_toggled_cb), mb);
1152 * ---------------------------------------------------------------
1153 * Top window
1154 * ---------------------------------------------------------------
1157 static gint
1158 delete_chart_cb (GtkWidget * widget, GdkEvent * event, GHidPort * port)
1160 ghid_config_files_write ();
1161 hid_action ("Quit");
1164 * Return TRUE to keep our app running. A FALSE here would let the
1165 * delete signal continue on and kill our program.
1167 return TRUE;
1170 static void
1171 destroy_chart_cb (GtkWidget * widget, GHidPort * port)
1173 ghid_shutdown_renderer (port);
1174 gtk_main_quit ();
1177 static void
1178 get_widget_styles (GtkStyle **menu_bar_style,
1179 GtkStyle **tool_button_style,
1180 GtkStyle **tool_button_label_style)
1182 GtkWidget *tool_button;
1183 GtkWidget *tool_button_label;
1184 GtkToolItem *tool_item;
1186 /* Build a tool item to extract the theme's styling for a toolbar button with text */
1187 tool_item = gtk_tool_item_new ();
1188 gtk_toolbar_insert (GTK_TOOLBAR (ghidgui->mode_toolbar), tool_item, 0);
1189 tool_button = gtk_button_new ();
1190 gtk_container_add (GTK_CONTAINER (tool_item), tool_button);
1191 tool_button_label = gtk_label_new ("");
1192 gtk_container_add (GTK_CONTAINER (tool_button), tool_button_label);
1194 /* Grab the various styles we need */
1195 gtk_widget_ensure_style (ghidgui->menu_bar);
1196 *menu_bar_style = gtk_widget_get_style (ghidgui->menu_bar);
1198 gtk_widget_ensure_style (tool_button);
1199 *tool_button_style = gtk_widget_get_style (tool_button);
1201 gtk_widget_ensure_style (tool_button_label);
1202 *tool_button_label_style = gtk_widget_get_style (tool_button_label);
1204 gtk_widget_destroy (GTK_WIDGET (tool_item));
1207 static void
1208 do_fix_topbar_theming (void)
1210 GtkWidget *rel_pos_frame;
1211 GtkWidget *abs_pos_frame;
1212 GtkStyle *menu_bar_style;
1213 GtkStyle *tool_button_style;
1214 GtkStyle *tool_button_label_style;
1216 get_widget_styles (&menu_bar_style,
1217 &tool_button_style,
1218 &tool_button_label_style);
1220 /* Style the top bar background as if it were all a menu bar */
1221 gtk_widget_set_style (ghidgui->top_bar_background, menu_bar_style);
1223 /* Style the cursor position labels using the menu bar style as well.
1224 * If this turns out to cause problems with certain gtk themes, we may
1225 * need to grab the GtkStyle associated with an actual menu item to
1226 * get a text color to render with.
1228 gtk_widget_set_style (ghidgui->cursor_position_relative_label, menu_bar_style);
1229 gtk_widget_set_style (ghidgui->cursor_position_absolute_label, menu_bar_style);
1231 /* Style the units button as if it were a toolbar button - hopefully
1232 * this isn't too ugly sitting on a background themed as a menu bar.
1233 * It is unlikely any theme defines colours for a GtkButton sitting on
1234 * a menu bar.
1236 rel_pos_frame = gtk_widget_get_parent (ghidgui->cursor_position_relative_label);
1237 abs_pos_frame = gtk_widget_get_parent (ghidgui->cursor_position_absolute_label);
1238 gtk_widget_set_style (rel_pos_frame, menu_bar_style);
1239 gtk_widget_set_style (abs_pos_frame, menu_bar_style);
1240 gtk_widget_set_style (ghidgui->grid_units_button, tool_button_style);
1241 gtk_widget_set_style (ghidgui->grid_units_label, tool_button_label_style);
1244 /* Attempt to produce a conststent style for our extra menu-bar items by
1245 * copying aspects from the menu bar style set by the user's GTK theme.
1246 * Setup signal handlers to update our efforts if the user changes their
1247 * theme whilst we are running.
1249 static void
1250 fix_topbar_theming (void)
1252 GtkSettings *settings;
1254 do_fix_topbar_theming ();
1256 settings = gtk_widget_get_settings (ghidgui->top_bar_background);
1257 g_signal_connect (settings, "notify::gtk-theme-name",
1258 G_CALLBACK (do_fix_topbar_theming), NULL);
1259 g_signal_connect (settings, "notify::gtk-font-name",
1260 G_CALLBACK (do_fix_topbar_theming), NULL);
1264 * Create the top_window contents. The config settings should be loaded
1265 * before this is called.
1267 static void
1268 ghid_build_pcb_top_window (void)
1270 GtkWidget *window;
1271 GtkWidget *vbox_main, *hbox_middle, *hbox;
1272 GtkWidget *vbox, *frame;
1273 GtkWidget *label;
1274 /* FIXME: IFDEF HACK */
1275 #ifdef ENABLE_GL
1276 GtkWidget *trackball;
1277 #endif
1278 GHidPort *port = &ghid_port;
1279 GtkWidget *scrolled;
1281 window = gport->top_window;
1283 vbox_main = gtk_vbox_new (FALSE, 0);
1284 gtk_container_add (GTK_CONTAINER (window), vbox_main);
1286 /* -- Top control bar */
1287 ghidgui->top_bar_background = gtk_event_box_new ();
1288 gtk_box_pack_start (GTK_BOX (vbox_main),
1289 ghidgui->top_bar_background, FALSE, FALSE, 0);
1291 ghidgui->top_hbox = gtk_hbox_new (FALSE, 4);
1292 gtk_container_add (GTK_CONTAINER (ghidgui->top_bar_background),
1293 ghidgui->top_hbox);
1296 * menu_hbox will be made insensitive when the gui needs
1297 * a modal button GetLocation button press.
1299 ghidgui->menu_hbox = gtk_hbox_new (FALSE, 0);
1300 gtk_box_pack_start (GTK_BOX (ghidgui->top_hbox), ghidgui->menu_hbox,
1301 FALSE, FALSE, 0);
1303 ghidgui->menubar_toolbar_vbox = gtk_vbox_new (FALSE, 0);
1304 gtk_box_pack_start (GTK_BOX (ghidgui->menu_hbox),
1305 ghidgui->menubar_toolbar_vbox, FALSE, FALSE, 0);
1307 /* Build layer menus */
1308 ghidgui->layer_selector = ghid_layer_selector_new ();
1309 make_layer_buttons (ghidgui->layer_selector);
1310 make_virtual_layer_buttons (ghidgui->layer_selector);
1311 g_signal_connect (G_OBJECT (ghidgui->layer_selector), "select-layer",
1312 G_CALLBACK (layer_selector_select_callback),
1313 NULL);
1314 g_signal_connect (G_OBJECT (ghidgui->layer_selector), "toggle-layer",
1315 G_CALLBACK (layer_selector_toggle_callback),
1316 NULL);
1317 g_signal_connect (G_OBJECT (ghidgui->layer_selector), "rename-layer",
1318 G_CALLBACK (layer_selector_rename_callback),
1319 NULL);
1320 /* Build main menu */
1321 ghidgui->menu_bar = ghid_load_menus ();
1322 gtk_box_pack_start (GTK_BOX (ghidgui->menubar_toolbar_vbox),
1323 ghidgui->menu_bar, FALSE, FALSE, 0);
1325 make_mode_buttons_and_toolbar (&ghidgui->mode_buttons_frame,
1326 &ghidgui->mode_toolbar);
1327 gtk_box_pack_start (GTK_BOX (ghidgui->menubar_toolbar_vbox),
1328 ghidgui->mode_toolbar, FALSE, FALSE, 0);
1331 ghidgui->position_hbox = gtk_hbox_new (FALSE, 0);
1332 gtk_box_pack_end (GTK_BOX(ghidgui->top_hbox),
1333 ghidgui->position_hbox, FALSE, FALSE, 4);
1335 make_cursor_position_labels (ghidgui->position_hbox, port);
1337 hbox_middle = gtk_hbox_new (FALSE, 0);
1338 gtk_box_pack_start (GTK_BOX (vbox_main), hbox_middle, TRUE, TRUE, 3);
1340 fix_topbar_theming (); /* Must be called after toolbar is created */
1342 /* -- Left control bar */
1344 * This box will be made insensitive when the gui needs
1345 * a modal button GetLocation button press.
1347 ghidgui->left_toolbar = gtk_vbox_new (FALSE, 0);
1348 gtk_box_pack_start (GTK_BOX (hbox_middle),
1349 ghidgui->left_toolbar, FALSE, FALSE, 3);
1351 vbox = ghid_scrolled_vbox (ghidgui->left_toolbar, &scrolled,
1352 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1353 gtk_box_pack_start (GTK_BOX(vbox), ghidgui->layer_selector,
1354 FALSE, FALSE, 0);
1356 /* FIXME: IFDEF HACK */
1357 #ifdef ENABLE_GL
1358 trackball = ghid_trackball_new ();
1359 gport->trackball = trackball;
1360 g_signal_connect (trackball, "rotation-changed",
1361 G_CALLBACK (ghid_port_rotate), NULL);
1362 g_signal_connect (trackball, "view-2d-changed",
1363 G_CALLBACK (ghid_view_2d), NULL);
1364 gtk_box_pack_start (GTK_BOX(ghidgui->left_toolbar),
1365 trackball, FALSE, FALSE, 0);
1366 #endif
1368 /* ghidgui->mode_buttons_frame was created above in the call to
1369 * make_mode_buttons_and_toolbar (...);
1371 gtk_box_pack_start (GTK_BOX (ghidgui->left_toolbar),
1372 ghidgui->mode_buttons_frame, FALSE, FALSE, 0);
1374 frame = gtk_frame_new(NULL);
1375 gtk_box_pack_end (GTK_BOX (ghidgui->left_toolbar), frame, FALSE, FALSE, 0);
1376 vbox = gtk_vbox_new(FALSE, 0);
1377 gtk_container_add(GTK_CONTAINER(frame), vbox);
1378 hbox = gtk_hbox_new(FALSE, 0);
1379 gtk_box_pack_start(GTK_BOX (vbox), hbox, FALSE, FALSE, 1);
1380 ghidgui->route_style_selector = ghid_route_style_selector_new ();
1381 make_route_style_buttons
1382 (GHID_ROUTE_STYLE_SELECTOR (ghidgui->route_style_selector));
1383 gtk_box_pack_start(GTK_BOX(hbox), ghidgui->route_style_selector,
1384 FALSE, FALSE, 4);
1386 ghidgui->vbox_middle = gtk_vbox_new (FALSE, 0);
1387 gtk_box_pack_start (GTK_BOX (hbox_middle),
1388 ghidgui->vbox_middle, TRUE, TRUE, 0);
1390 hbox = gtk_hbox_new (FALSE, 0);
1391 gtk_box_pack_start (GTK_BOX (ghidgui->vbox_middle), hbox, TRUE, TRUE, 0);
1393 /* -- The PCB layout output drawing area */
1395 gport->drawing_area = gtk_drawing_area_new ();
1396 ghid_init_drawing_widget (gport->drawing_area, gport);
1398 gtk_widget_add_events (gport->drawing_area, GDK_EXPOSURE_MASK
1399 | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
1400 | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK
1401 | GDK_KEY_RELEASE_MASK | GDK_KEY_PRESS_MASK
1402 | GDK_FOCUS_CHANGE_MASK | GDK_POINTER_MOTION_MASK
1403 | GDK_POINTER_MOTION_HINT_MASK);
1406 * This is required to get the drawing_area key-press-event. Also the
1407 * enter and button press callbacks grab focus to be sure we have it
1408 * when in the drawing_area.
1410 gtk_widget_set_can_focus (gport->drawing_area, TRUE);
1412 gtk_box_pack_start (GTK_BOX (hbox), gport->drawing_area, TRUE, TRUE, 0);
1414 ghidgui->v_adjustment = gtk_adjustment_new (0.0, 0.0, 100.0,
1415 10.0, 10.0, 10.0);
1416 ghidgui->v_range =
1417 gtk_vscrollbar_new (GTK_ADJUSTMENT (ghidgui->v_adjustment));
1419 gtk_box_pack_start (GTK_BOX (hbox), ghidgui->v_range, FALSE, FALSE, 0);
1421 g_signal_connect (G_OBJECT (ghidgui->v_adjustment), "value_changed",
1422 G_CALLBACK (v_adjustment_changed_cb), ghidgui);
1424 ghidgui->h_adjustment = gtk_adjustment_new (0.0, 0.0, 100.0,
1425 10.0, 10.0, 10.0);
1426 ghidgui->h_range =
1427 gtk_hscrollbar_new (GTK_ADJUSTMENT (ghidgui->h_adjustment));
1428 gtk_box_pack_start (GTK_BOX (ghidgui->vbox_middle),
1429 ghidgui->h_range, FALSE, FALSE, 0);
1431 g_signal_connect (G_OBJECT (ghidgui->h_adjustment), "value_changed",
1432 G_CALLBACK (h_adjustment_changed_cb), ghidgui);
1434 /* -- The bottom status line label */
1435 ghidgui->status_line_hbox = gtk_hbox_new (FALSE, 0);
1436 gtk_box_pack_start (GTK_BOX (ghidgui->vbox_middle),
1437 ghidgui->status_line_hbox, FALSE, FALSE, 2);
1439 label = gtk_label_new ("");
1440 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1441 gtk_box_pack_start (GTK_BOX (ghidgui->status_line_hbox), label, FALSE,
1442 FALSE, 0);
1443 ghidgui->status_line_label = label;
1445 /* Depending on user setting, the command_combo_box may get packed into
1446 | the status_line_hbox, but it will happen on demand the first time
1447 | the user does a command entry.
1450 g_signal_connect (G_OBJECT (gport->drawing_area), "realize",
1451 G_CALLBACK (ghid_port_drawing_realize_cb),
1452 port);
1453 g_signal_connect (G_OBJECT (gport->drawing_area), "expose_event",
1454 G_CALLBACK (ghid_drawing_area_expose_cb),
1455 port);
1456 g_signal_connect (G_OBJECT (gport->top_window), "configure_event",
1457 G_CALLBACK (top_window_configure_event_cb), port);
1458 g_signal_connect (gport->top_window, "enter-notify-event",
1459 G_CALLBACK (top_window_enter_cb), port);
1460 g_signal_connect (G_OBJECT (gport->drawing_area), "configure_event",
1461 G_CALLBACK (ghid_port_drawing_area_configure_event_cb),
1462 port);
1465 /* Mouse and key events will need to be intercepted when PCB needs a
1466 | location from the user.
1469 ghid_interface_input_signals_connect ();
1471 g_signal_connect (G_OBJECT (gport->drawing_area), "enter_notify_event",
1472 G_CALLBACK (ghid_port_window_enter_cb), port);
1473 g_signal_connect (G_OBJECT (gport->drawing_area), "leave_notify_event",
1474 G_CALLBACK (ghid_port_window_leave_cb), port);
1475 g_signal_connect (G_OBJECT (gport->drawing_area), "motion_notify_event",
1476 G_CALLBACK (ghid_port_window_motion_cb), port);
1480 g_signal_connect (G_OBJECT (window), "delete_event",
1481 G_CALLBACK (delete_chart_cb), port);
1482 g_signal_connect (G_OBJECT (window), "destroy",
1483 G_CALLBACK (destroy_chart_cb), port);
1485 ghidgui->creating = FALSE;
1487 gtk_widget_show_all (gport->top_window);
1488 ghid_pack_mode_buttons ();
1489 gdk_window_set_back_pixmap (gtk_widget_get_window (gport->drawing_area),
1490 NULL, FALSE);
1492 port->tooltip_update_timeout_id = 0;
1496 /* Connect and disconnect just the signals a g_main_loop() will need.
1497 | Cursor and motion events still need to be handled by the top level
1498 | loop, so don't connect/reconnect these.
1499 | A g_main_loop will be running when PCB wants the user to select a
1500 | location or if command entry is needed in the status line hbox.
1501 | During these times normal button/key presses are intercepted, either
1502 | by new signal handlers or the command_combo_box entry.
1504 static gulong button_press_handler;
1505 static gulong button_release_handler;
1506 static gulong scroll_event_handler;
1507 static gulong key_press_handler;
1508 static gulong key_release_handler;
1510 void
1511 ghid_interface_input_signals_connect (void)
1513 button_press_handler =
1514 g_signal_connect (G_OBJECT (gport->drawing_area), "button_press_event",
1515 G_CALLBACK (ghid_port_button_press_cb), NULL);
1517 button_release_handler =
1518 g_signal_connect (G_OBJECT (gport->drawing_area), "button_release_event",
1519 G_CALLBACK (ghid_port_button_release_cb), NULL);
1521 scroll_event_handler =
1522 g_signal_connect (G_OBJECT (gport->drawing_area), "scroll_event",
1523 G_CALLBACK (ghid_port_window_mouse_scroll_cb), NULL);
1525 key_press_handler =
1526 g_signal_connect (G_OBJECT (gport->drawing_area), "key_press_event",
1527 G_CALLBACK (ghid_port_key_press_cb), NULL);
1529 key_release_handler =
1530 g_signal_connect (G_OBJECT (gport->drawing_area), "key_release_event",
1531 G_CALLBACK (ghid_port_key_release_cb), NULL);
1534 void
1535 ghid_interface_input_signals_disconnect (void)
1537 if (button_press_handler)
1538 g_signal_handler_disconnect (gport->drawing_area, button_press_handler);
1540 if (button_release_handler)
1541 g_signal_handler_disconnect (gport->drawing_area, button_release_handler);
1543 if (scroll_event_handler)
1544 g_signal_handler_disconnect (gport->drawing_area, scroll_event_handler);
1546 if (key_press_handler)
1547 g_signal_handler_disconnect (gport->drawing_area, key_press_handler);
1549 if (key_release_handler)
1550 g_signal_handler_disconnect (gport->drawing_area, key_release_handler);
1552 button_press_handler = 0;
1553 button_release_handler = 0;
1554 scroll_event_handler = 0;
1555 key_press_handler = 0;
1556 key_release_handler = 0;
1560 /* We'll set the interface insensitive when a g_main_loop is running so the
1561 | Gtk menus and buttons don't respond and interfere with the special entry
1562 | the user needs to be doing.
1564 void
1565 ghid_interface_set_sensitive (gboolean sensitive)
1567 gtk_widget_set_sensitive (ghidgui->left_toolbar, sensitive);
1568 gtk_widget_set_sensitive (ghidgui->menu_hbox, sensitive);
1572 /* ----------------------------------------------------------------------
1573 * initializes icon pixmap and also cursor bit maps
1575 static void
1576 ghid_init_icons (GHidPort * port)
1578 GdkWindow *window = gtk_widget_get_window (gport->top_window);
1580 XC_clock_source = gdk_bitmap_create_from_data (window,
1581 (char *) rotateIcon_bits,
1582 rotateIcon_width,
1583 rotateIcon_height);
1584 XC_clock_mask =
1585 gdk_bitmap_create_from_data (window, (char *) rotateMask_bits,
1586 rotateMask_width, rotateMask_height);
1588 XC_hand_source = gdk_bitmap_create_from_data (window,
1589 (char *) handIcon_bits,
1590 handIcon_width,
1591 handIcon_height);
1592 XC_hand_mask =
1593 gdk_bitmap_create_from_data (window, (char *) handMask_bits,
1594 handMask_width, handMask_height);
1596 XC_lock_source = gdk_bitmap_create_from_data (window,
1597 (char *) lockIcon_bits,
1598 lockIcon_width,
1599 lockIcon_height);
1600 XC_lock_mask =
1601 gdk_bitmap_create_from_data (window, (char *) lockMask_bits,
1602 lockMask_width, lockMask_height);
1605 void
1606 ghid_create_pcb_widgets (void)
1608 GHidPort *port = &ghid_port;
1609 GError *err = NULL;
1611 if (bg_image_file)
1612 ghidgui->bg_pixbuf = gdk_pixbuf_new_from_file(bg_image_file, &err);
1613 if (err)
1615 g_error("%s", err->message);
1616 g_error_free(err);
1618 ghid_build_pcb_top_window ();
1619 ghid_install_accel_groups (GTK_WINDOW (port->top_window), ghidgui);
1620 ghid_update_toggle_flags ();
1622 ghid_init_icons (port);
1623 SetMode (ARROW_MODE);
1624 ghid_mode_buttons_update ();
1627 static gboolean
1628 ghid_listener_cb (GIOChannel *source,
1629 GIOCondition condition,
1630 gpointer data)
1632 GIOStatus status;
1633 gchar *str;
1634 gsize len;
1635 gsize term;
1636 GError *err = NULL;
1639 if (condition & G_IO_HUP)
1641 gui->log (_("Read end of pipe died!\n"));
1642 return FALSE;
1645 if (condition == G_IO_IN)
1647 status = g_io_channel_read_line (source, &str, &len, &term, &err);
1648 switch (status)
1650 case G_IO_STATUS_NORMAL:
1651 hid_parse_actions (str);
1652 g_free (str);
1653 break;
1655 case G_IO_STATUS_ERROR:
1656 gui->log (_("ERROR status from g_io_channel_read_line\n"));
1657 return FALSE;
1658 break;
1660 case G_IO_STATUS_EOF:
1661 gui->log (_("Input pipe returned EOF. The --listen option is \n"
1662 "probably not running anymore in this session.\n"));
1663 return FALSE;
1664 break;
1666 case G_IO_STATUS_AGAIN:
1667 gui->log (_("AGAIN status from g_io_channel_read_line\n"));
1668 return FALSE;
1669 break;
1671 default:
1672 fprintf (stderr, _("ERROR: unhandled case in ghid_listener_cb\n"));
1673 return FALSE;
1674 break;
1678 else
1679 fprintf (stderr, _("Unknown condition in ghid_listener_cb\n"));
1681 return TRUE;
1684 static void
1685 ghid_create_listener (void)
1687 GIOChannel *channel;
1688 int fd = fileno (stdin);
1690 channel = g_io_channel_unix_new (fd);
1691 g_io_add_watch (channel, G_IO_IN, ghid_listener_cb, NULL);
1695 /* ------------------------------------------------------------ */
1697 static int stdin_listen = 0;
1698 static char *pcbmenu_path = "gpcb-menu.res";
1700 HID_Attribute ghid_attribute_list[] = {
1702 /* %start-doc options "21 GTK+ GUI Options"
1703 @ftable @code
1704 @item --listen
1705 Listen for actions on stdin.
1706 @end ftable
1707 %end-doc
1709 {"listen", N_("Listen for actions on stdin"),
1710 HID_Boolean, 0, 0, {0, 0, 0}, 0, &stdin_listen},
1711 #define HA_listen 0
1713 /* %start-doc options "21 GTK+ GUI Options"
1714 @ftable @code
1715 @item --bg-image <string>
1716 File name of an image to put into the background of the GUI canvas. The image must
1717 be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
1718 automatically scaled to fit the canvas.
1719 @end ftable
1720 %end-doc
1722 {"bg-image", N_("Background Image"),
1723 HID_String, 0, 0, {0, 0, 0}, 0, &bg_image_file},
1724 #define HA_bg_image 1
1726 /* %start-doc options "21 GTK+ GUI Options"
1727 @ftable @code
1728 @item --pcb-menu <string>
1729 Location of the @file{gpcb-menu.res} file which defines the menu for the GTK+ GUI.
1730 @end ftable
1731 %end-doc
1733 {"pcb-menu", N_("Location of gpcb-menu.res file"),
1734 HID_String, 0, 0, {0, PCBLIBDIR "/gpcb-menu.res", 0}, 0, &pcbmenu_path}
1735 #define HA_pcbmenu 2
1738 REGISTER_ATTRIBUTES (ghid_attribute_list)
1740 HID_Attribute *
1741 ghid_get_export_options (int *n_ret)
1743 *n_ret = sizeof (ghid_attribute_list) / sizeof (HID_Attribute);
1744 return ghid_attribute_list;
1747 /* Create top level window for routines that will need top_window
1748 | before ghid_create_pcb_widgets() is called.
1750 void
1751 ghid_parse_arguments (int *argc, char ***argv)
1753 GtkWidget *window;
1754 gint i;
1755 GdkPixbuf *icon;
1757 /* on windows we need to figure out the installation directory */
1758 #ifdef WIN32
1759 char * tmps;
1760 char * libdir;
1761 tmps = g_win32_get_package_installation_directory(PACKAGE "-" VERSION, NULL);
1762 #define REST_OF_PATH G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "newlib"
1763 libdir = (char *) malloc(strlen(tmps) +
1764 strlen(REST_OF_PATH) +
1766 sprintf(libdir, "%s%s", tmps, REST_OF_PATH);
1767 free(tmps);
1769 Settings.LibraryTree = libdir;
1771 #undef REST_OF_PATH
1773 #endif
1775 #if defined (DEBUG)
1776 for (i = 0; i < *argc; i++)
1778 printf ("ghid_parse_arguments(): *argv[%d] = \"%s\"\n", i, (*argv)[i]);
1780 #endif
1782 /* Threads aren't used in PCB, but this call would go here.
1784 /* g_thread_init (NULL); */
1787 * Prevent gtk_init() and gtk_init_check() from automatically calling
1788 * setlocale (LC_ALL, "") which would undo LC_NUMERIC handling in main().
1790 gtk_disable_setlocale ();
1792 gtk_init (argc, argv);
1794 gport = &ghid_port;
1795 gport->view.coord_per_px = 300.0;
1796 pixel_slop = 300;
1798 ghid_init_renderer (argc, argv, gport);
1800 ghid_config_files_read (argc, argv);
1802 Settings.AutoPlace = 0;
1803 for (i = 0; i < *argc; i++)
1805 if (strcmp ((*argv)[i], "-auto-place") == 0)
1806 Settings.AutoPlace = 1;
1809 bindtextdomain (PACKAGE, LOCALEDIR);
1810 textdomain (PACKAGE);
1811 bind_textdomain_codeset (PACKAGE, "UTF-8");
1813 icon = gdk_pixbuf_new_from_xpm_data ((const gchar **) icon_bits);
1814 gtk_window_set_default_icon (icon);
1816 window = gport->top_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1817 gtk_window_set_title (GTK_WINDOW (window), "PCB");
1818 gtk_window_resize (GTK_WINDOW(window),
1819 ghidgui->top_window_width, ghidgui->top_window_height);
1821 if (Settings.AutoPlace)
1822 gtk_window_move (GTK_WINDOW (window), 10, 10);
1824 gtk_widget_show_all (gport->top_window);
1825 ghidgui->creating = TRUE;
1828 void
1829 ghid_do_export (HID_Attr_Val * options)
1831 ghid_create_pcb_widgets ();
1833 /* These are needed to make sure the @layerpick and @layerview menus
1834 * are properly initialized and synchronized with the current PCB.
1836 ghid_layer_buttons_update ();
1837 ghid_main_menu_install_route_style_selector
1838 (GHID_MAIN_MENU (ghidgui->menu_bar),
1839 GHID_ROUTE_STYLE_SELECTOR (ghidgui->route_style_selector));
1841 if (stdin_listen)
1842 ghid_create_listener ();
1844 ghidgui->snavi = setup_snavi (ndof_pan_cb,
1845 ndof_roll_cb,
1846 ndof_done_cb,
1847 ndof_button_cb,
1848 NULL);
1850 ghid_notify_gui_is_up ();
1852 gtk_main ();
1853 ghid_config_files_write ();
1857 /*! \brief callback for */
1858 static gboolean
1859 get_layer_visible_cb (int id)
1861 int visible;
1862 layer_process (NULL, NULL, &visible, id);
1863 return visible;
1866 gint
1867 LayersChanged (int argc, char **argv, Coord x, Coord y)
1869 if (!ghidgui || !ghidgui->menu_bar)
1870 return 0;
1872 ghid_config_groups_changed();
1873 ghid_layer_buttons_update ();
1874 ghid_layer_selector_show_layers
1875 (GHID_LAYER_SELECTOR (ghidgui->layer_selector), get_layer_visible_cb);
1877 /* FIXME - if a layer is moved it should retain its color. But layers
1878 | currently can't do that because color info is not saved in the
1879 | pcb file. So this makes a moved layer change its color to reflect
1880 | the way it will be when the pcb is reloaded.
1882 pcb_colors_from_settings (PCB);
1883 return 0;
1886 static const char toggleview_syntax[] =
1887 N_("ToggleView(1..MAXLAYER)\n"
1888 "ToggleView(layername)\n"
1889 "ToggleView(Silk|Rats|Pins|Vias|Mask|BackSide)");
1891 static const char toggleview_help[] =
1892 N_("Toggle the visibility of the specified layer or layer group.");
1894 /* %start-doc actions ToggleView
1896 If you pass an integer, that layer is specified by index (the first
1897 layer is @code{1}, etc). If you pass a layer name, that layer is
1898 specified by name. When a layer is specified, the visibility of the
1899 layer group containing that layer is toggled.
1901 If you pass a special layer name, the visibility of those components
1902 (silk, rats, etc) is toggled. Note that if you have a layer named
1903 the same as a special layer, the layer is chosen over the special layer.
1905 %end-doc */
1907 static int
1908 ToggleView (int argc, char **argv, Coord x, Coord y)
1910 int i, l;
1912 #ifdef DEBUG_MENUS
1913 puts ("Starting ToggleView().");
1914 #endif
1916 if (argc == 0)
1918 AFAIL (toggleview);
1920 if (isdigit ((int) argv[0][0]))
1922 l = atoi (argv[0]) - 1;
1924 else if (strcmp (argv[0], "Silk") == 0)
1925 l = LAYER_BUTTON_SILK;
1926 else if (strcmp (argv[0], "Rats") == 0)
1927 l = LAYER_BUTTON_RATS;
1928 else if (strcmp (argv[0], "Pins") == 0)
1929 l = LAYER_BUTTON_PINS;
1930 else if (strcmp (argv[0], "Vias") == 0)
1931 l = LAYER_BUTTON_VIAS;
1932 else if (strcmp (argv[0], "Mask") == 0)
1933 l = LAYER_BUTTON_MASK;
1934 else if (strcmp (argv[0], "BackSide") == 0)
1935 l = LAYER_BUTTON_FARSIDE;
1936 else
1938 l = -1;
1939 for (i = 0; i < max_copper_layer + 2; i++)
1940 if (strcmp (argv[0], PCB->Data->Layer[i].Name) == 0)
1942 l = i;
1943 break;
1945 if (l == -1)
1947 AFAIL (toggleview);
1952 /* Now that we've figured out which toggle button ought to control
1953 * this layer, simply hit the button and let the pre-existing code deal
1955 ghid_layer_selector_toggle_layer
1956 (GHID_LAYER_SELECTOR (ghidgui->layer_selector), l);
1957 return 0;
1960 static const char selectlayer_syntax[] =
1961 N_("SelectLayer(1..MAXLAYER|Silk|Rats|Mask)");
1963 static const char selectlayer_help[] =
1964 N_("Select which layer is the current layer.");
1966 /* %start-doc actions SelectLayer
1968 The specified layer becomes the currently active layer. It is made
1969 visible if it is not already visible
1971 %end-doc */
1973 static int
1974 SelectLayer (int argc, char **argv, Coord x, Coord y)
1976 int i;
1977 int newl = -1;
1978 if (argc == 0)
1979 AFAIL (selectlayer);
1981 for (i = 0; i < max_copper_layer; ++i)
1982 if (strcasecmp (argv[0], PCB->Data->Layer[i].Name) == 0)
1983 newl = i;
1985 if (strcasecmp (argv[0], "silk") == 0)
1986 newl = LAYER_BUTTON_SILK;
1987 else if (strcasecmp (argv[0], "rats") == 0)
1988 newl = LAYER_BUTTON_RATS;
1989 else if (strcasecmp (argv[0], "mask") == 0)
1990 newl = LAYER_BUTTON_MASK;
1991 else if (newl == -1)
1992 newl = atoi (argv[0]) - 1;
1994 #ifdef DEBUG_MENUS
1995 printf ("SelectLayer(): newl = %d\n", newl);
1996 #endif
1998 /* Now that we've figured out which radio button ought to select
1999 * this layer, simply hit the button and let the pre-existing code deal
2001 ghid_layer_selector_select_layer
2002 (GHID_LAYER_SELECTOR (ghidgui->layer_selector), newl);
2004 return 0;
2008 HID_Action gtk_topwindow_action_list[] = {
2009 {"LayersChanged", 0, LayersChanged,
2010 layerschanged_help, layerschanged_syntax},
2011 {"SelectLayer", 0, SelectLayer,
2012 selectlayer_help, selectlayer_syntax},
2013 {"ToggleView", 0, ToggleView,
2014 toggleview_help, toggleview_syntax}
2017 REGISTER_ACTIONS (gtk_topwindow_action_list)
2020 * This function is used to check if a specified hotkey in the menu
2021 * resource file is "special". In this case "special" means that gtk
2022 * assigns a particular meaning to it and the normal menu setup will
2023 * never see these key presses. We capture those and feed them back
2024 * into the menu callbacks. This function is called as new
2025 * accelerators are added when the menus are being built
2027 static void
2028 ghid_check_special_key (const char *accel, GtkAction *action,
2029 const Resource *node)
2031 size_t len;
2032 unsigned int mods;
2033 unsigned int ind;
2035 if (action == NULL || accel == NULL || *accel == '\0')
2036 return ;
2038 #ifdef DEBUG_MENUS
2039 printf ("%s(\"%s\", \"%s\")\n", __FUNCTION__, accel, name);
2040 #endif
2042 mods = 0;
2043 if (strstr (accel, "<alt>") )
2045 mods |= GHID_KEY_ALT;
2047 if (strstr (accel, "<ctrl>") )
2049 mods |= GHID_KEY_CONTROL;
2051 if (strstr (accel, "<shift>") )
2053 mods |= GHID_KEY_SHIFT;
2057 len = strlen (accel);
2059 #define CHECK_KEY(a) ((len >= strlen (a)) && (strcmp (accel + len - strlen (a), (a)) == 0))
2061 ind = 0;
2062 if ( CHECK_KEY ("Tab") )
2064 ind = mods | GHID_KEY_TAB;
2066 else if ( CHECK_KEY ("Up") )
2068 ind = mods | GHID_KEY_UP;
2070 else if ( CHECK_KEY ("Down") )
2072 ind = mods | GHID_KEY_DOWN;
2074 else if ( CHECK_KEY ("Left") )
2076 ind = mods | GHID_KEY_LEFT;
2078 else if ( CHECK_KEY ("Right") )
2080 ind = mods | GHID_KEY_RIGHT;
2083 if (ind > 0)
2085 if (ind >= N_HOTKEY_ACTIONS)
2087 fprintf (stderr, _("ERROR: overflow of the ghid_hotkey_actions array. Index = %d\n"
2088 "Please report this.\n"), ind);
2089 exit (1);
2092 ghid_hotkey_actions[ind].action = action;
2093 ghid_hotkey_actions[ind].node = node;
2094 #ifdef DEBUG_MENUS
2095 printf ("Adding \"special\" hotkey to ghid_hotkey_actions[%u] :"
2096 " %s (%s)\n", ind, accel, name);
2097 #endif
2101 /*! \brief Finds the gpcb-menu.res file */
2102 char *
2103 get_menu_filename (void)
2105 char *rv = NULL;
2106 char *home_pcbmenu = NULL;
2108 /* homedir is set by the core */
2109 if (homedir)
2111 Message (_("Note: home directory is \"%s\"\n"), homedir);
2112 home_pcbmenu = Concat (homedir, PCB_DIR_SEPARATOR_S, ".pcb",
2113 PCB_DIR_SEPARATOR_S, "gpcb-menu.res", NULL);
2115 else
2116 Message (_("Warning: could not determine home directory\n"));
2118 if (access ("gpcb-menu.res", R_OK) == 0)
2119 rv = strdup ("gpcb-menu.res");
2120 else if (home_pcbmenu != NULL && (access (home_pcbmenu, R_OK) == 0) )
2121 rv = home_pcbmenu;
2122 else if (access (pcbmenu_path, R_OK) == 0)
2123 rv = strdup (pcbmenu_path);
2125 return rv;
2128 static GtkWidget *
2129 ghid_load_menus (void)
2131 char *filename;
2132 const Resource *r = 0, *bir;
2133 const Resource *mr;
2134 GtkWidget *menu_bar = NULL;
2135 int i;
2137 for (i = 0; i < N_HOTKEY_ACTIONS; i++)
2139 ghid_hotkey_actions[i].action = NULL;
2140 ghid_hotkey_actions[i].node = NULL;
2143 bir = resource_parse (0, gpcb_menu_default);
2144 if (!bir)
2146 fprintf (stderr, _("Error: internal menu resource didn't parse\n"));
2147 exit(1);
2150 filename = get_menu_filename ();
2151 if (filename)
2153 Message (_("Loading menus from %s\n"), filename);
2154 r = resource_parse (filename, 0);
2157 if (!r)
2159 Message (_("Using default menus\n"));
2160 r = bir;
2162 free (filename);
2164 mr = resource_subres (r, "MainMenu");
2165 if (!mr)
2166 mr = resource_subres (bir, "MainMenu");
2168 if (mr)
2170 menu_bar = ghid_main_menu_new (G_CALLBACK (ghid_menu_cb),
2171 ghid_check_special_key);
2172 ghid_main_menu_add_resource (GHID_MAIN_MENU (menu_bar), mr);
2175 mr = resource_subres (r, "PopupMenus");
2176 if (!mr)
2177 mr = resource_subres (bir, "PopupMenus");
2179 if (mr)
2181 int i;
2182 for (i = 0; i < mr->c; i++)
2183 if (resource_type (mr->v[i]) == 101)
2184 /* This is a named resource which defines a popup menu */
2185 ghid_main_menu_add_popup_resource (GHID_MAIN_MENU (menu_bar),
2186 mr->v[i].name, mr->v[i].subres);
2189 #ifdef DEBUG_MENUS
2190 puts ("Finished loading menus.");
2191 #endif
2193 mr = resource_subres (r, "Mouse");
2194 if (!mr)
2195 mr = resource_subres (bir, "Mouse");
2196 if (mr)
2197 load_mouse_resource (mr);
2199 return menu_bar;
2202 /* ------------------------------------------------------------ */
2204 static const char adjuststyle_syntax[] =
2205 N_("AdjustStyle()\n");
2207 static const char adjuststyle_help[] =
2208 N_("Open the window which allows editing of the route styles.");
2210 /* %start-doc actions AdjustStyle
2212 Opens the window which allows editing of the route styles.
2214 %end-doc */
2216 static int
2217 AdjustStyle(int argc, char **argv, Coord x, Coord y)
2219 ghid_route_style_selector_edit_dialog
2220 (GHID_ROUTE_STYLE_SELECTOR (ghidgui->route_style_selector));
2222 return 0;
2225 /* ------------------------------------------------------------ */
2227 static const char editlayergroups_syntax[] =
2228 N_("EditLayerGroups()\n");
2230 static const char editlayergroups_help[] =
2231 N_("Open the preferences window which allows editing of the layer groups.");
2233 /* %start-doc actions EditLayerGroups
2235 Opens the preferences window which is where the layer groups
2236 are edited. This action is primarily provides to provide menu
2237 resource compatibility with the lesstif HID.
2239 %end-doc */
2241 static int
2242 EditLayerGroups(int argc, char **argv, Coord x, Coord y)
2245 if (argc != 0)
2246 AFAIL (editlayergroups);
2248 hid_actionl ("DoWindows", "Preferences", NULL);
2250 return 0;
2253 /* ------------------------------------------------------------ */
2255 HID_Action ghid_menu_action_list[] = {
2256 {"AdjustStyle", 0, AdjustStyle, adjuststyle_help, adjuststyle_syntax},
2257 {"EditLayerGroups", 0, EditLayerGroups, editlayergroups_help, editlayergroups_syntax}
2260 REGISTER_ACTIONS (ghid_menu_action_list)