Take page as input and take advantage of s_visit_page.
[geda-gaf/berndj.git] / gschem / src / x_menus.c
blobb93ec4adeabb388ebc87f848ed985364acca3379
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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_("/Copy"), NULL, i_callback_edit_copy, 0, NULL},
50 { N_("/Move"), NULL, i_callback_edit_move, 0, NULL},
51 { N_("/Delete"), NULL, i_callback_edit_delete, 0, NULL},
52 /* Menu items for hierarchy added by SDB 1.9.2005. */
53 {"/sep1", NULL, NULL, 0, "<Separator>"},
54 {N_("/Down Schematic"), NULL, i_callback_hierarchy_down_schematic, 0, NULL},
55 {N_("/Down Symbol"), NULL, i_callback_hierarchy_down_symbol, 0, NULL},
56 {N_("/Up"), NULL, i_callback_hierarchy_up, 0, NULL},
57 };
59 int npopup_items = sizeof(popup_items) / sizeof(popup_items[0]);
61 /*! \todo Finish function documentation!!!
62 * \brief
63 * \par Function Description
66 static void g_menu_execute(char *func)
68 g_scm_apply_protected(g_scm_safe_ref_lookup(func), SCM_EOL, NULL, NULL);
71 /*! \todo Finish function documentation!!!
72 * \brief
73 * \par Function Description
76 void get_main_menu(GtkWidget ** menubar)
78 char *buf;
79 GtkWidget *menu_item;
80 GtkWidget *root_menu;
81 GtkWidget *menu_bar;
82 GtkWidget *menu;
83 char *menu_name;
84 char **raw_menu_name = g_malloc (sizeof(char *));
85 char *menu_item_name;
86 char *raw_menu_item_name;
87 char *menu_item_keys;
88 char *spaces;
89 int i;
90 int name_len, key_len, pad;
91 int sum, diff, max_size, space_size;
92 PangoLayout *layout;
93 int name_width, keys_width;
95 menu_bar = gtk_menu_bar_new ();
96 for (i = 0 ; i < s_menu_return_num(); i++) {
97 SCM scm_items;
98 SCM rest;
100 scm_items = s_menu_return_entry(i, raw_menu_name);
101 if (*raw_menu_name == NULL) {
102 fprintf(stderr, "Oops.. got a NULL menu name in get_main_menu()\n");
103 exit(-1);
106 menu = gtk_menu_new();
108 menu_item = gtk_tearoff_menu_item_new ();
109 gtk_menu_append(GTK_MENU(menu), menu_item);
110 gtk_widget_show(menu_item);
112 layout = gtk_widget_create_pango_layout(menu, " ");
113 pango_layout_get_pixel_size(layout, &space_size, NULL);
114 g_object_unref(layout);
116 layout = gtk_widget_create_pango_layout(menu,
117 "123456789012345678901234567890");
118 pango_layout_get_pixel_size(layout, &max_size, NULL);
119 g_object_unref(layout);
121 for (rest = scm_items; !scm_is_null(rest); rest = SCM_CDR(rest)) {
122 SCM scm_item = SCM_CAR(rest);
123 SCM scm_item_name;
124 SCM scm_item_func;
125 SCM scm_item_hotkey_func;
126 SCM scm_keys;
128 scm_item_name = SCM_CAR (scm_item);
129 scm_item_func = SCM_CADR (scm_item);
130 scm_item_hotkey_func = SCM_CADDR (scm_item);
131 SCM_ASSERT(scm_is_string(scm_item_name), scm_item_name, SCM_ARGn, "get_main_menu item_name");
132 SCM_ASSERT(SCM_SYMBOLP(scm_item_func), scm_item_func, SCM_ARGn, "get_main_menu item_func");
133 SCM_ASSERT(SCM_SYMBOLP(scm_item_hotkey_func), scm_item_hotkey_func, SCM_ARGn, "get_main_menu hotkey_func");
135 scm_dynwind_begin(0);
136 raw_menu_item_name = scm_to_locale_string(scm_item_name);
137 scm_dynwind_free(raw_menu_item_name);
139 menu_item_name = (char *) gettext(raw_menu_item_name);
141 if (strcmp(menu_item_name, "SEPARATOR") == 0) {
142 menu_item = gtk_menu_item_new();
143 gtk_menu_append(GTK_MENU(menu), menu_item);
144 } else {
145 scm_keys = g_scm_apply_protected(g_scm_safe_ref_lookup("find-key"),
146 scm_list_1(scm_item_hotkey_func),
147 NULL, NULL);
148 if (scm_keys == SCM_BOOL_F) {
149 menu_item_keys = g_strdup (" ");
150 } else {
151 menu_item_keys = g_strdup_scm_string(scm_keys);
154 layout = gtk_widget_create_pango_layout(menu, menu_item_name);
155 pango_layout_get_pixel_size(layout, &name_width, NULL);
156 g_object_unref(layout);
158 layout = gtk_widget_create_pango_layout(menu, menu_item_keys);
159 pango_layout_get_pixel_size(layout, &keys_width, NULL);
160 g_object_unref(layout);
162 sum = name_width + keys_width;
164 diff = max_size - sum;
165 pad = diff/space_size;
166 if (pad < 0) {
167 pad = 1;
170 spaces = g_strnfill (pad, ' ');
171 buf = g_strdup_printf("%s%s%s", menu_item_name, spaces, menu_item_keys);
173 #if DEBUG
174 name_len = strlen(menu_item_name);
175 key_len = strlen(menu_item_keys);
177 printf("%s :\n %d %d %d = %d\n", buf, name_len, pad, key_len,
178 name_len + pad + key_len);
179 #endif
181 menu_item = gtk_menu_item_new_with_label(buf);
182 g_free(buf);
183 gtk_menu_append(GTK_MENU(menu), menu_item);
184 /* FIXME: Store the SCM so as to allow function literals. */
185 gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
186 GTK_SIGNAL_FUNC(g_menu_execute),
187 g_strdup_scm_string(scm_symbol_to_string(scm_item_func)));
188 /* The g_strdup is a memory leak, but this is okay. I think. */
189 g_free(spaces);
190 g_free(menu_item_keys);
193 gtk_widget_show(menu_item);
195 /* add a handle to the menu_bar object to get access to widget objects */
196 /* This string should NOT be internationalized */
197 buf = g_strdup_printf("%s/%s", *raw_menu_name, raw_menu_item_name);
198 gtk_object_set_data(GTK_OBJECT(menu_bar), buf, menu_item);
199 g_free(buf);
201 scm_dynwind_end();
204 menu_name = (char *) gettext(*raw_menu_name);
205 root_menu = gtk_menu_item_new_with_label (menu_name);
206 /* do not free *raw_menu_name */
208 /* no longer right justify the help menu since that has gone out of style */
210 gtk_widget_show (root_menu);
211 gtk_menu_item_set_submenu (GTK_MENU_ITEM (root_menu), menu);
212 gtk_menu_bar_append (GTK_MENU_BAR (menu_bar), root_menu);
215 g_free(raw_menu_name);
216 *menubar = menu_bar;
219 /*! \todo Finish function documentation!!!
220 * \brief
221 * \par Function Description
224 static gchar* gettext_fn(const gchar *path,
225 gpointer func_data ATTRIBUTE_UNUSED)
227 return gettext(path);
230 GtkWidget *get_main_popup(GSCHEM_TOPLEVEL *w_current)
232 static GtkItemFactory *item_factory;
233 GtkAccelGroup *accel_group;
234 GtkWidget *menu;
236 accel_group = gtk_accel_group_new();
238 /* This function initializes the item factory.
239 Param 1: The type of menu - can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU, or GTK_TYPE_OPTION_MENU.
240 Param 2: The path of the menu.
241 Param 3: A pointer to a gtk_accel_group. The item factory sets up
242 the accelerator table while generating menus.
244 item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<popup>",
245 accel_group);
246 gtk_item_factory_set_translate_func (item_factory,
247 gettext_fn,
248 NULL, NULL);
249 /* This function creates the pop-up menu itself & attaches it to the
250 GtkItemFactory. Pass the item factory,
251 the number of items in the array, the array itself, and any
252 callback data for the the menu items. Note that npopup_items is
253 a static var declared in this file above; popup_items is also a
254 static var declared above.
256 gtk_item_factory_create_items(item_factory, npopup_items, popup_items, w_current);
258 /* Finally, return the actual menu created by the item factory. */
259 menu = (GtkWidget *) gtk_item_factory_get_widget(item_factory, "<popup>");
260 return (menu);
264 /*! \todo Finish function documentation!!!
265 * \brief
266 * \par Function Description
268 * \note
269 * need to look at this... here and the setup
271 gint do_popup (GSCHEM_TOPLEVEL *w_current, GdkEventButton *event)
273 GtkWidget *menu; /* =NULL; */ /* was static */
275 menu = NULL; /* Why do I need to do this? */
276 if (!menu)
277 menu = (GtkWidget *) w_current->popup_menu;
279 if (menu == NULL) {
280 printf("null menu\n");
283 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
284 event->button, event->time);
286 return FALSE;
289 /*! \todo Finish function documentation!!!
290 * \brief
291 * \par Function Description
294 void x_menus_sensitivity (GSCHEM_TOPLEVEL *w_current, const char *buf, int flag)
296 GtkWidget* item=NULL;
298 if (!buf) {
299 return;
302 if (!w_current->menubar) {
303 return;
306 item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w_current->menubar), buf);
308 if (item) {
309 gtk_widget_set_sensitive(GTK_WIDGET(item), flag);
310 /* free(item); */ /* Why doesn't this need to be freed? */
311 } else {
312 s_log_message(_("Tried to set the sensitivity on non-existent menu item '%s'\n"), buf);
317 /*! \todo Finish function documentation!!!
318 * \brief
319 * \par Function Description
320 * This function sets the sensitivity of the items in the right button
321 * popup.
323 * \note
324 * 1.9.2005 -- SDB.
326 void x_menus_popup_sensitivity (GSCHEM_TOPLEVEL *w_current, const char *buf, int flag)
328 GtkWidget *menu_item;
329 GtkItemFactory *menu_item_factory;
331 if (!buf) {
332 return;
335 if (!w_current->popup_menu) {
336 s_log_message(_("Popup_menu_item_factory doesn't exist!\n"));
337 return;
341 * first get entire item factory from popup, then get the individual
342 * menu item indexed by buf.
344 menu_item_factory = (GtkItemFactory *)gtk_item_factory_from_widget(w_current->popup_menu);
345 menu_item = (GtkWidget *) gtk_item_factory_get_widget(menu_item_factory, buf);
346 if (menu_item) {
347 gtk_widget_set_sensitive(GTK_WIDGET(menu_item), flag);
348 } else {
349 s_log_message(_("Tried to set the sensitivity on a non-existent popup menu_item\n"));
353 #if !GLIB_CHECK_VERSION(2,8,0)
354 /* g_file_set_contents() exists only in glib >= 2.8 */
355 gboolean g_file_set_contents(const gchar *filename, const gchar *contents,
356 gssize length, GError **error)
358 FILE *fp;
359 gboolean ret = FALSE;
361 fp = fopen(filename, "wb");
362 if(fp == NULL)
363 return FALSE;
365 if(length == -1) {
366 /* It's a null-terminated string. */
367 if(fputs(contents, fp) == EOF)
368 goto out;
369 } else {
370 if(fwrite(contents, length, 1, fp) != 1)
371 goto out;
374 ret = TRUE;
375 out:
376 fclose(fp);
377 return ret;
379 #endif /* !GLIB_CHECK_VERSION(2,8,0) */
382 /* The list of recently loaded files. */
383 static GList *recent_files = NULL;
385 #define RECENT_FILES_STORE ".gEDA/gschem-recent-files"
387 /*! \brief Make all toplevels reflect changes to the
388 * recent files list.
390 static void update_recent_files_menus()
392 GSCHEM_TOPLEVEL *w_current;
393 GtkWidget *submenu, *recent_menu_item;
394 GList *iter;
396 for (iter = global_window_list;
397 iter != NULL;
398 iter = g_list_next (iter)) {
399 w_current = (GSCHEM_TOPLEVEL *)iter->data;
401 if (w_current->menubar == NULL)
402 continue;
404 recent_menu_item =
405 (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w_current->menubar),
406 "File/Recent files");
407 if(recent_menu_item == NULL)
408 return;
410 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(recent_menu_item));
411 gtk_widget_destroy(submenu);
412 x_menu_attach_recent_files_submenu(w_current);
416 /*! \brief Remove all entries from the recent files
417 * list and update all toplevels.
419 static void clear_recent_file_list(gpointer data)
421 GList *p;
423 p = recent_files;
424 while(p) {
425 g_free(p->data);
426 p = g_list_next(p);
428 g_list_free(recent_files);
429 recent_files = NULL;
431 update_recent_files_menus();
434 static void recent_file_clicked(gpointer filename)
436 FILE *fp;
437 PAGE *page;
438 GSCHEM_TOPLEVEL *w_current;
440 /* Check if the file exists */
441 fp = fopen((char *) filename, "r");
442 if(fp == NULL) {
443 /* Remove this entry from all menus */
444 s_log_message(_("Couldn't open file %s\n"), (char *) filename);
445 recent_files = g_list_remove(recent_files, filename);
446 update_recent_files_menus();
447 return;
449 fclose(fp);
451 w_current = gschem_toplevel_new();
452 w_current->toplevel = s_toplevel_new();
453 x_window_setup(w_current);
454 page = x_window_open_page(w_current, (char *)filename);
455 x_window_set_current_page(w_current, page);
456 s_log_message (_("New Window created [%s]\n"), (char *)filename);
459 /*! \brief Attach a submenu with filenames to the 'Recent files'
460 * menu item.
462 * Called from x_window_setup().
464 void x_menu_attach_recent_files_submenu(GSCHEM_TOPLEVEL *w_current)
466 gulong id;
467 GtkWidget *tmp;
468 GtkWidget *recent_menu_item, *recent_submenu;
470 recent_menu_item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(
471 w_current->menubar), "File/Recent files");
472 if(recent_menu_item == NULL)
473 return;
475 /* disconnect all unblocked signals */
476 while(1) {
477 id = g_signal_handler_find(recent_menu_item, G_SIGNAL_MATCH_UNBLOCKED,
478 0, 0, NULL, NULL, NULL);
479 if(id == 0)
480 break;
481 gtk_signal_disconnect(recent_menu_item, id);
484 /* remove 'q' from the menu item string; there has to be a better way to
485 * create a menu item without a hotkey being assigned to it automatically */
486 GtkWidget *label = gtk_bin_get_child(GTK_BIN(recent_menu_item));
487 gtk_label_set_text(GTK_LABEL(label), _("Recent files"));
489 recent_submenu = gtk_menu_new();
490 GList *p = recent_files;
491 while(p) {
492 tmp = gtk_menu_item_new_with_label((gchar *)p->data);
493 gtk_signal_connect_object(GTK_OBJECT(tmp), "activate",
494 GTK_SIGNAL_FUNC (recent_file_clicked),
495 p->data);
496 gtk_menu_append(GTK_MENU(recent_submenu), tmp);
497 p = g_list_next(p);
500 if(recent_files != NULL) {
501 /* Append the 'Clear' menu item to the submenu */
502 GtkWidget *alignment = gtk_alignment_new(0.5, 0, 0, 0);
504 tmp = gtk_menu_item_new();
505 gtk_container_add(GTK_CONTAINER(alignment), gtk_label_new(_("Clear")));
506 gtk_container_add(GTK_CONTAINER(tmp), alignment);
508 gtk_signal_connect_object(GTK_OBJECT(tmp), "activate",
509 GTK_SIGNAL_FUNC (clear_recent_file_list), NULL);
511 gtk_menu_append(GTK_MENU(recent_submenu), gtk_separator_menu_item_new());
512 gtk_menu_append(GTK_MENU(recent_submenu), tmp);
515 gtk_widget_show_all(recent_submenu);
516 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_menu_item), recent_submenu);
519 /*! \brief Add a filename to the list of recent files.
521 void recent_files_add(const char *filename)
523 gchar *basename;
525 basename = g_path_get_basename(filename);
526 if(strstr(basename, "untitled_") == basename) {
527 g_free(basename);
528 return;
531 g_free(basename);
533 /* check if it is a duplicate */
534 GList *p = recent_files;
535 while(p) {
536 if(strcmp((char *)p->data, filename) == 0 )
537 return;
538 p = g_list_next(p);
541 filename = g_strdup(filename);
542 recent_files = g_list_prepend(recent_files, (gpointer)filename);
543 update_recent_files_menus();
546 /*! \brief Make RECENT_FILES_STORE contain an empty file list.
548 static void recent_files_create_empty()
550 gchar *c;
551 const gchar * const tmp[] = { NULL };
552 GKeyFile *kf = g_key_file_new();
553 gchar *file = g_build_filename(g_get_home_dir(), RECENT_FILES_STORE, NULL);
555 g_key_file_set_string_list(kf, "Recent files", "Files", tmp, 0);
556 c = g_key_file_to_data(kf, NULL, NULL);
557 g_key_file_free(kf);
559 g_file_set_contents(file, c, -1, NULL);
560 g_free(c);
561 g_free(file);
564 /*! \brief Save the list of recent files to RECENT_FILES_STORE.
566 * \param [in] user_data unused
568 void recent_files_save(gpointer user_data)
570 gchar **files = NULL;
571 int num = 0;
572 gchar *c;
573 gchar *file = g_build_filename(g_get_home_dir(), RECENT_FILES_STORE, NULL);
575 GList *p = recent_files;
576 if(p == NULL) {
577 recent_files_create_empty();
578 return;
581 while(p) {
582 files = g_realloc(files, (num + 1) * sizeof(gchar *));
583 files[num++] = (gchar *)p->data;
584 p = g_list_next(p);
587 GKeyFile *kf = g_key_file_new();
589 g_key_file_set_string_list(kf, "Recent files", "Files",
590 (const gchar **)files, num);
591 c = g_key_file_to_data(kf, NULL, NULL);
592 g_file_set_contents(file, c, -1, NULL);
594 g_free(c);
595 g_free(file);
596 g_free(files);
597 g_key_file_free(kf);
600 /*! \brief Load the recent file list using data from
601 * RECENT_FILES_STORE.
603 * Must be called before any other recent-files-related
604 * functions.
606 void recent_files_load()
608 GKeyFile *kf = g_key_file_new();
609 gchar *file = g_build_filename(g_get_home_dir(), RECENT_FILES_STORE, NULL);
611 if(!g_file_test(file, G_FILE_TEST_EXISTS)) {
612 gchar *dir = g_build_filename(g_get_home_dir(), ".gEDA", NULL);
613 g_mkdir(dir, S_IRWXU | S_IRWXG);
614 g_free(dir);
616 recent_files_create_empty();
619 if(!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, NULL)) {
620 /* error opening key file, create an empty one and try again */
621 recent_files_create_empty();
622 if(!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, NULL))
623 return;
626 gsize len;
627 gchar **list = g_key_file_get_string_list(kf, "Recent files",
628 "Files", &len, NULL);
630 if(list == NULL) {
631 /* error reading key file, don't bother to correct;
632 * just overwrite it with an empty one */
633 recent_files_create_empty();
634 return;
637 while(len > 0) {
638 len--;
639 recent_files = g_list_prepend(recent_files, list[len]);
642 g_free(list);
643 g_free(file);
644 g_key_file_free(kf);