X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Finterface.cpp;h=0fd2b65ead4617801f81823cb04f3e033729d4b9;hb=8a2e76b7021b9b960d7c30801a1a14461d9b5939;hp=17248a206bd81f3dfcc894b08ed9f764eef81d28;hpb=469c9ea92e7d1cc3fec6a26cc79ac54c6ff5b3df;p=inkscape.git diff --git a/src/interface.cpp b/src/interface.cpp index 17248a206..0fd2b65ea 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -1,12 +1,12 @@ -#define __SP_INTERFACE_C__ - -/** - * Main UI stuff - * - * Authors: +/** @file + * @brief Main UI stuff + */ +/* Authors: * Lauris Kaplinski * Frank Felfe * bulia byak + * Jon A. Cruz + * Abhishek Sharma * * Copyright (C) 1999-2005 authors * Copyright (C) 2001-2002 Ximian, Inc. @@ -20,14 +20,16 @@ #endif #include +#include + #include "inkscape-private.h" +#include "extension/db.h" #include "extension/effect.h" +#include "extension/input.h" #include "widgets/icon.h" -#include "prefs-utils.h" +#include "preferences.h" #include "path-prefix.h" - #include "shortcuts.h" - #include "document.h" #include "desktop-handles.h" #include "file.h" @@ -39,19 +41,19 @@ #include "svg-view-widget.h" #include "widgets/desktop-widget.h" #include "sp-item-group.h" +#include "sp-text.h" +#include "sp-gradient-fns.h" +#include "sp-gradient.h" +#include "sp-flowtext.h" #include "sp-namedview.h" - +#include "ui/view/view.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" +#include "ui/uxmanager.h" // Added for color drag-n-drop #if ENABLE_LCMS @@ -62,13 +64,16 @@ #include "svg/svg-color.h" #include "desktop-style.h" #include "style.h" +#include "event-context.h" +#include "gradient-drag.h" +#include "widgets/ege-paint-def.h" +// Include Mac OS X menu synchronization on native OSX build +#ifdef GDK_WINDOWING_QUARTZ +#include "ige-mac-menu.h" +#endif -using Inkscape::IO::StringOutputStream; -using Inkscape::IO::Base64OutputStream; - -/* forward declaration */ -static gint sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view); +using Inkscape::DocumentUndo; /* Drag and Drop */ typedef enum { @@ -79,23 +84,26 @@ typedef enum { JPEG_DATA, IMAGE_DATA, APP_X_INKY_COLOR, - APP_X_COLOR + APP_X_COLOR, + APP_OSWB_COLOR, } 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}, + {(gchar *)"text/uri-list", 0, URI_LIST }, + {(gchar *)"image/svg+xml", 0, SVG_XML_DATA }, + {(gchar *)"image/svg", 0, SVG_DATA }, + {(gchar *)"image/png", 0, PNG_DATA }, + {(gchar *)"image/jpeg", 0, JPEG_DATA }, #if ENABLE_MAGIC_COLORS - {"application/x-inkscape-color", 0, APP_X_INKY_COLOR}, + {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR}, #endif // ENABLE_MAGIC_COLORS - {"application/x-color", 0, APP_X_COLOR} + {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR }, + {(gchar *)"application/x-color", 0, APP_X_COLOR } }; static GtkTargetEntry *completeDropTargets = 0; static int completeDropTargetsCount = 0; +static bool temporarily_block_actions = false; #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0]) static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries); @@ -109,12 +117,26 @@ static void sp_ui_drag_data_received(GtkWidget *widget, guint info, guint event_time, gpointer user_data); +static void sp_ui_drag_motion( GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint event_time, + gpointer user_data ); +static void sp_ui_drag_leave( GtkWidget *widget, + GdkDragContext *drag_context, + guint event_time, + gpointer user_data ); static void sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data); -static void sp_ui_menu_item_set_name(SPAction *action, +static void sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data); +static void sp_recent_open(GtkRecentChooser *, gpointer); + +static void injectRenamedIcons(); SPActionEventVector menu_item_event_vector = { {NULL}, @@ -125,34 +147,78 @@ SPActionEventVector menu_item_event_vector = { sp_ui_menu_item_set_name /* set_name */ }; +static const int MIN_ONSCREEN_DISTANCE = 50; + void sp_create_window(SPViewWidget *vw, gboolean editable) { g_return_if_fail(vw != NULL); g_return_if_fail(SP_IS_VIEW_WIDGET(vw)); - GtkWidget *w = sp_window_new("", TRUE); + Gtk::Window *win = Inkscape::UI::window_new("", TRUE); - if (editable) { - g_object_set_data(G_OBJECT(vw), "window", w); - reinterpret_cast(vw)->window = - static_cast((void*)w); - } + gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw)); + gtk_widget_show(GTK_WIDGET(vw)); if (editable) { - /* fixme: */ - 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); + g_object_set_data(G_OBJECT(vw), "window", win); + + SPDesktopWidget *desktop_widget = reinterpret_cast(vw); + SPDesktop* desktop = desktop_widget->desktop; + + desktop_widget->window = win; + + win->set_data("desktop", desktop); + win->set_data("desktopwidget", desktop_widget); + + win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI)); + win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent)); + win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent)); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint prefs_geometry = + (2==prefs->getInt("/options/savewindowgeometry/value", 0)); + if (prefs_geometry) { + gint pw = prefs->getInt("/desktop/geometry/width", -1); + gint ph = prefs->getInt("/desktop/geometry/height", -1); + gint px = prefs->getInt("/desktop/geometry/x", -1); + gint py = prefs->getInt("/desktop/geometry/y", -1); + gint full = prefs->getBool("/desktop/geometry/fullscreen"); + gint maxed = prefs->getBool("/desktop/geometry/maximized"); + if (pw>0 && ph>0) { + gint w = MIN(gdk_screen_width(), pw); + gint h = MIN(gdk_screen_height(), ph); + gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px); + gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py); + if (w>0 && h>0) { + x = MIN(gdk_screen_width() - w, x); + y = MIN(gdk_screen_height() - h, y); + desktop->setWindowSize(w, h); + } + + // Only restore xy for the first window so subsequent windows don't overlap exactly + // with first. (Maybe rule should be only restore xy if it's different from xy of + // other desktops?) + + // Empirically it seems that active_desktop==this desktop only the first time a + // desktop is created. + SPDesktop *active_desktop = SP_ACTIVE_DESKTOP; + if (active_desktop == desktop || active_desktop==NULL) { + desktop->setWindowPosition(Geom::Point(x, y)); + } + } + if (maxed) { + win->maximize(); + } + if (full) { + win->fullscreen(); + } + } + } else { - gtk_window_set_policy(GTK_WINDOW(w), TRUE, TRUE, TRUE); + gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE); } - gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(vw)); - gtk_widget_show(GTK_WIDGET(vw)); - if ( completeDropTargets == 0 || completeDropTargetsCount == 0 ) { std::vector types; @@ -184,16 +250,26 @@ sp_create_window(SPViewWidget *vw, gboolean editable) } } - gtk_drag_dest_set(w, + gtk_drag_dest_set((GtkWidget*)win->gobj(), GTK_DEST_DEFAULT_ALL, completeDropTargets, completeDropTargetsCount, GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE)); - g_signal_connect(G_OBJECT(w), + + + g_signal_connect(G_OBJECT(win->gobj()), "drag_data_received", G_CALLBACK(sp_ui_drag_data_received), NULL); - gtk_widget_show(w); + g_signal_connect(G_OBJECT(win->gobj()), + "drag_motion", + G_CALLBACK(sp_ui_drag_motion), + NULL); + g_signal_connect(G_OBJECT(win->gobj()), + "drag_leave", + G_CALLBACK(sp_ui_drag_leave), + NULL); + win->show(); // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop); @@ -213,6 +289,7 @@ sp_ui_new_view() sp_create_window(dtw, TRUE); sp_namedview_window_from_document(static_cast(dtw->view)); + sp_namedview_update_layers_from_document(static_cast(dtw->view)); } /* TODO: not yet working */ @@ -237,15 +314,21 @@ sp_ui_new_view_preview() * \param widget unused */ void -sp_ui_close_view(GtkWidget *widget) +sp_ui_close_view(GtkWidget */*widget*/) { - if (SP_ACTIVE_DESKTOP == NULL) { + SPDesktop *dt = SP_ACTIVE_DESKTOP; + + if (dt == NULL) { return; } - if ((SP_ACTIVE_DESKTOP)->shutdown()) { - return; + + if (dt->shutdown()) { + return; // Shutdown operation has been canceled, so do nothing } - SP_ACTIVE_DESKTOP->destroyWidget(); + + // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP, + // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad) + dt->destroyWidget(); } @@ -266,22 +349,19 @@ 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 */ + SPDesktop *dt = SP_ACTIVE_DESKTOP; + if (dt->shutdown()) { + /* The user canceled the operation, so end doing the close */ return FALSE; } - SP_ACTIVE_DESKTOP->destroyWidget(); + // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP, + // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad) + dt->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 @@ -290,19 +370,21 @@ sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view) * investigate when they're called. */ static void -sp_ui_menu_activate(void *object, SPAction *action) +sp_ui_menu_activate(void */*object*/, SPAction *action) { - sp_action_perform(action, NULL); + if (!temporarily_block_actions) { + sp_action_perform(action, NULL); + } } static void -sp_ui_menu_select_action(void *object, SPAction *action) +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) +sp_ui_menu_deselect_action(void */*object*/, SPAction *action) { action->view->tipsMessageContext()->clear(); } @@ -330,6 +412,11 @@ sp_ui_menu_deselect(gpointer object) void sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name ) { + static bool iconsInjected = false; + if ( !iconsInjected ) { + iconsInjected = true; + injectRenamedIcons(); + } GtkWidget *icon; icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name ); @@ -378,84 +465,12 @@ sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock, } // 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); @@ -469,11 +484,12 @@ sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c) g_free(atitle); shortcut = sp_shortcut_get_primary(verb); - if (shortcut) { + if (shortcut!=GDK_VoidSymbol) { + gchar* key = sp_shortcut_get_label(shortcut); s = g_stpcpy(s, " ("); - sp_ui_shortcut_string(shortcut, key); s = g_stpcpy(s, key); s = g_stpcpy(s, ")"); + g_free(key); } } @@ -503,9 +519,8 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape:: if (!action) return NULL; shortcut = sp_shortcut_get_primary(verb); - if (shortcut) { - gchar c[256]; - sp_ui_shortcut_string(shortcut, c); + if (shortcut!=GDK_VoidSymbol) { + gchar* c = sp_shortcut_get_label(shortcut); 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); @@ -521,6 +536,7 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape:: item = gtk_image_menu_item_new(); } gtk_container_add((GtkContainer *) item, hb); + g_free(c); } else { if (radio) { item = gtk_radio_menu_item_new (group); @@ -542,9 +558,8 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape:: 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_object_set_data(G_OBJECT(item), "view", (gpointer) view); + 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 ); } @@ -557,39 +572,52 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape:: } // end of sp_ui_menu_append_item_from_verb +static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ) +{ + Glib::ustring prefPath; + + if (reinterpret_cast(view)->is_focusMode()) { + prefPath = "/focus/"; + } else if (reinterpret_cast(view)->is_fullscreen()) { + prefPath = "/fullscreen/"; + } else { + prefPath = "/window/"; + } + + return prefPath; +} + 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(view)->is_fullscreen) - pref_path = g_strconcat("fullscreen.", pref, NULL); - else - pref_path = g_strconcat("window.", pref, NULL); + Glib::ustring pref_path = getLayoutPrefPath( view ); + pref_path += pref; + pref_path += "/state"; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gboolean checked = gtk_check_menu_item_get_active(menuitem); - prefs_set_int_attribute(pref_path, "state", checked); + prefs->setBool(pref_path, checked); reinterpret_cast(view)->layoutWidget(); } static gboolean -checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +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(view)->is_fullscreen) - pref_path = g_strconcat("fullscreen.", pref, NULL); - else - pref_path = g_strconcat("window.", pref, NULL); + Glib::ustring pref_path = getLayoutPrefPath( view ); + pref_path += pref; + pref_path += "/state"; - gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool ison = prefs->getBool(pref_path, true); g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data); gtk_check_menu_item_set_active(menuitem, ison); @@ -598,6 +626,60 @@ checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) return FALSE; } +static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData) +{ + if ( gtk_check_menu_item_get_active(menuitem) ) { + gint taskNum = GPOINTER_TO_INT(userData); + taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum; + + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + + // note: this will change once more options are in the task set support: + Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast(view), taskNum ); + } +} + + +/** + * \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline) + */ + +static gboolean +update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data) +{ + SPAction *action = (SPAction *) user_data; + g_assert(action->id != NULL); + + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view"); + SPDesktop *dt = static_cast(view); + Inkscape::RenderMode mode = dt->getMode(); + + bool new_state = false; + if (!strcmp(action->id, "ViewModeNormal")) { + new_state = mode == Inkscape::RENDERMODE_NORMAL; + } else if (!strcmp(action->id, "ViewModeNoFilters")) { + new_state = mode == Inkscape::RENDERMODE_NO_FILTERS; + } else if (!strcmp(action->id, "ViewModeOutline")) { + new_state = mode == Inkscape::RENDERMODE_OUTLINE; + } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) { + new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW; + } else { + g_warning("update_view_menu does not handle this verb"); + } + + if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated + if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) { + // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second + // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled. + // This emission however should not invoke any actions, hence we block it here: + temporarily_block_actions = true; + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE); + temporarily_block_actions = false; + } + } + + 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, @@ -605,19 +687,12 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View * gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data), Inkscape::Verb *verb) { - GtkWidget *item; + unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0; + SPAction *action = (verb) ? verb->get_action(view) : 0; + GtkWidget *item = gtk_check_menu_item_new(); - 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); + if (verb && shortcut!=GDK_VoidSymbol) { + gchar* c = sp_shortcut_get_label(shortcut); GtkWidget *hb = gtk_hbox_new(FALSE, 16); @@ -635,12 +710,11 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View * gtk_widget_show_all(hb); - item = gtk_check_menu_item_new(); gtk_container_add((GtkContainer *) item, hb); + g_free(c); } 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 @@ -663,13 +737,20 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View * } static void -sp_recent_open(GtkWidget *widget, gchar const *uri) +sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/) { - sp_file_open(uri, NULL); + // dealing with the bizarre filename convention in Inkscape for now + gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu)); + gchar *local_fn = g_filename_from_uri(uri, NULL, NULL); + gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL); + sp_file_open(utf8_fn, NULL); + g_free(utf8_fn); + g_free(local_fn); + g_free(uri); } static void -sp_file_new_from_template(GtkWidget *widget, gchar const *uri) +sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri) { sp_file_new(uri); } @@ -691,7 +772,7 @@ sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view) 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")) + if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz")) continue; // skip non-svg files gchar *basename = g_path_get_basename(file); @@ -731,35 +812,6 @@ sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view) } } -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) { @@ -767,6 +819,8 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) // 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, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox", + checkitem_toggled, checkitem_update, 0); sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "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", @@ -781,6 +835,55 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) checkitem_toggled, checkitem_update, 0); } + +void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view) +{ + gchar const* data[] = { + _("Default"), _("Default interface setup"), + _("Custom"), _("Set the custom task"), + _("Wide"), _("Setup for widescreen work"), + 0, 0 + }; + + GSList *group = 0; + int count = 0; + gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast(view) ); + for (gchar const **strs = data; strs[0]; strs += 2, count++) + { + GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] ); + group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) ); + if ( count == active ) + { + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE ); + } + + g_object_set_data( G_OBJECT(item), "view", view ); + g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast(taskToggled), GINT_TO_POINTER(count) ); + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast(strs[1]) ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 ); + + gtk_widget_show( item ); + gtk_menu_shell_append( GTK_MENU_SHELL(menu), item ); + } +} + + +/** @brief Observer that updates the recent list's max document count */ +class MaxRecentObserver : public Inkscape::Preferences::Observer { +public: + MaxRecentObserver(GtkWidget *recent_menu) : + Observer("/options/maxrecentdocuments/value"), + _rm(recent_menu) + {} + virtual void notify(Inkscape::Preferences::Entry const &e) { + gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt()); + // hack: the recent menu doesn't repopulate after changing the limit, so we force it + g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed"); + } +private: + GtkWidget *_rm; +}; + /** \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 @@ -792,7 +895,7 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) 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 + In the case of an unrecognized verb, a menu item is made to identify the verb that is missing, and display that. The menu item is also made insensitive. */ @@ -825,6 +928,10 @@ sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI: if (menu_pntr->attribute("default") != NULL) { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE); } + if (verb->get_code() != SP_VERB_NONE) { + SPAction *action = verb->get_action(view); + g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action); + } } else { sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view); group = NULL; @@ -855,13 +962,41 @@ sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI: continue; } if (!strcmp(menu_pntr->name(), "recent-file-list")) { - sp_menu_append_recent_documents(menu, view); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + // create recent files menu + int max_recent = prefs->getInt("/options/maxrecentdocuments/value"); + GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default()); + gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent); + // sort most recently used documents first to preserve previous behavior + gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU); + g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL); + + // add filter to only open files added by Inkscape + GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new(); + gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname()); + gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter); + + gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE); + gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE); + + GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent")); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu); + + gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item)); + // this will just sit and update the list's item count + static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu); + prefs->addObserver(*mro); continue; } if (!strcmp(menu_pntr->name(), "objects-checkboxes")) { sp_ui_checkboxes_menus(GTK_MENU(menu), view); continue; } + if (!strcmp(menu_pntr->name(), "task-checkboxes")) { + addTaskMenuItems(GTK_MENU(menu), view); + continue; + } } } @@ -877,9 +1012,17 @@ sp_ui_main_menubar(Inkscape::UI::View::View *view) { GtkWidget *mbar = gtk_menu_bar_new(); +#ifdef GDK_WINDOWING_QUARTZ + ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar)); +#endif + sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view); +#ifdef GDK_WINDOWING_QUARTZ + return NULL; +#else return mbar; +#endif } static void leave_group(GtkMenuItem *, SPDesktop *desktop) { @@ -935,13 +1078,14 @@ sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item) } if (( group && group != dt->currentLayer() ) || - ( dt->currentLayer() != dt->currentRoot() ) ) { + ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) { + /* Separator */ 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. , not a number. */ - gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group)); + gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId()); GtkWidget *w = gtk_menu_item_new_with_label(label); g_free(label); g_object_set_data(G_OBJECT(w), "group", group); @@ -970,29 +1114,33 @@ sp_ui_drag_data_received(GtkWidget *widget, gint x, gint y, GtkSelectionData *data, guint info, - guint event_time, - gpointer user_data) + guint /*event_time*/, + gpointer /*user_data*/) { + SPDocument *doc = SP_ACTIVE_DOCUMENT; + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + switch (info) { #if ENABLE_MAGIC_COLORS case APP_X_INKY_COLOR: { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; int destX = 0; int destY = 0; gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY ); - NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) ); + Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) ); - SPItem *item = desktop->item_at_point( where, true ); + SPItem *item = desktop->getItemAtPoint( where, true ); if ( item ) { + bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE); + if ( data->length >= 8 ) { cmsHPROFILE srgbProf = cmsCreate_sRGBProfile(); gchar c[64] = {0}; // Careful about endian issues. guint16* dataVals = (guint16*)data->data; - sp_svg_write_color( c, 64, + sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( 0x0ff & (dataVals[0] >> 8), 0x0ff & (dataVals[1] >> 8), @@ -1020,26 +1168,25 @@ sp_ui_drag_data_received(GtkWidget *widget, g_free(str); str = 0; - sp_object_setAttribute( SP_OBJECT(item), - (drag_context->action != GDK_ACTION_MOVE) ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag", + SP_OBJECT(item)->setAttribute( + fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag", palName.c_str(), false ); item->updateRepr(); - sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c ); + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c ); updatePerformed = true; } } if ( !updatePerformed ) { - sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c ); + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c ); } sp_desktop_apply_css_recursive( item, css, true ); item->updateRepr(); - SPDocument *doc = SP_ACTIVE_DOCUMENT; - sp_document_done( doc , SP_VERB_NONE, + SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color")); if ( srgbProf ) { @@ -1053,36 +1200,183 @@ sp_ui_drag_data_received(GtkWidget *widget, case APP_X_COLOR: { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; int destX = 0; int destY = 0; gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY ); - NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) ); + Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) ); + Geom::Point const button_dt(desktop->w2d(where)); + Geom::Point const button_doc(desktop->dt2doc(button_dt)); + + if ( data->length == 8 ) { + gchar colorspec[64] = {0}; + // Careful about endian issues. + guint16* dataVals = (guint16*)data->data; + sp_svg_write_color( colorspec, sizeof(colorspec), + SP_RGBA32_U_COMPOSE( + 0x0ff & (dataVals[0] >> 8), + 0x0ff & (dataVals[1] >> 8), + 0x0ff & (dataVals[2] >> 8), + 0xff // can't have transparency in the color itself + //0x0ff & (data->data[3] >> 8), + )); + + SPItem *item = desktop->getItemAtPoint( where, true ); + + bool consumed = false; + if (desktop->event_context && desktop->event_context->get_drag()) { + consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt); + if (consumed) { + DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") ); + desktop->event_context->get_drag()->updateDraggers(); + } + } + + //if (!consumed && tools_active(desktop, TOOLS_TEXT)) { + // consumed = sp_text_context_drop_color(c, button_doc); + // if (consumed) { + // SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient stop")); + // } + //} + + if (!consumed && item) { + bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE); + if (fillnotstroke && + (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) { + Path *livarot_path = Path_for_item(item, true, true); + livarot_path->ConvertWithBackData(0.04); + + boost::optional position = get_nearest_position_on_Path(livarot_path, button_doc); + if (position) { + Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t); + Geom::Point delta = nearest - button_doc; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + delta = desktop->d2w(delta); + double stroke_tolerance = + ( !SP_OBJECT_STYLE(item)->stroke.isNone() ? + desktop->current_zoom() * + SP_OBJECT_STYLE (item)->stroke_width.computed * + to_2geom(item->i2d_affine()).descrim() * 0.5 + : 0.0) + + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + if (Geom::L2 (delta) < stroke_tolerance) { + fillnotstroke = false; + } + } + delete livarot_path; + } - SPItem *item = desktop->item_at_point( where, true ); - if ( item ) - { - if ( data->length == 8 ) { - gchar c[64] = {0}; - // Careful about endian issues. - guint16* dataVals = (guint16*)data->data; - sp_svg_write_color( c, 64, - SP_RGBA32_U_COMPOSE( - 0x0ff & (dataVals[0] >> 8), - 0x0ff & (dataVals[1] >> 8), - 0x0ff & (dataVals[2] >> 8), - 0xff // can't have transparency in the color itself - //0x0ff & (data->data[3] >> 8), - )); SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c ); + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec ); sp_desktop_apply_css_recursive( item, css, true ); item->updateRepr(); - SPDocument *doc = SP_ACTIVE_DOCUMENT; - sp_document_done( doc , SP_VERB_NONE, - _("Drop color")); + DocumentUndo::done( doc , SP_VERB_NONE, + _("Drop color") ); + } + } + } + break; + + case APP_OSWB_COLOR: + { + bool worked = false; + Glib::ustring colorspec; + if ( data->format == 8 ) { + ege::PaintDef color; + worked = color.fromMIMEData("application/x-oswb-color", + reinterpret_cast(data->data), + data->length, + data->format); + if ( worked ) { + if ( color.getType() == ege::PaintDef::CLEAR ) { + colorspec = ""; // TODO check if this is sufficient + } else if ( color.getType() == ege::PaintDef::NONE ) { + colorspec = "none"; + } else { + unsigned int r = color.getR(); + unsigned int g = color.getG(); + unsigned int b = color.getB(); + + SPGradient* matches = 0; + const GSList *gradients = doc->getResourceList("gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( color.descr == grad->getId() ) { + if ( grad->hasStops() ) { + matches = grad; + break; + } + } + } + if (matches) { + colorspec = "url(#"; + colorspec += matches->getId(); + colorspec += ")"; + } else { + gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b); + colorspec = tmp; + g_free(tmp); + } + } + } + } + if ( worked ) { + int destX = 0; + int destY = 0; + gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY ); + Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) ); + Geom::Point const button_dt(desktop->w2d(where)); + Geom::Point const button_doc(desktop->dt2doc(button_dt)); + + SPItem *item = desktop->getItemAtPoint( where, true ); + + bool consumed = false; + if (desktop->event_context && desktop->event_context->get_drag()) { + consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt); + if (consumed) { + DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") ); + desktop->event_context->get_drag()->updateDraggers(); + } + } + + if (!consumed && item) { + bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE); + if (fillnotstroke && + (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) { + Path *livarot_path = Path_for_item(item, true, true); + livarot_path->ConvertWithBackData(0.04); + + boost::optional position = get_nearest_position_on_Path(livarot_path, button_doc); + if (position) { + Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t); + Geom::Point delta = nearest - button_doc; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + delta = desktop->d2w(delta); + double stroke_tolerance = + ( !SP_OBJECT_STYLE(item)->stroke.isNone() ? + desktop->current_zoom() * + SP_OBJECT_STYLE (item)->stroke_width.computed * + to_2geom(item->i2d_affine()).descrim() * 0.5 + : 0.0) + + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + if (Geom::L2 (delta) < stroke_tolerance) { + fillnotstroke = false; + } + } + delete livarot_path; + } + + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() ); + + sp_desktop_apply_css_recursive( item, css, true ); + item->updateRepr(); + + DocumentUndo::done( doc , SP_VERB_NONE, + _("Drop color") ); } } } @@ -1092,8 +1386,6 @@ sp_ui_drag_data_received(GtkWidget *widget, 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) { @@ -1101,20 +1393,20 @@ sp_ui_drag_data_received(GtkWidget *widget, return; } - Inkscape::XML::Node *repr = sp_repr_document_root(rnewdoc); + Inkscape::XML::Node *repr = rnewdoc->root(); gchar const *style = repr->attribute("style"); - Inkscape::XML::Node *newgroup = sp_repr_new("svg:g"); + Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g"); newgroup->setAttribute("style", style); + Inkscape::XML::Document * xml_doc = doc->getReprDoc(); for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) { - Inkscape::XML::Node *newchild = child->duplicate(); + Inkscape::XML::Node *newchild = child->duplicate(xml_doc); 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 @@ -1123,20 +1415,20 @@ sp_ui_drag_data_received(GtkWidget *widget, Inkscape::Selection *selection = sp_desktop_selection(desktop); selection->set(SP_ITEM(new_obj)); - // To move the imported object, we must temporarily set the "transform pattern with - // object" option. + + // move to mouse pointer { - 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_desktop_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); + sp_desktop_document(desktop)->ensureUpToDate(); + Geom::OptRect sel_bbox = selection->bounds(); + if (sel_bbox) { + Geom::Point m( desktop->point() - sel_bbox->midpoint() ); + sp_selection_move_relative(selection, m, false); + } } Inkscape::GC::release(newgroup); - sp_document_done(doc, SP_VERB_NONE, - _("Drop SVG")); + DocumentUndo::done( doc, SP_VERB_NONE, + _("Drop SVG") ); break; } @@ -1149,57 +1441,56 @@ sp_ui_drag_data_received(GtkWidget *widget, case PNG_DATA: case JPEG_DATA: case IMAGE_DATA: { - char tmp[1024]; - - StringOutputStream outs; - Base64OutputStream b64out(outs); - b64out.setColumnWidth(0); + const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png"); - 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] ); + Inkscape::Extension::DB::InputList o; + Inkscape::Extension::db.get_input_list(o); + Inkscape::Extension::DB::InputList::const_iterator i = o.begin(); + while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) { + ++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()); + Inkscape::Extension::Extension *ext = *i; + bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0); + ext->set_param_optiongroup("link", "embed"); + ext->set_gui(false); + + gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL ); + g_file_set_contents(filename, reinterpret_cast(data->data), data->length, NULL); + file_import(doc, filename, ext); + g_free(filename); + + ext->set_param_optiongroup("link", save ? "embed" : "link"); + ext->set_gui(true); + DocumentUndo::done( doc , SP_VERB_NONE, + _("Drop bitmap image") ); + break; + } + } +} - 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); +#include "gradient-context.h" - snprintf( tmp, sizeof(tmp), "%d", height ); - newImage->setAttribute("height", tmp); - } - } - } +void sp_ui_drag_motion( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + gint /*x*/, gint /*y*/, + GtkSelectionData */*data*/, + guint /*info*/, + guint /*event_time*/, + gpointer /*user_data*/) +{ +// SPDocument *doc = SP_ACTIVE_DOCUMENT; +// SPDesktop *desktop = SP_ACTIVE_DESKTOP; - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - // Add it to the current layer - desktop->currentLayer()->appendChildRepr(newImage); +// g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time); +} - Inkscape::GC::release(newImage); - sp_document_done( doc , SP_VERB_NONE, - _("Drop bitmap image")); - break; - } - } +static void sp_ui_drag_leave( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + guint /*event_time*/, + gpointer /*user_data*/ ) +{ +// g_message("drag-n-drop leave at %d", event_time); } static void @@ -1214,7 +1505,7 @@ sp_ui_import_files(gchar *buffer) } static void -sp_ui_import_one_file_with_check(gpointer filename, gpointer unused) +sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/) { if (filename) { if (strlen((char const *)filename) > 2) @@ -1242,7 +1533,7 @@ sp_ui_error_dialog(gchar const *message) gchar *safeMsg = Inkscape::IO::sanitizeString(message); dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, safeMsg); + GTK_BUTTONS_CLOSE, "%s", safeMsg); sp_transientize(dlg); gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE); gtk_dialog_run(GTK_DIALOG(dlg)); @@ -1254,52 +1545,34 @@ 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); - - // TODO - replace with Inkscape-specific call - 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) { + Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel(); + gchar* baseName = g_path_get_basename( filename ); + gchar* dirName = g_path_get_dirname( filename ); + GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(), + (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _( "A file named \"%s\" already exists. Do you want to replace it?\n\n" + "The file already exists in \"%s\". Replacing it will overwrite its contents." ), + baseName, + dirName + ); + gtk_dialog_add_buttons( GTK_DIALOG(dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_NO, + _("Replace"), GTK_RESPONSE_YES, + NULL ); + gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES ); + + 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); + g_free( baseName ); + g_free( dirName ); } else { return_value = TRUE; } @@ -1308,17 +1581,53 @@ sp_ui_overwrite_file(gchar const *filename) } static void -sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data) +sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data) { return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive); } static void -sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data) +sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data) { - gtk_label_set_markup_with_mnemonic( - GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (GTK_BIN (data)->child))->data), + void *child = GTK_BIN (data)->child; + //child is either + //- a GtkHBox, whose first child is a label displaying name if the menu + //item has an accel key + //- a GtkLabel if the menu has no accel key + if (GTK_IS_LABEL(child)) { + gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str()); + } else if (GTK_IS_HBOX(child)) { + gtk_label_set_markup_with_mnemonic( + GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), name.c_str()); + }//else sp_ui_menu_append_item_from_verb has been modified and can set + //a menu item in yet another way... +} + +void injectRenamedIcons() +{ + Glib::RefPtr iconTheme = Gtk::IconTheme::get_default(); + + std::vector< std::pair > renamed; + renamed.push_back(std::make_pair("gtk-file", "document-x-generic")); + renamed.push_back(std::make_pair("gtk-directory", "folder")); + + for ( std::vector< std::pair >::iterator it = renamed.begin(); it < renamed.end(); ++it ) { + bool hasIcon = iconTheme->has_icon(it->first); + bool hasSecondIcon = iconTheme->has_icon(it->second); + + if ( !hasIcon && hasSecondIcon ) { + Glib::ArrayHandle sizes = iconTheme->get_icon_sizes(it->second); + for ( Glib::ArrayHandle::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) { + Glib::RefPtr pb = iconTheme->load_icon( it->second, *it2 ); + if ( pb ) { + // install a private copy of the pixbuf to avoid pinning a theme + Glib::RefPtr pbCopy = pb->copy(); + Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy ); + } + } + } + } } @@ -1331,4 +1640,4 @@ sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :