diff options
| author | MenTaLguY <mental@rydia.net> | 2006-01-16 02:36:01 +0000 |
|---|---|---|
| committer | mental <mental@users.sourceforge.net> | 2006-01-16 02:36:01 +0000 |
| commit | 179fa413b047bede6e32109e2ce82437c5fb8d34 (patch) | |
| tree | a5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/interface.cpp | |
| download | inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip | |
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/interface.cpp')
| -rw-r--r-- | src/interface.cpp | 1197 |
1 files changed, 1197 insertions, 0 deletions
diff --git a/src/interface.cpp b/src/interface.cpp new file mode 100644 index 000000000..69ed8818b --- /dev/null +++ b/src/interface.cpp @@ -0,0 +1,1197 @@ +#define __SP_INTERFACE_C__ + +/** + * Main UI stuff + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Frank Felfe <innerspace@iname.com> + * bulia byak <buliabyak@users.sf.net> + * + * Copyright (C) 1999-2005 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * Copyright (C) 2004 David Turner + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gtk/gtk.h> +#include "inkscape-private.h" +#include "extension/effect.h" +#include "widgets/icon.h" +#include "prefs-utils.h" +#include "path-prefix.h" + +#include "shortcuts.h" + +#include "document.h" +#include "desktop-handles.h" +#include "file.h" +#include "interface.h" +#include "desktop.h" +#include "object-ui.h" +#include "selection.h" +#include "selection-chemistry.h" +#include "svg-view-widget.h" +#include "widgets/desktop-widget.h" +#include "sp-item-group.h" +#include "sp-namedview.h" + +#include "helper/action.h" +#include "helper/gnome-utils.h" +#include "helper/window.h" + +#include "io/sys.h" +#include "io/stringstream.h" +#include "io/base64stream.h" + +#include "dialogs/dialog-events.h" + +#include "message-context.h" + + +using Inkscape::IO::StringOutputStream; +using Inkscape::IO::Base64OutputStream; + +/* forward declaration */ +static gint sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view); + +/* Drag and Drop */ +typedef enum { + URI_LIST, + SVG_XML_DATA, + SVG_DATA, + PNG_DATA, + JPEG_DATA, + IMAGE_DATA +} ui_drop_target_info; + +static GtkTargetEntry ui_drop_target_entries [] = { + {"text/uri-list", 0, URI_LIST}, + {"image/svg+xml", 0, SVG_XML_DATA}, + {"image/svg", 0, SVG_DATA}, + {"image/png", 0, PNG_DATA}, + {"image/jpeg", 0, JPEG_DATA}, +}; + +static GtkTargetEntry *completeDropTargets = 0; +static int completeDropTargetsCount = 0; + +#define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0]) +static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries); +static void sp_ui_import_files(gchar *buffer); +static void sp_ui_import_one_file(char const *filename); +static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused); +static void sp_ui_drag_data_received(GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint event_time, + gpointer user_data); +static void sp_ui_menu_item_set_sensitive(SPAction *action, + unsigned int sensitive, + void *data); + +SPActionEventVector menu_item_event_vector = { + {NULL}, + NULL, + NULL, /* set_active */ + sp_ui_menu_item_set_sensitive, /* set_sensitive */ + NULL /* set_shortcut */ +}; + +void +sp_create_window(SPViewWidget *vw, gboolean editable) +{ + GtkWidget *w, *hb; + + g_return_if_fail(vw != NULL); + g_return_if_fail(SP_IS_VIEW_WIDGET(vw)); + + w = sp_window_new("", TRUE); + + if (editable) { + g_object_set_data(G_OBJECT(vw), "window", w); + reinterpret_cast<SPDesktopWidget*>(vw)->window = + static_cast<GtkWindow*>((void*)w); + } + + hb = gtk_hbox_new(FALSE, 0); + gtk_widget_show(hb); + gtk_container_add(GTK_CONTAINER(w), hb); + g_object_set_data(G_OBJECT(w), "hbox", hb); + + /* fixme: */ + if (editable) { + gtk_window_set_default_size((GtkWindow *) w, 640, 480); + g_object_set_data(G_OBJECT(w), "desktop", SP_DESKTOP_WIDGET(vw)->desktop); + g_object_set_data(G_OBJECT(w), "desktopwidget", vw); + g_signal_connect(G_OBJECT(w), "delete_event", G_CALLBACK(sp_ui_delete), vw->view); + g_signal_connect(G_OBJECT(w), "focus_in_event", G_CALLBACK(sp_desktop_widget_set_focus), vw); + } else { + gtk_window_set_policy(GTK_WINDOW(w), TRUE, TRUE, TRUE); + } + + gtk_box_pack_end(GTK_BOX(hb), GTK_WIDGET(vw), TRUE, TRUE, 0); + gtk_widget_show(GTK_WIDGET(vw)); + + + if ( completeDropTargets == 0 || completeDropTargetsCount == 0 ) + { + std::vector<gchar*> types; + + GSList *list = gdk_pixbuf_get_formats(); + while ( list ) { + int i = 0; + GdkPixbufFormat *one = (GdkPixbufFormat*)list->data; + gchar** typesXX = gdk_pixbuf_format_get_mime_types(one); + for ( i = 0; typesXX[i]; i++ ) { + types.push_back(g_strdup(typesXX[i])); + } + g_strfreev(typesXX); + + list = g_slist_next(list); + } + completeDropTargetsCount = nui_drop_target_entries + types.size(); + completeDropTargets = new GtkTargetEntry[completeDropTargetsCount]; + for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) { + completeDropTargets[i] = ui_drop_target_entries[i]; + } + int pos = nui_drop_target_entries; + + for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) { + completeDropTargets[pos].target = *it; + completeDropTargets[pos].flags = 0; + completeDropTargets[pos].info = IMAGE_DATA; + pos++; + } + } + + gtk_drag_dest_set(w, + GTK_DEST_DEFAULT_ALL, + completeDropTargets, + completeDropTargetsCount, + GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE)); + g_signal_connect(G_OBJECT(w), + "drag_data_received", + G_CALLBACK(sp_ui_drag_data_received), + NULL); + gtk_widget_show(w); + + // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet + inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop); +} + +void +sp_ui_new_view() +{ + SPDocument *document; + SPViewWidget *dtw; + + document = SP_ACTIVE_DOCUMENT; + if (!document) return; + + dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL)); + g_return_if_fail(dtw != NULL); + + sp_create_window(dtw, TRUE); + sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view)); +} + +/* TODO: not yet working */ +/* To be re-enabled (by adding to menu) once it works. */ +void +sp_ui_new_view_preview() +{ + SPDocument *document; + SPViewWidget *dtw; + + document = SP_ACTIVE_DOCUMENT; + if (!document) return; + + dtw = (SPViewWidget *) sp_svg_view_widget_new(document); + g_return_if_fail(dtw != NULL); + sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0); + + sp_create_window(dtw, FALSE); +} + +/** + * \param widget unused + */ +void +sp_ui_close_view(GtkWidget *widget) +{ + if (SP_ACTIVE_DESKTOP == NULL) { + return; + } + if ((SP_ACTIVE_DESKTOP)->shutdown()) { + return; + } + SP_ACTIVE_DESKTOP->destroyWidget(); +} + + +/** + * sp_ui_close_all + * + * This function is called to exit the program, and iterates through all + * open document view windows, attempting to close each in turn. If the + * view has unsaved information, the user will be prompted to save, + * discard, or cancel. + * + * Returns FALSE if the user cancels the close_all operation, TRUE + * otherwise. + */ +unsigned int +sp_ui_close_all(void) +{ + /* Iterate through all the windows, destroying each in the order they + become active */ + while (SP_ACTIVE_DESKTOP) { + if ((SP_ACTIVE_DESKTOP)->shutdown()) { + /* The user cancelled the operation, so end doing the close */ + return FALSE; + } + SP_ACTIVE_DESKTOP->destroyWidget(); + } + + return TRUE; +} + +static gint +sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view) +{ + return view->shutdown(); +} + +/* + * Some day when the right-click menus are ready to start working + * smarter with the verbs, we'll need to change this NULL being + * sent to sp_action_perform to something useful, or set some kind + * of global "right-clicked position" variable for actions to + * investigate when they're called. + */ +static void +sp_ui_menu_activate(void *object, SPAction *action) +{ + sp_action_perform(action, NULL); +} + +static void +sp_ui_menu_select_action(void *object, SPAction *action) +{ + action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip); +} + +static void +sp_ui_menu_deselect_action(void *object, SPAction *action) +{ + action->view->tipsMessageContext()->clear(); +} + +static void +sp_ui_menu_select(gpointer object, gpointer tip) +{ + Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view")); + view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip); +} + +static void +sp_ui_menu_deselect(gpointer object) +{ + Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view")); + view->tipsMessageContext()->clear(); +} + +/** + * sp_ui_menuitem_add_icon + * + * Creates and attaches a scaled icon to the given menu item. + * + */ +void +sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name ) +{ + GtkWidget *icon; + + icon = sp_icon_new( GTK_ICON_SIZE_MENU, icon_name ); + gtk_widget_show(icon); + gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon); +} // end of sp_ui_menu_add_icon + +/** + * sp_ui_menu_append_item + * + * Appends a UI item with specific info for Inkscape/Sodipodi. + * + */ +static GtkWidget * +sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock, + gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback, + gpointer data, gboolean with_mnemonic = TRUE ) +{ + GtkWidget *item; + + if (stock) { + item = gtk_image_menu_item_new_from_stock(stock, NULL); + } else if (label) { + item = (with_mnemonic) + ? gtk_image_menu_item_new_with_mnemonic(label) : + gtk_image_menu_item_new_with_label(label); + } else { + item = gtk_separator_menu_item_new(); + } + + gtk_widget_show(item); + + if (callback) { + g_signal_connect(G_OBJECT(item), "activate", callback, data); + } + + if (tip && view) { + g_object_set_data(G_OBJECT(item), "view", (gpointer) view); + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL); + } + + gtk_menu_append(GTK_MENU(menu), item); + + return item; + +} // end of sp_ui_menu_append_item() + +/** +\brief a wrapper around gdk_keyval_name producing (when possible) characters, not names + */ +static gchar const * +sp_key_name(guint keyval) +{ + /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or + simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */ + gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval)); + + if (!strcmp(n, "asciicircum")) return "^"; + else if (!strcmp(n, "parenleft" )) return "("; + else if (!strcmp(n, "parenright" )) return ")"; + else if (!strcmp(n, "plus" )) return "+"; + else if (!strcmp(n, "minus" )) return "-"; + else if (!strcmp(n, "asterisk" )) return "*"; + else if (!strcmp(n, "KP_Multiply")) return "*"; + else if (!strcmp(n, "Delete" )) return "Del"; + else if (!strcmp(n, "Page_Up" )) return "PgUp"; + else if (!strcmp(n, "Page_Down" )) return "PgDn"; + else if (!strcmp(n, "grave" )) return "`"; + else if (!strcmp(n, "numbersign" )) return "#"; + else if (!strcmp(n, "bar" )) return "|"; + else if (!strcmp(n, "slash" )) return "/"; + else if (!strcmp(n, "exclam" )) return "!"; + else return n; +} + + +/** + * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values. + * \param c Points to a buffer at least 256 bytes long. + */ +void +sp_ui_shortcut_string(unsigned const shortcut, gchar *const c) +{ + /* TODO: This function shouldn't exist. Our callers should use GtkAccelLabel instead of + * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator. + * Will probably need to change sp_shortcut_invoke callers. + * + * The existing gtk_label_new_with_mnemonic call can be replaced with + * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by + * gtk_label_set_text_with_mnemonic(lbl, str). + */ + static GtkAccelLabelClass const &accel_lbl_cls + = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL); + + struct { unsigned test; char const *name; } const modifier_tbl[] = { + { SP_SHORTCUT_SHIFT_MASK, accel_lbl_cls.mod_name_shift }, + { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control }, + { SP_SHORTCUT_ALT_MASK, accel_lbl_cls.mod_name_alt } + }; + + gchar *p = c; + gchar *end = p + 256; + + for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) { + if ((shortcut & modifier_tbl[i].test) + && (p < end)) + { + p += g_snprintf(p, end - p, "%s%s", + modifier_tbl[i].name, + accel_lbl_cls.mod_separator); + } + } + if (p < end) { + p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff)); + } + end[-1] = '\0'; // snprintf doesn't guarantee to nul-terminate the string. +} + +void +sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c) +{ + SPAction *action; + unsigned int shortcut; + gchar *s; + gchar key[256]; + gchar *atitle; + + action = verb->get_action(NULL); + if (!action) + return; + + atitle = sp_action_get_title(action); + + s = g_stpcpy(c, atitle); + + g_free(atitle); + + shortcut = sp_shortcut_get_primary(verb); + if (shortcut) { + s = g_stpcpy(s, " ("); + sp_ui_shortcut_string(shortcut, key); + s = g_stpcpy(s, key); + s = g_stpcpy(s, ")"); + } +} + + +/** + * sp_ui_menu_append_item_from_verb + * + * Appends a custom menu UI from a verb. + * + */ + +static GtkWidget * +sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL) +{ + SPAction *action; + GtkWidget *item; + + if (verb->get_code() == SP_VERB_NONE) { + + item = gtk_separator_menu_item_new(); + + } else { + unsigned int shortcut; + + action = verb->get_action(view); + + if (!action) return NULL; + + shortcut = sp_shortcut_get_primary(verb); + if (shortcut) { + gchar c[256]; + sp_ui_shortcut_string(shortcut, c); + GtkWidget *const hb = gtk_hbox_new(FALSE, 16); + GtkWidget *const name_lbl = gtk_label_new(""); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name); + gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5); + gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0); + GtkWidget *const accel_lbl = gtk_label_new(c); + gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5); + gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0); + gtk_widget_show_all(hb); + if (radio) { + item = gtk_radio_menu_item_new (group); + } else { + item = gtk_image_menu_item_new(); + } + gtk_container_add((GtkContainer *) item, hb); + } else { + if (radio) { + item = gtk_radio_menu_item_new (group); + } else { + item = gtk_image_menu_item_new (); + } + GtkWidget *const name_lbl = gtk_label_new(""); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name); + gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5); + gtk_container_add((GtkContainer *) item, name_lbl); + } + + nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item); + if (!action->sensitive) { + gtk_widget_set_sensitive(item, FALSE); + } + + if (action->image) { + sp_ui_menuitem_add_icon(item, action->image); + } + gtk_widget_set_events(item, GDK_KEY_PRESS_MASK); + g_signal_connect( G_OBJECT(item), "activate", + G_CALLBACK(sp_ui_menu_activate), action ); + + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action ); + } + + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + + return item; + +} // end of sp_ui_menu_append_item_from_verb + + +static void +checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data) +{ + gchar const *pref = (gchar const *) user_data; + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + + gchar const *pref_path; + if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen) + pref_path = g_strconcat("fullscreen.", pref, NULL); + else + pref_path = g_strconcat("window.", pref, NULL); + + gboolean checked = gtk_check_menu_item_get_active(menuitem); + prefs_set_int_attribute(pref_path, "state", checked); + + reinterpret_cast<SPDesktop*>(view)->layoutWidget(); +} + +static gboolean +checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget); + + gchar const *pref = (gchar const *) user_data; + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + + gchar const *pref_path; + if (static_cast<SPDesktop*>(view)->is_fullscreen) + pref_path = g_strconcat("fullscreen.", pref, NULL); + else + pref_path = g_strconcat("window.", pref, NULL); + + gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1); + + g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data); + gtk_check_menu_item_set_active(menuitem, ison); + g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data); + + return FALSE; +} + + +void +sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref, + void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data), + gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data), + Inkscape::Verb *verb) +{ + GtkWidget *item; + + unsigned int shortcut = 0; + SPAction *action = NULL; + + if (verb) { + shortcut = sp_shortcut_get_primary(verb); + action = verb->get_action(view); + } + + if (verb && shortcut) { + gchar c[256]; + sp_ui_shortcut_string(shortcut, c); + + GtkWidget *hb = gtk_hbox_new(FALSE, 16); + + { + GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label); + gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5); + gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0); + } + + { + GtkWidget *l = gtk_label_new(c); + gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5); + gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0); + } + + gtk_widget_show_all(hb); + + item = gtk_check_menu_item_new(); + gtk_container_add((GtkContainer *) item, hb); + } else { + GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label); + gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5); + item = gtk_check_menu_item_new(); + gtk_container_add((GtkContainer *) item, l); + } +#if 0 + nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item); + if (!action->sensitive) { + gtk_widget_set_sensitive(item, FALSE); + } +#endif + gtk_widget_show(item); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + g_object_set_data(G_OBJECT(item), "view", (gpointer) view); + + g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref); + g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref); + + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip)); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL); +} + +static void +sp_recent_open(GtkWidget *widget, gchar const *uri) +{ + sp_file_open(uri, NULL); +} + +static void +sp_file_new_from_template(GtkWidget *widget, gchar const *uri) +{ + sp_file_new(uri); +} + +void +sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view) +{ + std::list<gchar *> sources; + sources.push_back( profile_path("templates") ); // first try user's local dir + sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir + + // Use this loop to iterate through a list of possible document locations. + while (!sources.empty()) { + gchar *dirname = sources.front(); + + if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) { + GError *err = 0; + GDir *dir = g_dir_open(dirname, 0, &err); + + if (dir) { + for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) { + if (!g_str_has_suffix(file, ".svg")) + continue; // skip non-svg files + + gchar *basename = g_path_get_basename(file); + if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default.")) + continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already + + gchar const *filepath = g_build_filename(dirname, file, NULL); + gchar *dupfile = g_strndup(file, strlen(file) - 4); + gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL); + g_free(dupfile); + GtkWidget *item = gtk_menu_item_new_with_label(filename); + g_free(filename); + + gtk_widget_show(item); + // how does "filepath" ever get freed? + g_signal_connect(G_OBJECT(item), + "activate", + G_CALLBACK(sp_file_new_from_template), + (gpointer) filepath); + + if (view) { + // set null tip for now; later use a description from the template file + g_object_set_data(G_OBJECT(item), "view", (gpointer) view); + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL); + } + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + g_dir_close(dir); + } + } + + // toss the dirname + g_free(dirname); + sources.pop_front(); + } +} + +void +sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */) +{ + gchar const **recent = prefs_get_recent_files(); + if (recent) { + int i; + + for (i = 0; recent[i] != NULL; i += 2) { + gchar const *uri = recent[i]; + gchar const *name = recent[i + 1]; + + GtkWidget *item = gtk_menu_item_new_with_label(name); + gtk_widget_show(item); + g_signal_connect(G_OBJECT(item), + "activate", + G_CALLBACK(sp_recent_open), + (gpointer)uri); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + + g_free(recent); + } else { + GtkWidget *item = gtk_menu_item_new_with_label(_("None")); + gtk_widget_show(item); + gtk_widget_set_sensitive(item, FALSE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } +} + +void +sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) +{ + //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu", + // checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands", + checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls"), _("Show or hide the Tool Controls panel"), "toppanel", + checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox", + checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS)); + sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars", + checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS)); + sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar", + checkitem_toggled, checkitem_update, 0); + sp_ui_menu_append_check_item_from_verb(m, view, _("_Panels"), _("Show or hide the panels"), "panels", + checkitem_toggled, checkitem_update, 0); +} + +/** \brief This function turns XML into a menu + \param menus This is the XML that defines the menu + \param menu Menu to be added to + \param view The View that this menu is being built for + + This function is realitively simple as it just goes through the XML + and parses the individual elements. In the case of a submenu, it + just calls itself recursively. Because it is only reasonable to have + a couple of submenus, it is unlikely this will go more than two or + three times. + + In the case of an unreconginzed verb, a menu item is made to identify + the verb that is missing, and display that. The menu item is also made + insensitive. +*/ +void +sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view) +{ + if (menus == NULL) return; + if (menu == NULL) return; + GSList *group = NULL; + + for (Inkscape::XML::Node *menu_pntr = menus; + menu_pntr != NULL; + menu_pntr = menu_pntr->next()) { + if (!strcmp(menu_pntr->name(), "submenu")) { + if (!strcmp(menu_pntr->attribute("name"), "Effects") && !prefs_get_int_attribute("extensions", "show-effects-menu", 0)) { + continue; + } + GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name"))); + GtkWidget *submenu = gtk_menu_new(); + sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem); + continue; + } + if (!strcmp(menu_pntr->name(), "verb")) { + gchar const *verb_name = menu_pntr->attribute("verb-id"); + Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name); + + if (verb != NULL) { + if (menu_pntr->attribute("radio") != NULL) { + GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group); + group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item)); + if (menu_pntr->attribute("default") != NULL) { + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE); + } + } else { + sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view); + group = NULL; + } + } else { + gchar string[120]; + g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name); + string[119] = '\0'; /* may not be terminated */ + GtkWidget *item = gtk_menu_item_new_with_label(string); + gtk_widget_set_sensitive(item, false); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + } + continue; + } + if (!strcmp(menu_pntr->name(), "separator") + // This was spelt wrong in the original version + // and so this is for backward compatibility. It can + // probably be dropped after the 0.44 release. + || !strcmp(menu_pntr->name(), "seperator")) { + GtkWidget *item = gtk_separator_menu_item_new(); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + continue; + } + if (!strcmp(menu_pntr->name(), "template-list")) { + sp_menu_append_new_templates(menu, view); + continue; + } + if (!strcmp(menu_pntr->name(), "recent-file-list")) { + sp_menu_append_recent_documents(menu, view); + continue; + } + if (!strcmp(menu_pntr->name(), "objects-checkboxes")) { + sp_ui_checkboxes_menus(GTK_MENU(menu), view); + continue; + } + } +} + +/** \brief Build the main tool bar + \param view View to build the bar for + + Currently the main tool bar is built as a dynamic XML menu using + \c sp_ui_build_dyn_menus. This function builds the bar, and then + pass it to get items attached to it. +*/ +GtkWidget * +sp_ui_main_menubar(Inkscape::UI::View::View *view) +{ + GtkWidget *mbar = gtk_menu_bar_new(); + + sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view); + + return mbar; +} + +static void leave_group(GtkMenuItem *, SPDesktop *desktop) { + desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer())); +} + +static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) { + desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group"))); + SP_DT_SELECTION(desktop)->clear(); +} + +GtkWidget * +sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item) +{ + GtkWidget *m; + SPDesktop *dt; + + dt = static_cast<SPDesktop*>(view); + + m = gtk_menu_new(); + + /* Undo and Redo */ + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view); + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view); + + /* Separator */ + sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL); + + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view); + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view); + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view); + + /* Separator */ + sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL); + + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view); + sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view); + + /* Item menu */ + if (item) { + sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL); + sp_object_menu((SPObject *) item, dt, GTK_MENU(m)); + } + + /* layer menu */ + SPGroup *group=NULL; + if (item) { + if (SP_IS_GROUP(item)) { + group = SP_GROUP(item); + } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) { + group = SP_GROUP(SP_OBJECT_PARENT(item)); + } + } + + if (( group && group != dt->currentLayer() ) || + ( dt->currentLayer() != dt->currentRoot() ) ) { + sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL); + } + + if ( group && group != dt->currentLayer() ) { + /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */ + gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group)); + GtkWidget *w = gtk_menu_item_new_with_label(label); + g_free(label); + g_object_set_data(G_OBJECT(w), "group", group); + g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + } + + if ( dt->currentLayer() != dt->currentRoot() ) { + if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) { + GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent")); + g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + + } + } + + return m; +} + +/* Drag and Drop */ +void +sp_ui_drag_data_received(GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint event_time, + gpointer user_data) +{ + switch (info) { + case SVG_DATA: + case SVG_XML_DATA: { + gchar *svgdata = (gchar *)data->data; + + SPDocument *doc = SP_ACTIVE_DOCUMENT; + + Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI); + + if (rnewdoc == NULL) { + sp_ui_error_dialog(_("Could not parse SVG data")); + return; + } + + Inkscape::XML::Node *repr = sp_repr_document_root(rnewdoc); + gchar const *style = repr->attribute("style"); + + Inkscape::XML::Node *newgroup = sp_repr_new("svg:g"); + newgroup->setAttribute("style", style); + + for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) { + Inkscape::XML::Node *newchild = child->duplicate(); + newgroup->appendChild(newchild); + } + + Inkscape::GC::release(rnewdoc); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + // Add it to the current layer + + // Greg's edits to add intelligent positioning of svg drops + SPObject *new_obj = NULL; + new_obj = desktop->currentLayer()->appendChildRepr(newgroup); + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + selection->set(SP_ITEM(new_obj)); + // To move the imported object, we must temporarily set the "transform pattern with + // object" option. + { + int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1); + prefs_set_int_attribute("options.transform", "pattern", 1); + sp_document_ensure_up_to_date(SP_DT_DOCUMENT(desktop)); + NR::Point m( desktop->point() - selection->bounds().midpoint() ); + sp_selection_move_relative(selection, m); + prefs_set_int_attribute("options.transform", "pattern", saved_pref); + } + + Inkscape::GC::release(newgroup); + sp_document_done(doc); + break; + } + + case URI_LIST: { + gchar *uri = (gchar *)data->data; + sp_ui_import_files(uri); + break; + } + + case PNG_DATA: + case JPEG_DATA: + case IMAGE_DATA: { + char tmp[1024]; + + StringOutputStream outs; + Base64OutputStream b64out(outs); + b64out.setColumnWidth(0); + + SPDocument *doc = SP_ACTIVE_DOCUMENT; + + Inkscape::XML::Node *newImage = sp_repr_new("svg:image"); + + for ( int i = 0; i < data->length; i++ ) { + b64out.put( data->data[i] ); + } + b64out.close(); + + + Glib::ustring str = outs.getString(); + + snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) ); + str.insert( 0, tmp ); + newImage->setAttribute("xlink:href", str.c_str()); + + GError *error = NULL; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error ); + if ( loader ) { + error = NULL; + if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) { + GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader); + if ( pbuf ) { + int width = gdk_pixbuf_get_width(pbuf); + int height = gdk_pixbuf_get_height(pbuf); + snprintf( tmp, sizeof(tmp), "%d", width ); + newImage->setAttribute("width", tmp); + + snprintf( tmp, sizeof(tmp), "%d", height ); + newImage->setAttribute("height", tmp); + } + } + } + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + // Add it to the current layer + desktop->currentLayer()->appendChildRepr(newImage); + + Inkscape::GC::release(newImage); + sp_document_done( doc ); + break; + } + } +} + +static void +sp_ui_import_files(gchar *buffer) +{ + GList *list = gnome_uri_list_extract_filenames(buffer); + if (!list) + return; + g_list_foreach(list, sp_ui_import_one_file_with_check, NULL); + g_list_foreach(list, (GFunc) g_free, NULL); + g_list_free(list); +} + +static void +sp_ui_import_one_file_with_check(gpointer filename, gpointer unused) +{ + if (filename) { + if (strlen((char const *)filename) > 2) + sp_ui_import_one_file((char const *)filename); + } +} + +static void +sp_ui_import_one_file(char const *filename) +{ + SPDocument *doc = SP_ACTIVE_DOCUMENT; + if (!doc) return; + + if (filename == NULL) return; + + // Pass off to common implementation + // TODO might need to get the proper type of Inkscape::Extension::Extension + file_import( doc, filename, NULL ); +} + +void +sp_ui_error_dialog(gchar const *message) +{ + GtkWidget *dlg; + gchar *safeMsg = Inkscape::IO::sanitizeString(message); + + dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, safeMsg); + sp_transientize(dlg); + gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE); + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + g_free(safeMsg); +} + +bool +sp_ui_overwrite_file(gchar const *filename) +{ + bool return_value = FALSE; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *boxdata; + gchar *title; + gchar *text; + + if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) { + + title = g_strdup_printf(_("Overwrite %s"), filename); + dialog = gtk_dialog_new_with_buttons(title, + NULL, + (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_NO, + GTK_RESPONSE_NO, + GTK_STOCK_YES, + GTK_RESPONSE_YES, + NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES); + + sp_transientize(dialog); + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + + hbox = gtk_hbox_new(FALSE, 5); + boxdata = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); + gtk_widget_show(boxdata); + gtk_box_pack_start(GTK_BOX(hbox), boxdata, TRUE, TRUE, 5); + text = g_strdup_printf(_("The file %s already exists. Do you want to overwrite that file with the current document?"), filename); + boxdata = gtk_label_new(text); + gtk_label_set_line_wrap(GTK_LABEL(boxdata), TRUE); + gtk_widget_show(boxdata); + gtk_box_pack_start(GTK_BOX(hbox), boxdata, FALSE, FALSE, 5); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 5); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) { + return_value = TRUE; + } else { + return_value = FALSE; + } + + gtk_widget_destroy(dialog); + g_free(title); + g_free(text); + } else { + return_value = TRUE; + } + + return return_value; +} + +static void +sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data) +{ + return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
