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
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_("/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
},
59 int npopup_items
= sizeof(popup_items
) / sizeof(popup_items
[0]);
61 /*! \todo Finish function documentation!!!
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!!!
73 * \par Function Description
76 void get_main_menu(GtkWidget
** menubar
)
84 char **raw_menu_name
= g_malloc (sizeof(char *));
86 char *raw_menu_item_name
;
90 int name_len
, key_len
, pad
;
91 int sum
, diff
, max_size
, space_size
;
93 int name_width
, keys_width
;
95 menu_bar
= gtk_menu_bar_new ();
96 for (i
= 0 ; i
< s_menu_return_num(); i
++) {
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");
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
);
125 SCM scm_item_hotkey_func
;
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
);
145 scm_keys
= g_scm_apply_protected(g_scm_safe_ref_lookup("find-key"),
146 scm_list_1(scm_item_hotkey_func
),
148 if (scm_keys
== SCM_BOOL_F
) {
149 menu_item_keys
= g_strdup (" ");
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
;
170 spaces
= g_strnfill (pad
, ' ');
171 buf
= g_strdup_printf("%s%s%s", menu_item_name
, spaces
, menu_item_keys
);
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
);
181 menu_item
= gtk_menu_item_new_with_label(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. */
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
);
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
);
219 /*! \todo Finish function documentation!!!
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
;
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>",
246 gtk_item_factory_set_translate_func (item_factory
,
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>");
264 /*! \todo Finish function documentation!!!
266 * \par Function Description
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? */
277 menu
= (GtkWidget
*) w_current
->popup_menu
;
280 printf("null menu\n");
283 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
284 event
->button
, event
->time
);
289 /*! \todo Finish function documentation!!!
291 * \par Function Description
294 void x_menus_sensitivity (GSCHEM_TOPLEVEL
*w_current
, const char *buf
, int flag
)
296 GtkWidget
* item
=NULL
;
302 if (!w_current
->menubar
) {
306 item
= (GtkWidget
*) gtk_object_get_data(GTK_OBJECT(w_current
->menubar
), buf
);
309 gtk_widget_set_sensitive(GTK_WIDGET(item
), flag
);
310 /* free(item); */ /* Why doesn't this need to be freed? */
312 s_log_message(_("Tried to set the sensitivity on non-existent menu item '%s'\n"), buf
);
317 /*! \todo Finish function documentation!!!
319 * \par Function Description
320 * This function sets the sensitivity of the items in the right button
326 void x_menus_popup_sensitivity (GSCHEM_TOPLEVEL
*w_current
, const char *buf
, int flag
)
328 GtkWidget
*menu_item
;
329 GtkItemFactory
*menu_item_factory
;
335 if (!w_current
->popup_menu
) {
336 s_log_message(_("Popup_menu_item_factory doesn't exist!\n"));
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
);
347 gtk_widget_set_sensitive(GTK_WIDGET(menu_item
), flag
);
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
)
359 gboolean ret
= FALSE
;
361 fp
= fopen(filename
, "wb");
366 /* It's a null-terminated string. */
367 if(fputs(contents
, fp
) == EOF
)
370 if(fwrite(contents
, length
, 1, fp
) != 1)
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
390 static void update_recent_files_menus()
392 GSCHEM_TOPLEVEL
*w_current
;
393 GtkWidget
*submenu
, *recent_menu_item
;
396 for (iter
= global_window_list
;
398 iter
= g_list_next (iter
)) {
399 w_current
= (GSCHEM_TOPLEVEL
*)iter
->data
;
401 if (w_current
->menubar
== NULL
)
405 (GtkWidget
*) gtk_object_get_data(GTK_OBJECT(w_current
->menubar
),
406 "File/Recent files");
407 if(recent_menu_item
== NULL
)
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
)
428 g_list_free(recent_files
);
431 update_recent_files_menus();
434 static void recent_file_clicked(gpointer filename
)
438 GSCHEM_TOPLEVEL
*w_current
;
440 /* Check if the file exists */
441 fp
= fopen((char *) filename
, "r");
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();
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'
462 * Called from x_window_setup().
464 void x_menu_attach_recent_files_submenu(GSCHEM_TOPLEVEL
*w_current
)
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
)
475 /* disconnect all unblocked signals */
477 id
= g_signal_handler_find(recent_menu_item
, G_SIGNAL_MATCH_UNBLOCKED
,
478 0, 0, NULL
, NULL
, NULL
);
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
;
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
),
496 gtk_menu_append(GTK_MENU(recent_submenu
), tmp
);
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
)
525 basename
= g_path_get_basename(filename
);
526 if(strstr(basename
, "untitled_") == basename
) {
533 /* check if it is a duplicate */
534 GList
*p
= recent_files
;
536 if(strcmp((char *)p
->data
, filename
) == 0 )
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()
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
);
559 g_file_set_contents(file
, c
, -1, NULL
);
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
;
573 gchar
*file
= g_build_filename(g_get_home_dir(), RECENT_FILES_STORE
, NULL
);
575 GList
*p
= recent_files
;
577 recent_files_create_empty();
582 files
= g_realloc(files
, (num
+ 1) * sizeof(gchar
*));
583 files
[num
++] = (gchar
*)p
->data
;
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
);
600 /*! \brief Load the recent file list using data from
601 * RECENT_FILES_STORE.
603 * Must be called before any other recent-files-related
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
);
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
))
627 gchar
**list
= g_key_file_get_string_list(kf
, "Recent files",
628 "Files", &len
, NULL
);
631 /* error reading key file, don't bother to correct;
632 * just overwrite it with an empty one */
633 recent_files_create_empty();
639 recent_files
= g_list_prepend(recent_files
, list
[len
]);