Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / gschem / src / x_menus.c
blob7dc3d04962f98a77767ea6783d78e8253d1a3e3d
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20 #include "config.h"
22 #ifdef HAVE_STRING_H
23 #include <string.h>
24 #endif
27 #include "gschem.h"
29 #include <glib/gstdio.h>
31 #ifdef HAVE_LIBDMALLOC
32 #include <dmalloc.h>
33 #endif
35 static GtkItemFactoryEntry popup_items[] = {
36 { N_("/Add Net"), NULL, i_callback_add_net, 0, NULL},
37 { N_("/Add Attribute..."), NULL, i_callback_add_attribute, 0, NULL},
38 { N_("/Add Component..."), NULL, i_callback_add_component, 0, NULL},
39 { N_("/Add Bus"), NULL, i_callback_add_bus, 0, NULL},
40 { N_("/Add Text"), NULL, i_callback_add_text, 0, NULL},
41 { "/sep1", NULL, NULL, 0, "<Separator>"},
42 { N_("/Zoom In"), NULL, i_callback_view_zoom_in, 0, NULL},
43 { N_("/Zoom Out"), NULL, i_callback_view_zoom_out, 0, NULL},
44 { N_("/Zoom Box"), NULL, i_callback_view_zoom_box, 0, NULL},
45 { N_("/Zoom Extents"), NULL, i_callback_view_zoom_extents, 0, NULL},
46 { "/sep1", NULL, NULL, 0, "<Separator>"},
47 { N_("/Select"), NULL, i_callback_edit_select, 0, NULL},
48 { N_("/Edit..."), NULL, i_callback_edit_edit, 0, NULL},
49 { N_("/Edit pin type..."), NULL, i_callback_edit_pin_type, 0, NULL},
50 { N_("/Copy"), NULL, i_callback_edit_copy, 0, NULL},
51 { N_("/Move"), NULL, i_callback_edit_move, 0, NULL},
52 { N_("/Delete"), NULL, i_callback_edit_delete, 0, NULL},
53 /* Menu items for hierarchy added by SDB 1.9.2005. */
54 {"/sep1", NULL, NULL, 0, "<Separator>"},
55 {N_("/Down Schematic"), NULL, i_callback_hierarchy_down_schematic, 0, NULL},
56 {N_("/Down Symbol"), NULL, i_callback_hierarchy_down_symbol, 0, NULL},
57 {N_("/Up"), NULL, i_callback_hierarchy_up, 0, NULL},
58 };
60 int npopup_items = sizeof(popup_items) / sizeof(popup_items[0]);
62 /*! \todo Finish function documentation!!!
63 * \brief
64 * \par Function Description
67 static void g_menu_execute(char *func)
69 gchar *guile_string;
71 guile_string = g_strdup_printf("(%s)", func);
72 #if DEBUG
73 printf("%s\n", guile_string);
74 #endif
75 g_scm_c_eval_string_protected (guile_string);
76 g_free(guile_string);
79 /*! \todo Finish function documentation!!!
80 * \brief
81 * \par Function Description
84 void get_main_menu(GtkWidget ** menubar)
86 char *buf;
87 GschemAction *action;
88 GtkWidget *menu_item;
89 GtkWidget *root_menu;
90 GtkWidget *menu_bar;
91 GtkWidget *menu;
92 int scm_items_len;
93 SCM scm_items;
94 SCM scm_item;
95 SCM scm_item_name;
96 SCM scm_item_func;
97 SCM scm_item_hotkey_func;
98 SCM scm_item_stock;
99 SCM scm_index;
100 SCM scm_keys;
101 char *menu_name;
102 char **raw_menu_name = g_malloc (sizeof(char *));
103 char *menu_item_name;
104 char *raw_menu_item_name;
105 char *menu_item_func;
106 char *menu_item_hotkey_func;
107 char *menu_item_stock;
108 char *menu_item_keys;
109 int i, j;
111 menu_bar = gtk_menu_bar_new ();
112 for (i = 0 ; i < s_menu_return_num(); i++) {
114 scm_items = s_menu_return_entry(i, raw_menu_name);
115 if (*raw_menu_name == NULL) {
116 fprintf(stderr, "Oops.. got a NULL menu name in get_main_menu()\n");
117 exit(-1);
120 menu = gtk_menu_new();
122 menu_item = gtk_tearoff_menu_item_new ();
123 gtk_menu_append(GTK_MENU(menu), menu_item);
124 gtk_widget_show(menu_item);
126 scm_items_len = (int) scm_ilength (scm_items);
127 for (j = 0 ; j < scm_items_len; j++) {
129 scm_index = scm_from_int (j);
130 scm_item = scm_list_ref (scm_items, scm_index);
131 scm_item_name = SCM_CAR (scm_item);
132 scm_item_func = SCM_CADR (scm_item);
133 scm_item_hotkey_func = SCM_CADDR (scm_item);
134 scm_item_stock = scm_is_pair (SCM_CDDDR (scm_item)) ?
135 SCM_CADDDR (scm_item) : SCM_BOOL_F;
136 SCM_ASSERT(scm_is_string(scm_item_name), scm_item_name, SCM_ARGn, "get_main_menu item_name");
137 SCM_ASSERT(SCM_SYMBOLP (scm_item_func) ||
138 scm_is_false (scm_item_func),
139 scm_item_func, SCM_ARGn, "get_main_menu item_func");
140 SCM_ASSERT (SCM_SYMBOLP (scm_item_hotkey_func) ||
141 scm_is_false (scm_item_hotkey_func),
142 scm_item_hotkey_func, SCM_ARGn, "get_main_menu hotkey_func");
143 SCM_ASSERT (SCM_STRINGP (scm_item_stock) ||
144 scm_is_false (scm_item_stock),
145 scm_item_stock, SCM_ARGn, "get_main_menu stock");
147 raw_menu_item_name = SCM_STRING_CHARS (scm_item_name);
149 if (scm_is_false (scm_item_func))
150 menu_item_func = "no-action";
151 else
152 menu_item_func = SCM_SYMBOL_CHARS (scm_item_func);
154 if (scm_is_false (scm_item_hotkey_func))
155 menu_item_hotkey_func = NULL;
156 else
157 menu_item_hotkey_func = SCM_SYMBOL_CHARS (scm_item_hotkey_func);
159 if (scm_is_false (scm_item_stock))
160 menu_item_stock = NULL;
161 else
162 menu_item_stock = SCM_SYMBOL_CHARS (scm_item_stock);
164 menu_item_name = (char *) gettext(raw_menu_item_name);
166 if (strcmp(menu_item_name, "SEPARATOR") == 0) {
167 menu_item = gtk_menu_item_new();
168 gtk_menu_append(GTK_MENU(menu), menu_item);
169 } else {
171 if (menu_item_hotkey_func != NULL) {
173 buf = g_strdup_printf ("(find-key '%s)", menu_item_hotkey_func);
174 scm_keys = g_scm_c_eval_string_protected (buf);
175 g_free (buf);
177 if (scm_keys == SCM_BOOL_F) {
178 menu_item_keys = "";
179 } else {
180 menu_item_keys = SCM_STRING_CHARS (scm_keys);
183 } else {
184 menu_item_keys = "";
187 action = gschem_action_new (menu_item_func, /* Action name */
188 menu_item_name, /* Text */
189 menu_item_name, /* Tooltip */
190 menu_item_stock, /* Icon stock ID */
191 menu_item_keys); /* Accelerator string */
192 menu_item = gtk_action_create_menu_item (GTK_ACTION (action));
193 gtk_menu_append (GTK_MENU (menu), menu_item);
195 gtk_signal_connect_object (GTK_OBJECT(menu_item), "activate",
196 GTK_SIGNAL_FUNC(g_menu_execute),
197 (gpointer) g_strdup (menu_item_func));
198 /* The g_strdup is a memory leak, but this is okay. I think. */
201 gtk_widget_show (menu_item);
203 /* add a handle to the menu_bar object to get access to widget objects */
204 /* This string should NOT be internationalized */
205 buf = g_strdup_printf("%s/%s", *raw_menu_name, raw_menu_item_name);
206 gtk_object_set_data(GTK_OBJECT(menu_bar), buf, menu_item);
207 g_free(buf);
211 menu_name = (char *) gettext(*raw_menu_name);
212 root_menu = gtk_menu_item_new_with_mnemonic (menu_name);
213 /* do not free *raw_menu_name */
215 /* no longer right justify the help menu since that has gone out of style */
217 gtk_widget_show (root_menu);
218 gtk_menu_item_set_submenu (GTK_MENU_ITEM (root_menu), menu);
219 gtk_menu_bar_append (GTK_MENU_BAR (menu_bar), root_menu);
222 g_free(raw_menu_name);
223 *menubar = menu_bar;
226 /*! \todo Finish function documentation!!!
227 * \brief
228 * \par Function Description
231 static gchar* gettext_fn(const gchar *path,
232 gpointer func_data ATTRIBUTE_UNUSED)
234 return gettext(path);
237 GtkWidget *get_main_popup(GSCHEM_TOPLEVEL *w_current)
239 static GtkItemFactory *item_factory;
240 GtkAccelGroup *accel_group;
241 GtkWidget *menu;
243 accel_group = gtk_accel_group_new();
245 /* This function initializes the item factory.
246 Param 1: The type of menu - can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU, or GTK_TYPE_OPTION_MENU.
247 Param 2: The path of the menu.
248 Param 3: A pointer to a gtk_accel_group. The item factory sets up
249 the accelerator table while generating menus.
251 item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<popup>",
252 accel_group);
253 gtk_item_factory_set_translate_func (item_factory,
254 gettext_fn,
255 NULL, NULL);
256 /* This function creates the pop-up menu itself & attaches it to the
257 GtkItemFactory. Pass the item factory,
258 the number of items in the array, the array itself, and any
259 callback data for the the menu items. Note that npopup_items is
260 a static var declared in this file above; popup_items is also a
261 static var declared above.
263 gtk_item_factory_create_items(item_factory, npopup_items, popup_items, w_current);
265 /* Finally, return the actual menu created by the item factory. */
266 menu = (GtkWidget *) gtk_item_factory_get_widget(item_factory, "<popup>");
267 return (menu);
271 /*! \todo Finish function documentation!!!
272 * \brief
273 * \par Function Description
275 * \note
276 * need to look at this... here and the setup
278 gint do_popup (GSCHEM_TOPLEVEL *w_current, GdkEventButton *event)
280 GtkWidget *menu; /* =NULL; */ /* was static */
282 menu = NULL; /* Why do I need to do this? */
283 if (!menu)
284 menu = (GtkWidget *) w_current->popup_menu;
286 if (menu == NULL) {
287 printf("null menu\n");
290 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
291 event->button, event->time);
293 return FALSE;
296 /*! \todo Finish function documentation!!!
297 * \brief
298 * \par Function Description
301 void x_menus_sensitivity (GSCHEM_TOPLEVEL *w_current, const char *buf, int flag)
303 GtkWidget* item=NULL;
305 if (!buf) {
306 return;
309 if (!w_current->menubar) {
310 return;
313 item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w_current->menubar), buf);
315 if (item) {
316 gtk_widget_set_sensitive(GTK_WIDGET(item), flag);
317 /* free(item); */ /* Why doesn't this need to be freed? */
318 } else {
319 s_log_message(_("Tried to set the sensitivity on non-existent menu item '%s'\n"), buf);
324 /*! \todo Finish function documentation!!!
325 * \brief
326 * \par Function Description
327 * This function sets the sensitivity of the items in the right button
328 * popup.
330 * \note
331 * 1.9.2005 -- SDB.
333 void x_menus_popup_sensitivity (GSCHEM_TOPLEVEL *w_current, const char *buf, int flag)
335 GtkWidget *menu_item;
336 GtkItemFactory *menu_item_factory;
338 if (!buf) {
339 return;
342 if (!w_current->popup_menu) {
343 s_log_message(_("Popup_menu_item_factory doesn't exist!\n"));
344 return;
348 * first get entire item factory from popup, then get the individual
349 * menu item indexed by buf.
351 menu_item_factory = (GtkItemFactory *)gtk_item_factory_from_widget(w_current->popup_menu);
352 menu_item = (GtkWidget *) gtk_item_factory_get_widget(menu_item_factory, buf);
353 if (menu_item) {
354 gtk_widget_set_sensitive(GTK_WIDGET(menu_item), flag);
355 } else {
356 s_log_message(_("Tried to set the sensitivity on a non-existent popup menu_item\n"));
360 /* The list of recently loaded files. */
361 static GList *recent_files = NULL;
363 #define RECENT_FILES_STORE "gschem-recent-files"
364 #define MAX_RECENT_FILES 10
366 struct recent_file_menu_data {
367 GSCHEM_TOPLEVEL *w_current;
368 gchar *filename;
371 /*! \brief Make all toplevels reflect changes to the
372 * recent files list.
374 static void update_recent_files_menus()
376 GSCHEM_TOPLEVEL *w_current;
377 GtkWidget *submenu, *recent_menu_item;
378 GList *iter;
380 for (iter = global_window_list;
381 iter != NULL;
382 iter = g_list_next (iter)) {
383 w_current = (GSCHEM_TOPLEVEL *)iter->data;
385 if (w_current->menubar == NULL)
386 continue;
388 recent_menu_item =
389 (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w_current->menubar),
390 "_File/Open Recen_t");
391 if(recent_menu_item == NULL)
392 return;
394 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(recent_menu_item));
395 gtk_widget_destroy(submenu);
396 x_menu_attach_recent_files_submenu(w_current);
400 /*! \brief Remove all entries from the recent files
401 * list and update all toplevels.
403 static void clear_recent_file_list(gpointer data)
405 GList *p;
407 p = recent_files;
408 while(p) {
409 g_free(p->data);
410 p = g_list_next(p);
412 g_list_free(recent_files);
413 recent_files = NULL;
415 update_recent_files_menus();
418 static void
419 recent_file_free_menu_data (gpointer data, GClosure *closure) {
420 g_free (data);
423 static void recent_file_clicked(GtkMenuItem *menuitem, gpointer user_data)
425 FILE *fp;
426 PAGE *page;
427 struct recent_file_menu_data *data =
428 (struct recent_file_menu_data *) user_data;
429 GSCHEM_TOPLEVEL *w_current = data->w_current;
430 gchar *filename = data->filename;
432 /* Check if the file exists */
433 fp = fopen((char *) filename, "r");
434 if(fp == NULL) {
435 /* Remove this entry from all menus */
436 s_log_message(_("Couldn't open file %s\n"), (char *) filename);
437 recent_files = g_list_remove(recent_files, filename);
438 update_recent_files_menus();
439 return;
441 fclose(fp);
443 page = x_window_open_page(w_current, (char *)filename);
444 x_window_set_current_page(w_current, page);
447 /*! \brief Attach a submenu with filenames to the 'Open Recent'
448 * menu item.
450 * Called from x_window_setup().
452 void x_menu_attach_recent_files_submenu(GSCHEM_TOPLEVEL *w_current)
454 gulong id;
455 GtkWidget *tmp;
456 GtkWidget *recent_menu_item, *recent_submenu;
458 recent_menu_item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(
459 w_current->menubar), "_File/Open Recen_t");
460 if(recent_menu_item == NULL)
461 return;
463 /* disconnect all unblocked signals */
464 while(1) {
465 id = g_signal_handler_find(recent_menu_item, G_SIGNAL_MATCH_UNBLOCKED,
466 0, 0, NULL, NULL, NULL);
467 if(id == 0)
468 break;
469 gtk_signal_disconnect(recent_menu_item, id);
472 recent_submenu = gtk_menu_new();
473 GList *p = recent_files;
474 while(p) {
475 struct recent_file_menu_data *menu_data = g_new0 (struct recent_file_menu_data, 1);
476 menu_data->filename = p->data;
477 menu_data->w_current = w_current;
478 tmp = gtk_menu_item_new_with_label((gchar *)p->data);
479 g_signal_connect_data (GTK_OBJECT(tmp), "activate",
480 (GCallback) recent_file_clicked,
481 menu_data,
482 (GClosureNotify) recent_file_free_menu_data,
484 gtk_menu_append(GTK_MENU(recent_submenu), tmp);
485 p = g_list_next(p);
488 if(recent_files != NULL) {
489 /* Append the 'Clear' menu item to the submenu */
490 GtkWidget *alignment = gtk_alignment_new(0.5, 0, 0, 0);
492 tmp = gtk_menu_item_new();
493 gtk_container_add(GTK_CONTAINER(alignment), gtk_label_new(_("Clear")));
494 gtk_container_add(GTK_CONTAINER(tmp), alignment);
496 gtk_signal_connect_object(GTK_OBJECT(tmp), "activate",
497 GTK_SIGNAL_FUNC (clear_recent_file_list), NULL);
499 gtk_menu_append(GTK_MENU(recent_submenu), gtk_separator_menu_item_new());
500 gtk_menu_append(GTK_MENU(recent_submenu), tmp);
503 gtk_widget_show_all(recent_submenu);
504 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_menu_item), recent_submenu);
507 /*! \brief Add a filename to the list of recent files.
509 * If filename is already in the list, moves it to the head of the
510 * list.
512 void recent_files_add(const char *filename)
514 gchar *basename;
515 gchar *save_fn;
516 GError *err = NULL;
517 GList *p = recent_files;
519 basename = g_path_get_basename(filename);
520 if(strstr(basename, "untitled_") == basename) {
521 g_free(basename);
522 return;
525 g_free(basename);
527 /* Normalize the filename. */
528 save_fn = f_normalize_filename (filename, &err);
529 if (err != NULL) {
530 save_fn = g_strdup (filename);
531 g_error_free (err);
534 /* Check if the file is already in the list. */
535 while (p != NULL) {
536 if (strcmp (save_fn, (gchar *) p->data) == 0) {
537 break;
539 p = g_list_next (p);
542 if (p != NULL) {
543 /* Since we found the filename already in the list, move it to
544 * the head of the list. */
545 g_free (save_fn);
546 save_fn = (gchar *) p->data;
547 recent_files = g_list_delete_link (recent_files, p);
548 recent_files = g_list_prepend (recent_files, save_fn);
549 } else {
550 /* Otherwise, just add the new filename to the front of the
551 * list. */
552 recent_files = g_list_prepend (recent_files, save_fn);
555 update_recent_files_menus();
558 /*! \brief Make RECENT_FILES_STORE contain an empty file list.
560 static void recent_files_create_empty()
562 gchar *c;
563 const gchar * const tmp[] = { NULL };
564 GKeyFile *kf = g_key_file_new();
565 gchar *file = g_build_filename(s_path_user_config (), RECENT_FILES_STORE, NULL);
567 g_key_file_set_string_list(kf, "Recent files", "Files", tmp, 0);
568 c = g_key_file_to_data(kf, NULL, NULL);
569 g_key_file_free(kf);
571 g_file_set_contents(file, c, -1, NULL);
572 g_free(c);
573 g_free(file);
576 /*! \brief Save the list of recent files to RECENT_FILES_STORE.
578 * \param [in] user_data unused
580 void recent_files_save(gpointer user_data)
582 gchar *files[MAX_RECENT_FILES];
583 int num = 0;
584 gchar *c;
585 gchar *file = g_build_filename(s_path_user_config (), RECENT_FILES_STORE, NULL);
587 GList *p = recent_files;
588 if(p == NULL) {
589 recent_files_create_empty();
590 return;
593 while((p != NULL) && (num < MAX_RECENT_FILES)) {
594 files[num++] = (gchar *)p->data;
595 p = g_list_next(p);
598 GKeyFile *kf = g_key_file_new();
600 g_key_file_set_string_list(kf, "Recent files", "Files",
601 (const gchar **)files, num);
602 c = g_key_file_to_data(kf, NULL, NULL);
603 g_file_set_contents(file, c, -1, NULL);
605 g_free(c);
606 g_free(file);
607 g_key_file_free(kf);
610 /*! \brief Load the recent file list using data from
611 * RECENT_FILES_STORE.
613 * Must be called before any other recent-files-related
614 * functions.
616 void recent_files_load()
618 GKeyFile *kf = g_key_file_new();
619 gchar *file = g_build_filename(s_path_user_config (), RECENT_FILES_STORE, NULL);
621 if(!g_file_test(file, G_FILE_TEST_EXISTS)) {
622 g_mkdir(s_path_user_config (), S_IRWXU | S_IRWXG);
624 recent_files_create_empty();
627 if(!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, NULL)) {
628 /* error opening key file, create an empty one and try again */
629 recent_files_create_empty();
630 if(!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, NULL))
631 return;
634 gsize len;
635 gchar **list = g_key_file_get_string_list(kf, "Recent files",
636 "Files", &len, NULL);
638 if(list == NULL) {
639 /* error reading key file, don't bother to correct;
640 * just overwrite it with an empty one */
641 recent_files_create_empty();
642 return;
645 while(len > 0) {
646 len--;
647 recent_files = g_list_prepend(recent_files, list[len]);
650 g_free(list);
651 g_free(file);
652 g_key_file_free(kf);