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
29 #include <glib/gstdio.h>
31 #ifdef HAVE_LIBDMALLOC
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
},
60 int npopup_items
= sizeof(popup_items
) / sizeof(popup_items
[0]);
62 /*! \todo Finish function documentation!!!
64 * \par Function Description
67 static void g_menu_execute(char *func
)
71 guile_string
= g_strdup_printf("(%s)", func
);
73 printf("%s\n", guile_string
);
75 g_scm_c_eval_string_protected (guile_string
);
79 /*! \todo Finish function documentation!!!
81 * \par Function Description
84 void get_main_menu(GtkWidget
** menubar
)
97 SCM scm_item_hotkey_func
;
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
;
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");
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";
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
;
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
;
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
);
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
);
177 if (scm_keys
== SCM_BOOL_F
) {
180 menu_item_keys
= SCM_STRING_CHARS (scm_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
);
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
);
226 /*! \todo Finish function documentation!!!
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
;
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>",
253 gtk_item_factory_set_translate_func (item_factory
,
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>");
271 /*! \todo Finish function documentation!!!
273 * \par Function Description
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? */
284 menu
= (GtkWidget
*) w_current
->popup_menu
;
287 printf("null menu\n");
290 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
291 event
->button
, event
->time
);
296 /*! \todo Finish function documentation!!!
298 * \par Function Description
301 void x_menus_sensitivity (GSCHEM_TOPLEVEL
*w_current
, const char *buf
, int flag
)
303 GtkWidget
* item
=NULL
;
309 if (!w_current
->menubar
) {
313 item
= (GtkWidget
*) gtk_object_get_data(GTK_OBJECT(w_current
->menubar
), buf
);
316 gtk_widget_set_sensitive(GTK_WIDGET(item
), flag
);
317 /* free(item); */ /* Why doesn't this need to be freed? */
319 s_log_message(_("Tried to set the sensitivity on non-existent menu item '%s'\n"), buf
);
324 /*! \todo Finish function documentation!!!
326 * \par Function Description
327 * This function sets the sensitivity of the items in the right button
333 void x_menus_popup_sensitivity (GSCHEM_TOPLEVEL
*w_current
, const char *buf
, int flag
)
335 GtkWidget
*menu_item
;
336 GtkItemFactory
*menu_item_factory
;
342 if (!w_current
->popup_menu
) {
343 s_log_message(_("Popup_menu_item_factory doesn't exist!\n"));
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
);
354 gtk_widget_set_sensitive(GTK_WIDGET(menu_item
), flag
);
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
;
371 /*! \brief Make all toplevels reflect changes to the
374 static void update_recent_files_menus()
376 GSCHEM_TOPLEVEL
*w_current
;
377 GtkWidget
*submenu
, *recent_menu_item
;
380 for (iter
= global_window_list
;
382 iter
= g_list_next (iter
)) {
383 w_current
= (GSCHEM_TOPLEVEL
*)iter
->data
;
385 if (w_current
->menubar
== NULL
)
389 (GtkWidget
*) gtk_object_get_data(GTK_OBJECT(w_current
->menubar
),
390 "_File/Open Recen_t");
391 if(recent_menu_item
== NULL
)
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
)
412 g_list_free(recent_files
);
415 update_recent_files_menus();
419 recent_file_free_menu_data (gpointer data
, GClosure
*closure
) {
423 static void recent_file_clicked(GtkMenuItem
*menuitem
, gpointer user_data
)
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");
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();
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'
450 * Called from x_window_setup().
452 void x_menu_attach_recent_files_submenu(GSCHEM_TOPLEVEL
*w_current
)
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
)
463 /* disconnect all unblocked signals */
465 id
= g_signal_handler_find(recent_menu_item
, G_SIGNAL_MATCH_UNBLOCKED
,
466 0, 0, NULL
, NULL
, NULL
);
469 gtk_signal_disconnect(recent_menu_item
, id
);
472 recent_submenu
= gtk_menu_new();
473 GList
*p
= recent_files
;
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
,
482 (GClosureNotify
) recent_file_free_menu_data
,
484 gtk_menu_append(GTK_MENU(recent_submenu
), tmp
);
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
512 void recent_files_add(const char *filename
)
517 GList
*p
= recent_files
;
519 basename
= g_path_get_basename(filename
);
520 if(strstr(basename
, "untitled_") == basename
) {
527 /* Normalize the filename. */
528 save_fn
= f_normalize_filename (filename
, &err
);
530 save_fn
= g_strdup (filename
);
534 /* Check if the file is already in the list. */
536 if (strcmp (save_fn
, (gchar
*) p
->data
) == 0) {
543 /* Since we found the filename already in the list, move it to
544 * the head of the list. */
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
);
550 /* Otherwise, just add the new filename to the front of the
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()
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
);
571 g_file_set_contents(file
, c
, -1, NULL
);
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
];
585 gchar
*file
= g_build_filename(s_path_user_config (), RECENT_FILES_STORE
, NULL
);
587 GList
*p
= recent_files
;
589 recent_files_create_empty();
593 while((p
!= NULL
) && (num
< MAX_RECENT_FILES
)) {
594 files
[num
++] = (gchar
*)p
->data
;
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
);
610 /*! \brief Load the recent file list using data from
611 * RECENT_FILES_STORE.
613 * Must be called before any other recent-files-related
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
))
635 gchar
**list
= g_key_file_get_string_list(kf
, "Recent files",
636 "Files", &len
, NULL
);
639 /* error reading key file, don't bother to correct;
640 * just overwrite it with an empty one */
641 recent_files_create_empty();
647 recent_files
= g_list_prepend(recent_files
, list
[len
]);