Moved 3 rc promotion keywords from gschem into libgeda (fix for bug#1748143)
[geda-gaf/peter-b.git] / gschem / src / x_menus.c
blobf7c00b60b559c28fa07527facacca6a22e218dc3
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 <libgeda/libgeda.h>
29 #include "../include/globals.h"
30 #include "../include/prototype.h"
32 #if GLIB_CHECK_VERSION(2,6,0)
33 #include <glib/gstdio.h>
34 #endif
36 #ifdef HAVE_LIBDMALLOC
37 #include <dmalloc.h>
38 #endif
40 static GtkItemFactoryEntry popup_items[] = {
41 { N_("/Add Net"), NULL, i_callback_add_net, 0, NULL},
42 { N_("/Add Attribute..."), NULL, i_callback_add_attribute, 0, NULL},
43 { N_("/Add Component..."), NULL, i_callback_add_component, 0, NULL},
44 { N_("/Add Bus"), NULL, i_callback_add_bus, 0, NULL},
45 { N_("/Add Text"), NULL, i_callback_add_text, 0, NULL},
46 { "/sep1", NULL, NULL, 0, "<Separator>"},
47 { N_("/Zoom In"), NULL, i_callback_view_zoom_in, 0, NULL},
48 { N_("/Zoom Out"), NULL, i_callback_view_zoom_out, 0, NULL},
49 { N_("/Zoom Box"), NULL, i_callback_view_zoom_box, 0, NULL},
50 { N_("/Zoom Extents"), NULL, i_callback_view_zoom_extents, 0, NULL},
51 { "/sep1", NULL, NULL, 0, "<Separator>"},
52 { N_("/Select"), NULL, i_callback_edit_select, 0, NULL},
53 { N_("/Edit..."), NULL, i_callback_edit_edit, 0, NULL},
54 { N_("/Copy"), NULL, i_callback_edit_copy, 0, NULL},
55 { N_("/Move"), NULL, i_callback_edit_move, 0, NULL},
56 { N_("/Delete"), NULL, i_callback_edit_delete, 0, NULL},
57 /* Menu items for hierarchy added by SDB 1.9.2005. */
58 {"/sep1", NULL, NULL, 0, "<Separator>"},
59 {N_("/Down Schematic"), NULL, i_callback_hierarchy_down_schematic, 0, NULL},
60 {N_("/Down Symbol"), NULL, i_callback_hierarchy_down_symbol, 0, NULL},
61 {N_("/Up"), NULL, i_callback_hierarchy_up, 0, NULL},
62 };
64 int npopup_items = sizeof(popup_items) / sizeof(popup_items[0]);
66 /*! \todo Finish function documentation!!!
67 * \brief
68 * \par Function Description
71 static void g_menu_execute(char *func)
73 gchar *guile_string;
75 guile_string = g_strdup_printf("(%s)", func);
76 #if DEBUG
77 printf("%s\n", guile_string);
78 #endif
79 scm_c_eval_string (guile_string);
80 g_free(guile_string);
83 /*! \todo Finish function documentation!!!
84 * \brief
85 * \par Function Description
88 void get_main_menu(TOPLEVEL * w_current, GtkWidget ** menubar)
90 char *buf;
91 GtkWidget *menu_item;
92 GtkWidget *root_menu;
93 GtkWidget *menu_bar;
94 GtkWidget *menu;
95 int scm_items_len;
96 SCM scm_items;
97 SCM scm_item;
98 SCM scm_item_name;
99 SCM scm_item_func;
100 SCM scm_item_hotkey_func;
101 SCM scm_index;
102 SCM scm_keys;
103 char *menu_name;
104 char **raw_menu_name = g_malloc (sizeof(char *));
105 char *menu_item_name;
106 char *raw_menu_item_name;
107 char *menu_item_func;
108 char *menu_item_hotkey_func;
109 char *menu_item_keys;
110 char *spaces;
111 int i, j;
112 int name_len, key_len, pad;
113 int sum, diff, max_size, space_size;
114 PangoLayout *layout;
115 int name_width, keys_width;
117 menu_bar = gtk_menu_bar_new ();
118 for (i = 0 ; i < s_menu_return_num(); i++) {
120 scm_items = s_menu_return_entry(i, raw_menu_name);
121 if (*raw_menu_name == NULL) {
122 fprintf(stderr, "Oops.. got a NULL menu name in get_main_menu()\n");
123 exit(-1);
126 menu = gtk_menu_new();
128 menu_item = gtk_tearoff_menu_item_new ();
129 gtk_menu_append(GTK_MENU(menu), menu_item);
130 gtk_widget_show(menu_item);
132 layout = gtk_widget_create_pango_layout(menu, " ");
133 pango_layout_get_pixel_size(layout, &space_size, NULL);
134 g_object_unref(layout);
136 layout = gtk_widget_create_pango_layout(menu,
137 "123456789012345678901234567890");
138 pango_layout_get_pixel_size(layout, &max_size, NULL);
139 g_object_unref(layout);
141 scm_items_len = (int) scm_ilength (scm_items);
142 for (j = 0 ; j < scm_items_len; j++) {
144 scm_index = scm_from_int (j);
145 scm_item = scm_list_ref (scm_items, scm_index);
146 scm_item_name = SCM_CAR (scm_item);
147 scm_item_func = SCM_CADR (scm_item);
148 scm_item_hotkey_func = SCM_CADDR (scm_item);
149 SCM_ASSERT(scm_is_string(scm_item_name), scm_item_name, SCM_ARGn, "get_main_menu item_name");
150 SCM_ASSERT(SCM_SYMBOLP(scm_item_func), scm_item_func, SCM_ARGn, "get_main_menu item_func");
151 SCM_ASSERT(SCM_SYMBOLP(scm_item_hotkey_func), scm_item_hotkey_func, SCM_ARGn, "get_main_menu hotkey_func");
153 raw_menu_item_name = SCM_STRING_CHARS (scm_item_name);
154 menu_item_func = SCM_SYMBOL_CHARS (scm_item_func);
155 menu_item_hotkey_func = SCM_SYMBOL_CHARS (scm_item_hotkey_func);
157 menu_item_name = (char *) gettext(raw_menu_item_name);
159 if (strcmp(menu_item_name, "SEPARATOR") == 0) {
160 menu_item = gtk_menu_item_new();
161 gtk_menu_append(GTK_MENU(menu), menu_item);
162 } else {
163 buf = g_strdup_printf("(find-key '%s)", menu_item_hotkey_func);
164 scm_keys = scm_c_eval_string (buf);
165 g_free(buf);
166 if (scm_keys == SCM_BOOL_F) {
167 menu_item_keys = g_malloc(sizeof(char)*2);
168 menu_item_keys[0] = ' ';
169 menu_item_keys[1] = '\0';
170 } else {
171 menu_item_keys = g_strdup (SCM_STRING_CHARS (scm_keys));
174 name_len = strlen(menu_item_name);
175 key_len = strlen(menu_item_keys);
177 layout = gtk_widget_create_pango_layout(menu, menu_item_name);
178 pango_layout_get_pixel_size(layout, &name_width, NULL);
179 g_object_unref(layout);
181 layout = gtk_widget_create_pango_layout(menu, menu_item_keys);
182 pango_layout_get_pixel_size(layout, &keys_width, NULL);
183 g_object_unref(layout);
185 sum = name_width + keys_width;
187 diff = max_size - sum;
188 pad = diff/space_size;
189 if (pad < 0) {
190 pad = 1;
193 spaces = g_malloc(sizeof(char)*(pad+1));
194 memset(spaces, ' ', pad);
195 spaces[pad] = '\0';
196 buf = g_strdup_printf("%s%s%s", menu_item_name, spaces, menu_item_keys);
198 #if DEBUG
199 printf("%s :\n %d %d %d = %d\n", buf, name_len, pad, key_len,
200 name_len + pad + key_len);
201 #endif
203 menu_item = gtk_menu_item_new_with_label(buf);
204 g_free(buf);
205 gtk_menu_append(GTK_MENU(menu), menu_item);
206 gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
207 GTK_SIGNAL_FUNC(g_menu_execute),
208 (gpointer) g_strdup (menu_item_func));
209 /* The g_strdup is a memory leak, but this is okay. I think. */
210 g_free(spaces);
211 g_free(menu_item_keys);
214 gtk_widget_show(menu_item);
216 /* add a handle to the menu_bar object to get access to widget objects */
217 /* This string should NOT be internationalized */
218 buf = g_strdup_printf("%s/%s", *raw_menu_name, raw_menu_item_name);
219 gtk_object_set_data(GTK_OBJECT(menu_bar), buf, menu_item);
220 g_free(buf);
224 menu_name = (char *) gettext(*raw_menu_name);
225 root_menu = gtk_menu_item_new_with_label (menu_name);
226 /* do not free *raw_menu_name */
228 /* no longer right justify the help menu since that has gone out of style */
230 gtk_widget_show (root_menu);
231 gtk_menu_item_set_submenu (GTK_MENU_ITEM (root_menu), menu);
232 gtk_menu_bar_append (GTK_MENU_BAR (menu_bar), root_menu);
235 g_free(raw_menu_name);
236 *menubar = menu_bar;
239 /*! \todo Finish function documentation!!!
240 * \brief
241 * \par Function Description
244 static gchar* gettext_fn(const gchar *path,
245 gpointer func_data ATTRIBUTE_UNUSED)
247 return gettext(path);
250 GtkWidget *get_main_popup(TOPLEVEL *w_current)
252 static GtkItemFactory *item_factory;
253 GtkAccelGroup *accel_group;
254 GtkWidget *menu;
256 accel_group = gtk_accel_group_new();
258 /* This function initializes the item factory.
259 Param 1: The type of menu - can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU, or GTK_TYPE_OPTION_MENU.
260 Param 2: The path of the menu.
261 Param 3: A pointer to a gtk_accel_group. The item factory sets up
262 the accelerator table while generating menus.
264 item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<popup>",
265 accel_group);
266 gtk_item_factory_set_translate_func (item_factory,
267 gettext_fn,
268 NULL, NULL);
269 /* This function creates the pop-up menu itself & attaches it to the
270 GtkItemFactory. Pass the item factory,
271 the number of items in the array, the array itself, and any
272 callback data for the the menu items. Note that npopup_items is
273 a static var declared in this file above; popup_items is also a
274 static var declared above.
276 gtk_item_factory_create_items(item_factory, npopup_items, popup_items, w_current);
278 /* Finally, return the actual menu created by the item factory. */
279 menu = (GtkWidget *) gtk_item_factory_get_widget(item_factory, "<popup>");
280 return (menu);
284 /*! \todo Finish function documentation!!!
285 * \brief
286 * \par Function Description
288 * \note
289 * need to look at this... here and the setup
291 gint do_popup (TOPLEVEL *w_current, GdkEventButton *event)
293 GtkWidget *menu; /* =NULL; */ /* was static */
295 menu = NULL; /* Why do I need to do this? */
296 if (!menu)
297 menu = (GtkWidget *) w_current->popup_menu;
299 if (menu == NULL) {
300 printf("null menu\n");
303 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
304 event->button, event->time);
306 return FALSE;
309 /*! \todo Finish function documentation!!!
310 * \brief
311 * \par Function Description
314 void x_menus_sensitivity (TOPLEVEL* w_current, const char *buf, int flag)
316 GtkWidget* item=NULL;
318 if (!buf) {
319 return;
322 if (!w_current->menubar) {
323 return;
326 item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(w_current->menubar), buf);
328 if (item) {
329 gtk_widget_set_sensitive(GTK_WIDGET(item), flag);
330 /* free(item); */ /* Why doesn't this need to be freed? */
331 } else {
332 s_log_message(_("Tried to set the sensitivity on a non-existent menu item\n"));
337 /*! \todo Finish function documentation!!!
338 * \brief
339 * \par Function Description
340 * This function sets the sensitivity of the items in the right button
341 * popup.
343 * \note
344 * 1.9.2005 -- SDB.
346 void x_menus_popup_sensitivity (TOPLEVEL* w_current, const char *buf, int flag)
348 GtkWidget *menu_item;
349 GtkItemFactory *menu_item_factory;
351 if (!buf) {
352 return;
355 if (!w_current->popup_menu) {
356 s_log_message(_("Popup_menu_item_factory doesn't exist!\n"));
357 return;
361 * first get entire item factory from popup, then get the individual
362 * menu item indexed by buf.
364 menu_item_factory = (GtkItemFactory *)gtk_item_factory_from_widget(w_current->popup_menu);
365 menu_item = (GtkWidget *) gtk_item_factory_get_widget(menu_item_factory, buf);
366 if (menu_item) {
367 gtk_widget_set_sensitive(GTK_WIDGET(menu_item), flag);
368 } else {
369 s_log_message(_("Tried to set the sensitivity on a non-existent popup menu_item\n"));
373 #if !GLIB_CHECK_VERSION(2,8,0)
374 /* g_file_set_contents() exists only in glib >= 2.8 */
375 gboolean g_file_set_contents(const gchar *filename, const gchar *contents,
376 gssize length, GError **error)
378 FILE *fp;
379 gboolean ret = FALSE;
381 fp = fopen(filename, "wb");
382 if(fp == NULL)
383 return FALSE;
385 if(length == -1) {
386 /* It's a null-terminated string. */
387 if(fputs(contents, fp) == EOF)
388 goto out;
389 } else {
390 if(fwrite(contents, length, 1, fp) != 1)
391 goto out;
394 ret = TRUE;
395 out:
396 fclose(fp);
397 return ret;
399 #endif /* !GLIB_CHECK_VERSION(2,8,0) */
401 #if !GLIB_CHECK_VERSION(2,6,0)
403 /* disable recent files support */
404 inline void x_menu_attach_recent_files_submenu(TOPLEVEL *w_current)
406 GtkWidget *recent_menu_item;
408 recent_menu_item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(
409 w_current->menubar), "File/Recent files");
410 gtk_widget_destroy(recent_menu_item);
413 inline void recent_files_load() { }
414 inline void recent_files_save() { }
415 inline void recent_files_add(const char *filename) { }
417 #else
419 /* The list of recently loaded files. */
420 static GList *recent_files = NULL;
422 #define RECENT_FILES_STORE ".gEDA/gschem-recent-files"
424 /*! \brief Make all toplevels reflect changes to the
425 * recent files list.
427 static void update_recent_files_menus()
429 TOPLEVEL *w;
430 GtkWidget *submenu, *recent_menu_item;
432 w = global_window_current;
433 while(w->prev)
434 w = w->prev;
436 while(w) {
437 if(w->wid == -1 || w->menubar == NULL) {
438 w = w->next;
439 continue;
442 recent_menu_item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(
443 w->menubar), "File/Recent files");
444 if(recent_menu_item == NULL)
445 return;
447 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(recent_menu_item));
448 gtk_widget_destroy(submenu);
449 x_menu_attach_recent_files_submenu(w);
450 w = w->next;
454 /*! \brief Remove all entries from the recent files
455 * list and update all toplevels.
457 static void clear_recent_file_list(gpointer data)
459 GList *p;
461 p = recent_files;
462 while(p) {
463 g_free(p->data);
464 p = g_list_next(p);
466 g_list_free(recent_files);
467 recent_files = NULL;
469 update_recent_files_menus();
472 static void recent_file_clicked(gpointer filename)
474 FILE *fp;
475 PAGE *page;
476 TOPLEVEL *w;
478 /* Check if the file exists */
479 fp = fopen((char *) filename, "r");
480 if(fp == NULL) {
481 /* Remove this entry from all menus */
482 s_log_message(_("Couldn't open file %s\n"), (char *) filename);
483 recent_files = g_list_remove(recent_files, filename);
484 update_recent_files_menus();
485 return;
487 fclose(fp);
489 w = s_toplevel_new();
490 x_window_setup(w);
491 page = x_window_open_page(w, (char *)filename);
492 x_window_set_current_page(w, page);
493 s_log_message (_("New Window created [%s]\n"), (char *)filename);
496 /*! \brief Attach a submenu with filenames to the 'Recent files'
497 * menu item.
499 * Called from x_window_setup().
501 void x_menu_attach_recent_files_submenu(TOPLEVEL *w_current)
503 gulong id;
504 GtkWidget *tmp;
505 GtkWidget *recent_menu_item, *recent_submenu;
507 recent_menu_item = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(
508 w_current->menubar), "File/Recent files");
509 if(recent_menu_item == NULL)
510 return;
512 /* disconnect all unblocked signals */
513 while(1) {
514 id = g_signal_handler_find(recent_menu_item, G_SIGNAL_MATCH_UNBLOCKED,
515 0, 0, NULL, NULL, NULL);
516 if(id == 0)
517 break;
518 gtk_signal_disconnect(recent_menu_item, id);
521 /* remove 'q' from the menu item string; there has to be a better way to
522 * create a menu item without a hotkey being assigned to it automatically */
523 GtkWidget *label = gtk_bin_get_child(GTK_BIN(recent_menu_item));
524 gtk_label_set_text(GTK_LABEL(label), _("Recent files"));
526 recent_submenu = gtk_menu_new();
527 GList *p = recent_files;
528 while(p) {
529 tmp = gtk_menu_item_new_with_label((gchar *)p->data);
530 gtk_signal_connect_object(GTK_OBJECT(tmp), "activate",
531 GTK_SIGNAL_FUNC (recent_file_clicked),
532 p->data);
533 gtk_menu_append(GTK_MENU(recent_submenu), tmp);
534 p = g_list_next(p);
537 if(recent_files != NULL) {
538 /* Append the 'Clear' menu item to the submenu */
539 GtkWidget *alignment = gtk_alignment_new(0.5, 0, 0, 0);
541 tmp = gtk_menu_item_new();
542 gtk_container_add(GTK_CONTAINER(alignment), gtk_label_new(_("Clear")));
543 gtk_container_add(GTK_CONTAINER(tmp), alignment);
545 gtk_signal_connect_object(GTK_OBJECT(tmp), "activate",
546 GTK_SIGNAL_FUNC (clear_recent_file_list), NULL);
548 gtk_menu_append(GTK_MENU(recent_submenu), gtk_separator_menu_item_new());
549 gtk_menu_append(GTK_MENU(recent_submenu), tmp);
552 gtk_widget_show_all(recent_submenu);
553 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_menu_item), recent_submenu);
556 /*! \brief Add a filename to the list of recent files.
558 void recent_files_add(const char *filename)
560 gchar *basename;
562 basename = g_path_get_basename(filename);
563 if(strstr(basename, "untitled_") == basename) {
564 g_free(basename);
565 return;
568 g_free(basename);
570 /* check if it is a duplicate */
571 GList *p = recent_files;
572 while(p) {
573 if(strcmp((char *)p->data, filename) == 0 )
574 return;
575 p = g_list_next(p);
578 filename = g_strdup(filename);
579 recent_files = g_list_prepend(recent_files, (gpointer)filename);
580 update_recent_files_menus();
583 /*! \brief Make RECENT_FILES_STORE contain an empty file list.
585 static void recent_files_create_empty()
587 gchar *c;
588 const gchar * const tmp[] = { NULL };
589 GKeyFile *kf = g_key_file_new();
590 gchar *file = g_build_filename(g_get_home_dir(), RECENT_FILES_STORE, NULL);
592 g_key_file_set_string_list(kf, "Recent files", "Files", tmp, 0);
593 c = g_key_file_to_data(kf, NULL, NULL);
594 g_key_file_free(kf);
596 g_file_set_contents(file, c, -1, NULL);
597 g_free(c);
598 g_free(file);
601 /*! \brief Save the list of recent files to RECENT_FILES_STORE.
603 * \param [in] user_data unused
605 void recent_files_save(gpointer user_data)
607 gchar **files = NULL;
608 int num = 0;
609 gchar *c;
610 gchar *file = g_build_filename(g_get_home_dir(), RECENT_FILES_STORE, NULL);
612 GList *p = recent_files;
613 if(p == NULL) {
614 recent_files_create_empty();
615 return;
618 while(p) {
619 files = g_realloc(files, (num + 1) * sizeof(gchar *));
620 files[num++] = (gchar *)p->data;
621 p = g_list_next(p);
624 GKeyFile *kf = g_key_file_new();
626 g_key_file_set_string_list(kf, "Recent files", "Files",
627 (const gchar **)files, num);
628 c = g_key_file_to_data(kf, NULL, NULL);
629 g_file_set_contents(file, c, -1, NULL);
631 g_free(c);
632 g_free(file);
633 g_free(files);
634 g_key_file_free(kf);
637 /*! \brief Load the recent file list using data from
638 * RECENT_FILES_STORE.
640 * Must be called before any other recent-files-related
641 * functions.
643 void recent_files_load()
645 GKeyFile *kf = g_key_file_new();
646 gchar *file = g_build_filename(g_get_home_dir(), RECENT_FILES_STORE, NULL);
648 if(!g_file_test(file, G_FILE_TEST_EXISTS)) {
649 gchar *dir = g_build_filename(g_get_home_dir(), ".gEDA", NULL);
650 g_mkdir(dir, S_IRWXU | S_IRWXG);
651 g_free(dir);
653 recent_files_create_empty();
656 if(!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, NULL)) {
657 /* error opening key file, create an empty one and try again */
658 recent_files_create_empty();
659 if(!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, NULL))
660 return;
663 gsize len;
664 gchar **list = g_key_file_get_string_list(kf, "Recent files",
665 "Files", &len, NULL);
667 if(list == NULL) {
668 /* error reading key file, don't bother to correct;
669 * just overwrite it with an empty one */
670 recent_files_create_empty();
671 return;
674 while(len > 0) {
675 len--;
676 recent_files = g_list_prepend(recent_files, list[len]);
679 g_free(list);
680 g_free(file);
681 g_key_file_free(kf);
684 #endif /* GLIB_MINOR_VERSION < 6 */