interface.cpp revision 126aa5b056be3aec870a698c7a7d99df41ce12d8
469N/A/**
469N/A * @file
469N/A * Main UI stuff.
1068N/A */
1068N/A/* Authors:
1068N/A * Lauris Kaplinski <lauris@kaplinski.com>
1068N/A * Frank Felfe <innerspace@iname.com>
1068N/A * bulia byak <buliabyak@users.sf.net>
1068N/A * Jon A. Cruz <jon@joncruz.org>
1068N/A * Abhishek Sharma
1068N/A * Kris De Gussem <Kris.DeGussem@gmail.com>
1068N/A *
1068N/A * Copyright (C) 2012 Kris De Gussem
469N/A * Copyright (C) 2010 authors
919N/A * Copyright (C) 1999-2005 authors
919N/A * Copyright (C) 2004 David Turner
919N/A * Copyright (C) 2001-2002 Ximian, Inc.
919N/A *
919N/A * Released under GNU GPL, read the file 'COPYING' for more information
919N/A */
919N/A
919N/A#ifdef HAVE_CONFIG_H
919N/A# include "config.h"
919N/A#endif
469N/A
919N/A#include "file.h"
919N/A#include <glib.h>
919N/A#include <gtkmm/icontheme.h>
919N/A#include <gtkmm/imagemenuitem.h>
919N/A#include <gtkmm/separatormenuitem.h>
919N/A
919N/A#include "inkscape-private.h"
469N/A#include "extension/db.h"
469N/A#include "extension/effect.h"
1068N/A#include "extension/input.h"
469N/A#include "widgets/icon.h"
469N/A#include "preferences.h"
469N/A#include "path-prefix.h"
469N/A#include "shortcuts.h"
469N/A#include "document.h"
469N/A#include "desktop-handles.h"
469N/A#include "interface.h"
1233N/A#include "desktop.h"
1233N/A#include "selection.h"
1233N/A#include "selection-chemistry.h"
1233N/A#include "svg-view-widget.h"
1233N/A#include "widgets/desktop-widget.h"
469N/A#include "sp-item-group.h"
469N/A#include "sp-text.h"
469N/A#include "sp-gradient-fns.h"
469N/A#include "sp-gradient.h"
469N/A#include "sp-flowtext.h"
469N/A#include "sp-namedview.h"
469N/A#include "ui/view/view.h"
469N/A#include "helper/action.h"
469N/A#include "helper/gnome-utils.h"
469N/A#include "helper/window.h"
469N/A#include "io/sys.h"
469N/A#include "dialogs/dialog-events.h"
469N/A#include "message-context.h"
469N/A#include "ui/uxmanager.h"
469N/A
469N/A#include "display/sp-canvas.h"
469N/A#include "color.h"
469N/A#include "svg/svg-color.h"
469N/A#include "desktop-style.h"
469N/A#include "style.h"
469N/A#include "event-context.h"
469N/A#include "gradient-drag.h"
469N/A#include "widgets/ege-paint-def.h"
469N/A#include "document-undo.h"
469N/A#include "sp-anchor.h"
469N/A#include "sp-clippath.h"
469N/A#include "sp-image.h"
469N/A#include "sp-item.h"
469N/A#include "sp-mask.h"
469N/A// #include "verbs.h"
469N/A#include "message-stack.h"
469N/A// #include "inkscape.h"
469N/A#include "ui/dialog/dialog-manager.h"
1233N/A// #include "../xml/repr.h"
1233N/A
1233N/A#include <gdk/gdkkeysyms.h>
1233N/A#include <gtk/gtk.h>
1233N/A
1233N/A#if !GTK_CHECK_VERSION(2,22,0)
1233N/A#define GDK_KEY_VoidSymbol 0xffffff
469N/A#define GDK_KEY_Up 0xff52
1233N/A#define GDK_KEY_KP_Up 0xff97
1233N/A#define GDK_KEY_Down 0xff54
1233N/A#define GDK_KEY_KP_Down 0xff99
1233N/A#define GDK_KEY_Left 0xff51
1233N/A#define GDK_KEY_KP_Left 0xff96
469N/A#define GDK_KEY_Right 0xff53
469N/A#define GDK_KEY_KP_Right 0xff98
469N/A#define GDK_KEY_Page_Up 0xff55
469N/A#define GDK_KEY_KP_Page_Up 0xff9a
469N/A#define GDK_KEY_Page_Down 0xff56
469N/A#define GDK_KEY_KP_Page_Down 0xff9b
469N/A#define GDK_KEY_Home 0xff50
469N/A#define GDK_KEY_KP_Home 0xff95
469N/A#define GDK_KEY_End 0xff57
469N/A#define GDK_KEY_KP_End 0xff9c
469N/A#define GDK_KEY_a 0x061
469N/A#define GDK_KEY_A 0x041
469N/A#define GDK_KEY_d 0x064
469N/A#define GDK_KEY_D 0x044
469N/A#define GDK_KEY_g 0x067
469N/A#define GDK_KEY_G 0x047
469N/A#define GDK_KEY_l 0x06c
469N/A#define GDK_KEY_L 0x04c
469N/A#define GDK_KEY_q 0x071
469N/A#define GDK_KEY_Q 0x051
469N/A#define GDK_KEY_r 0x072
469N/A#define GDK_KEY_R 0x052
469N/A#define GDK_KEY_s 0x073
469N/A#define GDK_KEY_S 0x053
469N/A#define GDK_KEY_u 0x075
469N/A#define GDK_KEY_U 0x055
469N/A#define GDK_KEY_w 0x077
469N/A#define GDK_KEY_W 0x057
469N/A#define GDK_KEY_x 0x078
469N/A#define GDK_KEY_X 0x058
1233N/A#define GDK_KEY_z 0x07a
469N/A#define GDK_KEY_Z 0x05a
469N/A#define GDK_KEY_Escape 0xff1b
469N/A#define GDK_KEY_Control_L 0xffe3
469N/A#define GDK_KEY_Control_R 0xffe4
469N/A#define GDK_KEY_Alt_L 0xffe9
469N/A#define GDK_KEY_Alt_R 0xffea
469N/A#define GDK_KEY_Shift_L 0xffe1
469N/A#define GDK_KEY_Shift_R 0xffe2
469N/A#define GDK_KEY_Meta_L 0xffe7
469N/A#define GDK_KEY_Meta_R 0xffe8
469N/A#define GDK_KEY_KP_0 0xffb0
469N/A#define GDK_KEY_KP_1 0xffb1
469N/A#define GDK_KEY_KP_2 0xffb2
469N/A#define GDK_KEY_KP_3 0xffb3
469N/A#define GDK_KEY_KP_4 0xffb4
469N/A#define GDK_KEY_KP_5 0xffb5
469N/A#define GDK_KEY_KP_6 0xffb6
1233N/A#define GDK_KEY_KP_7 0xffb7
469N/A#define GDK_KEY_KP_8 0xffb8
469N/A#define GDK_KEY_KP_9 0xffb9
469N/A#define GDK_KEY_F1 0xffbe
469N/A#define GDK_KEY_F2 0xffbf
469N/A#define GDK_KEY_F3 0xffc0
469N/A#define GDK_KEY_F4 0xffc1
469N/A#define GDK_KEY_F5 0xffc2
469N/A#define GDK_KEY_F6 0xffc3
469N/A#define GDK_KEY_F7 0xffc4
469N/A#define GDK_KEY_F8 0xffc5
469N/A#define GDK_KEY_F9 0xffc6
469N/A#define GDK_KEY_F10 0xffc7
469N/A#define GDK_KEY_F11 0xffc8
469N/A#define GDK_KEY_Insert 0xff63
469N/A#define GDK_KEY_KP_Insert 0xff9e
469N/A#define GDK_KEY_Delete 0xffff
469N/A#define GDK_KEY_KP_Delete 0xff9f
469N/A#define GDK_KEY_BackSpace 0xff08
469N/A#define GDK_KEY_Return 0xff0d
469N/A#define GDK_KEY_KP_Enter 0xff8d
469N/A#define GDK_KEY_space 0x020
469N/A#define GDK_KEY_Tab 0xff09
469N/A#define GDK_KEY_ISO_Left_Tab 0xfe20
469N/A#define GDK_KEY_bracketleft 0x05b
469N/A#define GDK_KEY_bracketright 0x05d
469N/A#define GDK_KEY_less 0x03c
469N/A#define GDK_KEY_greater 0x03e
469N/A#define GDK_KEY_comma 0x02c
469N/A#define GDK_KEY_period 0x02e
469N/A#endif
469N/A
469N/Ausing Inkscape::DocumentUndo;
469N/A
469N/A/* Drag and Drop */
469N/Atypedef enum {
469N/A URI_LIST,
469N/A SVG_XML_DATA,
469N/A SVG_DATA,
1233N/A PNG_DATA,
1233N/A JPEG_DATA,
1233N/A IMAGE_DATA,
1233N/A APP_X_INKY_COLOR,
1233N/A APP_X_COLOR,
469N/A APP_OSWB_COLOR,
1233N/A} ui_drop_target_info;
1233N/A
469N/Astatic GtkTargetEntry ui_drop_target_entries [] = {
1233N/A {(gchar *)"text/uri-list", 0, URI_LIST },
469N/A {(gchar *)"image/svg+xml", 0, SVG_XML_DATA },
469N/A {(gchar *)"image/svg", 0, SVG_DATA },
469N/A {(gchar *)"image/png", 0, PNG_DATA },
469N/A {(gchar *)"image/jpeg", 0, JPEG_DATA },
469N/A#if ENABLE_MAGIC_COLORS
469N/A {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
469N/A#endif // ENABLE_MAGIC_COLORS
469N/A {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR },
469N/A {(gchar *)"application/x-color", 0, APP_X_COLOR }
469N/A};
469N/A
469N/Astatic GtkTargetEntry *completeDropTargets = 0;
469N/Astatic int completeDropTargetsCount = 0;
469N/Astatic bool temporarily_block_actions = false;
469N/A
469N/A#define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
469N/Astatic guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
469N/Astatic void sp_ui_import_files(gchar *buffer);
469N/Astatic void sp_ui_import_one_file(char const *filename);
469N/Astatic void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
469N/Astatic void sp_ui_drag_data_received(GtkWidget *widget,
469N/A GdkDragContext *drag_context,
469N/A gint x, gint y,
469N/A GtkSelectionData *data,
469N/A guint info,
469N/A guint event_time,
469N/A gpointer user_data);
469N/Astatic void sp_ui_drag_motion( GtkWidget *widget,
469N/A GdkDragContext *drag_context,
469N/A gint x, gint y,
469N/A GtkSelectionData *data,
469N/A guint info,
469N/A guint event_time,
469N/A gpointer user_data );
469N/Astatic void sp_ui_drag_leave( GtkWidget *widget,
469N/A GdkDragContext *drag_context,
469N/A guint event_time,
469N/A gpointer user_data );
469N/Astatic void sp_ui_menu_item_set_name(GtkWidget *data,
469N/A Glib::ustring const &name);
469N/Astatic void sp_recent_open(GtkRecentChooser *, gpointer);
469N/A
469N/Astatic void injectRenamedIcons();
469N/A
469N/Astatic const int MIN_ONSCREEN_DISTANCE = 50;
469N/A
469N/Avoid
469N/Asp_create_window(SPViewWidget *vw, gboolean editable)
469N/A{
469N/A g_return_if_fail(vw != NULL);
469N/A g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
469N/A
469N/A Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
469N/A
469N/A gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
469N/A gtk_widget_show(GTK_WIDGET(vw));
469N/A
469N/A if (editable) {
469N/A g_object_set_data(G_OBJECT(vw), "window", win);
469N/A
469N/A SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
469N/A SPDesktop* desktop = desktop_widget->desktop;
469N/A
469N/A desktop_widget->window = win;
469N/A
469N/A win->set_data("desktop", desktop);
469N/A win->set_data("desktopwidget", desktop_widget);
469N/A
469N/A win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
469N/A win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
469N/A win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
469N/A
469N/A Inkscape::Preferences *prefs = Inkscape::Preferences::get();
469N/A gint prefs_geometry =
469N/A (2==prefs->getInt("/options/savewindowgeometry/value", 0));
469N/A if (prefs_geometry) {
469N/A gint pw = prefs->getInt("/desktop/geometry/width", -1);
469N/A gint ph = prefs->getInt("/desktop/geometry/height", -1);
469N/A gint px = prefs->getInt("/desktop/geometry/x", -1);
469N/A gint py = prefs->getInt("/desktop/geometry/y", -1);
469N/A gint full = prefs->getBool("/desktop/geometry/fullscreen");
469N/A gint maxed = prefs->getBool("/desktop/geometry/maximized");
469N/A if (pw>0 && ph>0) {
469N/A gint w = MIN(gdk_screen_width(), pw);
469N/A gint h = MIN(gdk_screen_height(), ph);
469N/A gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
469N/A gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
469N/A if (w>0 && h>0) {
469N/A x = MIN(gdk_screen_width() - w, x);
469N/A y = MIN(gdk_screen_height() - h, y);
469N/A desktop->setWindowSize(w, h);
469N/A }
469N/A
469N/A // Only restore xy for the first window so subsequent windows don't overlap exactly
469N/A // with first. (Maybe rule should be only restore xy if it's different from xy of
469N/A // other desktops?)
469N/A
469N/A // Empirically it seems that active_desktop==this desktop only the first time a
469N/A // desktop is created.
469N/A SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
469N/A if (active_desktop == desktop || active_desktop==NULL) {
469N/A desktop->setWindowPosition(Geom::Point(x, y));
469N/A }
469N/A }
469N/A if (maxed) {
469N/A win->maximize();
469N/A }
469N/A if (full) {
469N/A win->fullscreen();
469N/A }
469N/A }
469N/A
469N/A }
469N/A
469N/A if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
469N/A {
469N/A std::vector<gchar*> types;
469N/A
469N/A GSList *list = gdk_pixbuf_get_formats();
469N/A while ( list ) {
469N/A int i = 0;
469N/A GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
469N/A gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
469N/A for ( i = 0; typesXX[i]; i++ ) {
469N/A types.push_back(g_strdup(typesXX[i]));
469N/A }
469N/A g_strfreev(typesXX);
469N/A
469N/A list = g_slist_next(list);
469N/A }
469N/A completeDropTargetsCount = nui_drop_target_entries + types.size();
469N/A completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
469N/A for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
469N/A completeDropTargets[i] = ui_drop_target_entries[i];
469N/A }
469N/A int pos = nui_drop_target_entries;
469N/A
469N/A for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
469N/A completeDropTargets[pos].target = *it;
469N/A completeDropTargets[pos].flags = 0;
469N/A completeDropTargets[pos].info = IMAGE_DATA;
469N/A pos++;
469N/A }
469N/A }
469N/A
469N/A gtk_drag_dest_set((GtkWidget*)win->gobj(),
469N/A GTK_DEST_DEFAULT_ALL,
469N/A completeDropTargets,
469N/A completeDropTargetsCount,
469N/A GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
469N/A
469N/A
469N/A g_signal_connect(G_OBJECT(win->gobj()),
1233N/A "drag_data_received",
469N/A G_CALLBACK(sp_ui_drag_data_received),
469N/A NULL);
469N/A g_signal_connect(G_OBJECT(win->gobj()),
469N/A "drag_motion",
469N/A G_CALLBACK(sp_ui_drag_motion),
469N/A NULL);
469N/A g_signal_connect(G_OBJECT(win->gobj()),
469N/A "drag_leave",
469N/A G_CALLBACK(sp_ui_drag_leave),
469N/A NULL);
469N/A win->show();
469N/A
469N/A // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
469N/A if ( SP_IS_DESKTOP_WIDGET(vw) ) {
469N/A inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
469N/A }
469N/A}
469N/A
469N/Avoid
469N/Asp_ui_new_view()
469N/A{
469N/A SPDocument *document;
469N/A SPViewWidget *dtw;
469N/A
469N/A document = SP_ACTIVE_DOCUMENT;
469N/A if (!document) return;
469N/A
469N/A dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
469N/A g_return_if_fail(dtw != NULL);
1233N/A
469N/A sp_create_window(dtw, TRUE);
469N/A sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
469N/A sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
469N/A}
469N/A
469N/Avoid sp_ui_new_view_preview()
469N/A{
469N/A SPDocument *document = SP_ACTIVE_DOCUMENT;
469N/A if ( document ) {
469N/A SPViewWidget *dtw = reinterpret_cast<SPViewWidget *>(sp_svg_view_widget_new(document));
469N/A g_return_if_fail(dtw != NULL);
469N/A SP_SVG_VIEW_WIDGET(dtw)->setResize(true, 400.0, 400.0);
469N/A
469N/A sp_create_window(dtw, FALSE);
469N/A }
469N/A}
469N/A
469N/Avoid
469N/Asp_ui_close_view(GtkWidget */*widget*/)
469N/A{
469N/A SPDesktop *dt = SP_ACTIVE_DESKTOP;
469N/A
469N/A if (dt == NULL) {
469N/A return;
469N/A }
469N/A
469N/A if (dt->shutdown()) {
469N/A return; // Shutdown operation has been canceled, so do nothing
469N/A }
469N/A
469N/A // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
469N/A // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
469N/A dt->destroyWidget();
469N/A}
469N/A
469N/A
469N/Aunsigned int
469N/Asp_ui_close_all(void)
469N/A{
469N/A /* Iterate through all the windows, destroying each in the order they
469N/A become active */
469N/A while (SP_ACTIVE_DESKTOP) {
469N/A SPDesktop *dt = SP_ACTIVE_DESKTOP;
469N/A if (dt->shutdown()) {
469N/A /* The user canceled the operation, so end doing the close */
469N/A return FALSE;
469N/A }
469N/A // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
469N/A // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
469N/A dt->destroyWidget();
469N/A }
469N/A
469N/A return TRUE;
469N/A}
469N/A
469N/A/*
469N/A * Some day when the right-click menus are ready to start working
469N/A * smarter with the verbs, we'll need to change this NULL being
469N/A * sent to sp_action_perform to something useful, or set some kind
469N/A * of global "right-clicked position" variable for actions to
469N/A * investigate when they're called.
469N/A */
469N/Astatic void
469N/Asp_ui_menu_activate(void */*object*/, SPAction *action)
469N/A{
469N/A if (!temporarily_block_actions) {
469N/A sp_action_perform(action, NULL);
469N/A }
469N/A}
469N/A
469N/Astatic void
1233N/Asp_ui_menu_select_action(void */*object*/, SPAction *action)
469N/A{
469N/A action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
469N/A}
469N/A
469N/Astatic void
469N/Asp_ui_menu_deselect_action(void */*object*/, SPAction *action)
469N/A{
469N/A action->view->tipsMessageContext()->clear();
469N/A}
469N/A
469N/Astatic void
469N/Asp_ui_menu_select(gpointer object, gpointer tip)
469N/A{
469N/A Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
469N/A view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
469N/A}
469N/A
469N/Astatic void
469N/Asp_ui_menu_deselect(gpointer object)
469N/A{
469N/A Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
469N/A view->tipsMessageContext()->clear();
469N/A}
469N/A
469N/A/**
469N/A * Creates and attaches a scaled icon to the given menu item.
469N/A */
469N/Avoid
469N/Asp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
469N/A{
469N/A static bool iconsInjected = false;
469N/A if ( !iconsInjected ) {
469N/A iconsInjected = true;
469N/A injectRenamedIcons();
469N/A }
1233N/A GtkWidget *icon;
469N/A
469N/A icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
469N/A gtk_widget_show(icon);
469N/A gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
469N/A} // end of sp_ui_menu_add_icon
469N/A
469N/Avoid
469N/Asp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
469N/A{
469N/A SPAction *action;
469N/A unsigned int shortcut;
469N/A gchar *s;
469N/A gchar *atitle;
469N/A
469N/A action = verb->get_action(NULL);
469N/A if (!action)
469N/A return;
1233N/A
469N/A atitle = sp_action_get_title(action);
469N/A
469N/A s = g_stpcpy(c, atitle);
469N/A
469N/A g_free(atitle);
469N/A
469N/A shortcut = sp_shortcut_get_primary(verb);
469N/A if (shortcut!=GDK_KEY_VoidSymbol) {
469N/A gchar* key = sp_shortcut_get_label(shortcut);
469N/A s = g_stpcpy(s, " (");
469N/A s = g_stpcpy(s, key);
469N/A s = g_stpcpy(s, ")");
469N/A g_free(key);
469N/A }
469N/A}
1233N/A
469N/A
469N/A/**
469N/A * Appends a custom menu UI from a verb.
469N/A *
469N/A * @see ContextMenu::AppendItemFromVerb for a c++ified alternative. Consider dropping sp_ui_menu_append_item_from_verb when c++ifying interface.cpp.
469N/A */
469N/Astatic GtkWidget *sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
469N/A{
469N/A SPAction *action;
469N/A GtkWidget *item;
469N/A
469N/A if (verb->get_code() == SP_VERB_NONE) {
469N/A
469N/A item = gtk_separator_menu_item_new();
469N/A
469N/A } else {
469N/A unsigned int shortcut;
469N/A
469N/A action = verb->get_action(view);
469N/A if (!action) return NULL;
469N/A
469N/A shortcut = sp_shortcut_get_primary(verb);
469N/A if (shortcut!=GDK_KEY_VoidSymbol) {
469N/A gchar* c = sp_shortcut_get_label(shortcut);
469N/A#if GTK_CHECK_VERSION(3,0,0)
1233N/A GtkWidget *const hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16);
469N/A gtk_box_set_homogeneous(GTK_BOX(hb), FALSE);
469N/A#else
469N/A GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
469N/A#endif
469N/A GtkWidget *const name_lbl = gtk_label_new("");
469N/A gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
469N/A gtk_misc_set_alignment(reinterpret_cast<GtkMisc *>(name_lbl), 0.0, 0.5);
469N/A gtk_box_pack_start(reinterpret_cast<GtkBox *>(hb), name_lbl, TRUE, TRUE, 0);
469N/A GtkWidget *const accel_lbl = gtk_label_new(c);
469N/A gtk_misc_set_alignment(reinterpret_cast<GtkMisc *>(accel_lbl), 1.0, 0.5);
469N/A gtk_box_pack_end(reinterpret_cast<GtkBox *>(hb), accel_lbl, FALSE, FALSE, 0);
469N/A gtk_widget_show_all(hb);
1233N/A if (radio) {
469N/A item = gtk_radio_menu_item_new (group);
469N/A } else {
469N/A item = gtk_image_menu_item_new();
469N/A }
469N/A gtk_container_add(reinterpret_cast<GtkContainer *>(item), hb);
469N/A g_free(c);
469N/A } else {
469N/A if (radio) {
469N/A item = gtk_radio_menu_item_new (group);
469N/A } else {
469N/A item = gtk_image_menu_item_new ();
469N/A }
469N/A GtkWidget *const name_lbl = gtk_label_new("");
469N/A gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
469N/A gtk_misc_set_alignment(reinterpret_cast<GtkMisc *>(name_lbl), 0.0, 0.5);
469N/A gtk_container_add(reinterpret_cast<GtkContainer *>(item), name_lbl);
469N/A }
469N/A
1233N/A action->signal_set_sensitive.connect(
469N/A sigc::bind<0>(
469N/A sigc::ptr_fun(&gtk_widget_set_sensitive),
469N/A item));
469N/A action->signal_set_name.connect(
469N/A sigc::bind<0>(
469N/A sigc::ptr_fun(&sp_ui_menu_item_set_name),
469N/A item));
469N/A
469N/A if (!action->sensitive) {
469N/A gtk_widget_set_sensitive(item, FALSE);
469N/A }
469N/A
469N/A if (action->image) {
469N/A sp_ui_menuitem_add_icon(item, action->image);
469N/A }
469N/A gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
469N/A g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
469N/A g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
469N/A g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
469N/A g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
469N/A }
469N/A
469N/A gtk_widget_show(item);
1233N/A gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
469N/A
469N/A return item;
469N/A
469N/A} // end of sp_ui_menu_append_item_from_verb
469N/A
469N/A
469N/Astatic Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
469N/A{
469N/A Glib::ustring prefPath;
469N/A
469N/A if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
469N/A prefPath = "/focus/";
469N/A } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
469N/A prefPath = "/fullscreen/";
469N/A } else {
469N/A prefPath = "/window/";
469N/A }
469N/A
469N/A return prefPath;
469N/A}
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");
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->setBool(pref_path, 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");
Glib::ustring pref_path = getLayoutPrefPath( view );
pref_path += pref;
pref_path += "/state";
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);
g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, 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<SPDesktop*>(view), taskNum );
}
}
/**
* Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline) and Color display mode.
*/
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<SPDesktop*>(view);
Inkscape::RenderMode mode = dt->getMode();
Inkscape::ColorMode colormode = dt->getColorMode();
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, "ViewColorModeNormal")) {
new_state = colormode == Inkscape::COLORMODE_NORMAL;
} else if (!strcmp(action->id, "ViewColorModeGrayscale")) {
new_state = colormode == Inkscape::COLORMODE_GRAYSCALE;
} else if (!strcmp(action->id, "ViewColorModePrintColorsPreview")) {
new_state = colormode == Inkscape::COLORMODE_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,
void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
Inkscape::Verb *verb)
{
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();
if (verb && shortcut!=GDK_KEY_VoidSymbol) {
gchar* c = sp_shortcut_get_label(shortcut);
#if GTK_CHECK_VERSION(3,0,0)
GtkWidget *hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16);
gtk_box_set_homogeneous(GTK_BOX(hb), FALSE);
#else
GtkWidget *hb = gtk_hbox_new(FALSE, 16);
#endif
{
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);
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);
gtk_container_add((GtkContainer *) item, l);
}
#if 0
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(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
{
// 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(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") && !g_str_has_suffix(file, ".svgz")) {
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.")) {
g_free(basename);
basename = 0;
continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
}
g_free(basename);
basename = 0;
}
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_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, _("Sn_ap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
checkitem_toggled, checkitem_update, 0);
sp_ui_menu_append_check_item_from_verb(m, view, _("T_ool 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",
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, _("_Palette"), _("Show or hide the color palette"), "panels",
checkitem_toggled, checkitem_update, 0);
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);
}
void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
{
gchar const* data[] = {
C_("Interface setup", "Default"), _("Default interface setup"),
C_("Interface setup", "Custom"), _("Set the custom task"),
C_("Interface setup", "Wide"), _("Setup for widescreen work"),
0, 0
};
GSList *group = 0;
int count = 0;
gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(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<GCallback>(taskToggled), GINT_TO_POINTER(count) );
g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(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 );
}
}
/**
* 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;
};
/**
* This function turns XML into a menu.
*
* 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 unrecognized verb, a menu item is made to identify
* the verb that is missing, and display that. The menu item is also made
* insensitive.
*
* @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
*/
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")) {
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);
}
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;
}
} 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_shell_append(GTK_MENU_SHELL(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_shell_append(GTK_MENU_SHELL(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")) {
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_shell_append(GTK_MENU_SHELL(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;
}
}
}
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;
}
/* 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*/)
{
SPDocument *doc = SP_ACTIVE_DOCUMENT;
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
switch (info) {
#if ENABLE_MAGIC_COLORS
case APP_X_INKY_COLOR:
{
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 ) ) );
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, sizeof(c),
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();
bool updatePerformed = false;
if ( data->length > 14 ) {
int flags = dataVals[4];
// piggie-backed palette entry info
int index = dataVals[5];
Glib::ustring palName;
for ( int i = 0; i < dataVals[6]; i++ ) {
palName += (gunichar)dataVals[7+i];
}
// Now hook in a magic tag of some sort.
if ( !palName.empty() && (flags & 1) ) {
gchar* str = g_strdup_printf("%d|", index);
palName.insert( 0, str );
g_free(str);
str = 0;
item->setAttribute(
fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
palName.c_str(),
false );
item->updateRepr();
sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
updatePerformed = true;
}
}
if ( !updatePerformed ) {
sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
}
sp_desktop_apply_css_recursive( item, css, true );
item->updateRepr();
SPDocumentUndo::done( doc , SP_VERB_NONE,
_("Drop color"));
if ( srgbProf ) {
cmsCloseProfile( srgbProf );
}
}
}
}
break;
#endif // ENABLE_MAGIC_COLORS
case APP_X_COLOR:
{
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));
if ( gtk_selection_data_get_length (data) == 8 ) {
gchar colorspec[64] = {0};
// Careful about endian issues.
guint16* dataVals = (guint16*)gtk_selection_data_get_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) {
#if GTK_CHECK_VERSION (2, 22, 0)
bool fillnotstroke = (gdk_drag_context_get_actions (drag_context) != GDK_ACTION_MOVE);
#else
bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
#endif
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<Path::cut_position> 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 =
( !item->style->stroke.isNone() ?
desktop->current_zoom() *
item->style->stroke_width.computed *
item->i2dt_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 );
sp_desktop_apply_css_recursive( item, css, true );
item->updateRepr();
DocumentUndo::done( doc , SP_VERB_NONE,
_("Drop color") );
}
}
}
break;
case APP_OSWB_COLOR:
{
bool worked = false;
Glib::ustring colorspec;
if ( gtk_selection_data_get_format (data) == 8 ) {
ege::PaintDef color;
worked = color.fromMIMEData("application/x-oswb-color",
reinterpret_cast<char const *>(gtk_selection_data_get_data (data)),
gtk_selection_data_get_length (data),
gtk_selection_data_get_format (data));
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) {
#if GTK_CHECK_VERSION (2, 22, 0)
bool fillnotstroke = (gdk_drag_context_get_actions (drag_context) != GDK_ACTION_MOVE);
#else
bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
#endif
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<Path::cut_position> 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 =
( !item->style->stroke.isNone() ?
desktop->current_zoom() *
item->style->stroke_width.computed *
item->i2dt_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") );
}
}
}
break;
case SVG_DATA:
case SVG_XML_DATA: {
gchar *svgdata = (gchar *)gtk_selection_data_get_data (data);
Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, gtk_selection_data_get_length (data), SP_SVG_NS_URI);
if (rnewdoc == NULL) {
sp_ui_error_dialog(_("Could not parse SVG data"));
return;
}
Inkscape::XML::Node *repr = rnewdoc->root();
gchar const *style = repr->attribute("style");
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(xml_doc);
newgroup->appendChild(newchild);
}
Inkscape::GC::release(rnewdoc);
// 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_desktop_selection(desktop);
selection->set(SP_ITEM(new_obj));
// move to mouse pointer
{
sp_desktop_document(desktop)->ensureUpToDate();
Geom::OptRect sel_bbox = selection->visualBounds();
if (sel_bbox) {
Geom::Point m( desktop->point() - sel_bbox->midpoint() );
sp_selection_move_relative(selection, m, false);
}
}
Inkscape::GC::release(newgroup);
DocumentUndo::done( doc, SP_VERB_NONE,
_("Drop SVG") );
break;
}
case URI_LIST: {
gchar *uri = (gchar *)gtk_selection_data_get_data (data);
sp_ui_import_files(uri);
break;
}
case PNG_DATA:
case JPEG_DATA:
case IMAGE_DATA: {
const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
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;
}
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<gchar const *>(gtk_selection_data_get_data (data)),
gtk_selection_data_get_length (data),
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;
}
}
}
#include "gradient-context.h"
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;
// g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
}
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
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, "%s", 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;
if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
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,
_( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\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( baseName );
g_free( dirName );
} else {
return_value = TRUE;
}
return return_value;
}
static void
sp_ui_menu_item_set_name(GtkWidget *data, Glib::ustring const &name)
{
if (data || GTK_IS_BIN (data)) {
void *child = gtk_bin_get_child (GTK_BIN (data));
//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<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
std::vector< std::pair<Glib::ustring, Glib::ustring> > 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<Glib::ustring, Glib::ustring> >::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<int> sizes = iconTheme->get_icon_sizes(it->second);
for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
if ( pb ) {
// install a private copy of the pixbuf to avoid pinning a theme
Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
}
}
}
}
}
ContextMenu::ContextMenu(SPDesktop *desktop, SPItem *item) :
_item(item),
separators(),
MIGroup(),
MIParent(_("Go to parent"))
{
// g_message("ContextMenu");
_object = static_cast<SPObject *>(item);
_desktop = desktop;
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_UNDO));
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_REDO));
AddSeparator();
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_CUT));
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_COPY));
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_PASTE));
AddSeparator();
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE));
AppendItemFromVerb(Inkscape::Verb::get(SP_VERB_EDIT_DELETE));
positionOfLastDialog = 10; // 9 in front + 1 for the separator in the next if; used to position the dialog menu entries below each other
/* Item menu */
if (item!=NULL) {
AddSeparator();
MakeObjectMenu();
}
/* layer menu */
SPGroup *group=NULL;
if (item) {
if (SP_IS_GROUP(item)) {
group = SP_GROUP(item);
} else if ( item != _desktop->currentRoot() && SP_IS_GROUP(item->parent) ) {
group = SP_GROUP(item->parent);
}
}
if (( group && group != _desktop->currentLayer() ) ||
( _desktop->currentLayer() != _desktop->currentRoot() && _desktop->currentLayer()->parent != _desktop->currentRoot() ) ) {
AddSeparator();
}
if ( group && group != _desktop->currentLayer() ) {
/* TRANSLATORS: #%1 is the id of the group e.g. <g id="#g7">, not a number. */
MIGroup.set_label (Glib::ustring::compose(_("Enter group #%1"), group->getId()));
MIGroup.set_data("group", group);
MIGroup.signal_activate().connect(sigc::bind(sigc::mem_fun(*this, &ContextMenu::EnterGroup),&MIGroup));
MIGroup.show();
append(MIGroup);
}
if ( _desktop->currentLayer() != _desktop->currentRoot() ) {
if ( _desktop->currentLayer()->parent != _desktop->currentRoot() ) {
MIParent.signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::LeaveGroup));
MIParent.show();
append(MIParent);
}
}
}
ContextMenu::~ContextMenu(void)
{
// g_message("~ContextMenu");
}
Gtk::SeparatorMenuItem* ContextMenu::AddSeparator(void)
{
Gtk::SeparatorMenuItem* sep = new Gtk::SeparatorMenuItem();
sep->show();
append(*sep);
separators.push_back(sep);
return sep;
}
void ContextMenu::EnterGroup(Gtk::MenuItem* mi)
{
_desktop->setCurrentLayer(reinterpret_cast<SPObject *>(mi->get_data("group")));
_desktop->selection->clear();
}
void ContextMenu::LeaveGroup(void)
{
_desktop->setCurrentLayer(_desktop->currentLayer()->parent);
}
void ContextMenu::AppendItemFromVerb(Inkscape::Verb *verb)//, SPDesktop *view)//, bool radio, GSList *group)
{
SPAction *action;
SPDesktop *view = _desktop;
if (verb->get_code() == SP_VERB_NONE) {
Gtk::MenuItem *item = AddSeparator();
item->show();
append(*item);
} else {
action = verb->get_action(view);
if (!action)
{
return;
}
Gtk::ImageMenuItem *item = NULL;
unsigned int shortcut = sp_shortcut_get_primary(verb);
if (shortcut!=GDK_KEY_VoidSymbol) {
gchar* c = sp_shortcut_get_label(shortcut);
Gtk::HBox *const hb = new Gtk::HBox (FALSE, 16);
Gtk::Label *const name_lbl = new Gtk::Label(action->name, true);
name_lbl->set_alignment(0.0, 0.5);
hb->pack_start(*name_lbl, TRUE, TRUE, 0);
Gtk::Label *const accel_lbl = new Gtk::Label(c);
accel_lbl->set_alignment(1.0, 0.5);
hb->pack_end(*accel_lbl, FALSE, FALSE, 0);
hb->show_all();
// if (radio) {
// item = gtk_radio_menu_item_new (group);
// } else {
item = new Gtk::ImageMenuItem();
// }
item->add(*hb);
g_free(c);
} else {
// if (radio) {
// item = gtk_radio_menu_item_new (group);
// } else {
item = new Gtk::ImageMenuItem();
// }
Gtk::Label *const name_lbl = new Gtk::Label(action->name, true);
name_lbl->set_alignment(0.0, 0.5);
item->add(*name_lbl);
}
action->signal_set_sensitive.connect(sigc::mem_fun(*this, &ContextMenu::set_sensitive));
action->signal_set_name.connect(sigc::mem_fun(*item, &ContextMenu::set_name));
if (!action->sensitive) {
item->set_sensitive(FALSE);
}
if (action->image) {
sp_ui_menuitem_add_icon((GtkWidget*)item->gobj(), action->image);
}
item->set_events(Gdk::KEY_PRESS_MASK);
item->signal_activate().connect(sigc::bind(sigc::ptr_fun(sp_ui_menu_activate),item,action));
item->signal_select().connect(sigc::bind(sigc::ptr_fun(sp_ui_menu_select_action),item,action));
item->signal_deselect().connect(sigc::bind(sigc::ptr_fun(sp_ui_menu_deselect_action),item,action));
item->show();
append(*item);
}
}
void ContextMenu::MakeObjectMenu(void)
{
GObjectClass *klass = G_OBJECT_GET_CLASS(_object); //to deduce the object's type from its class
if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_ITEM))
{
MakeItemMenu ();
}
if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_GROUP))
{
MakeGroupMenu();
}
if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_ANCHOR))
{
MakeAnchorMenu();
}
if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_IMAGE))
{
MakeImageMenu();
}
if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_SHAPE))
{
MakeShapeMenu();
}
if (G_TYPE_CHECK_CLASS_TYPE(klass, SP_TYPE_TEXT))
{
MakeTextMenu();
}
}
void ContextMenu::MakeItemMenu (void)
{
Gtk::MenuItem* mi;
/* Item dialog */
mi = new Gtk::MenuItem(_("_Object Properties..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemProperties));
mi->show();
append(*mi);//insert(*mi,positionOfLastDialog++);
AddSeparator();
/* Select item */
mi = new Gtk::MenuItem(_("_Select This"),1);
if (_desktop->selection->includes(_item)) {
mi->set_sensitive(FALSE);
} else {
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemSelectThis));
}
mi->show();
append(*mi);
/* Select same fill and stroke */
mi = new Gtk::MenuItem(_("_Select Same Fill and Stroke"),1);
if (_desktop->selection->isEmpty()) {
mi->set_sensitive(FALSE);
} else {
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SelectSameFillStroke));
}
mi->set_sensitive(!SP_IS_ANCHOR(_item));
mi->show();
append(*mi);
/* Create link */
mi = new Gtk::MenuItem(_("_Create Link"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ItemCreateLink));
mi->set_sensitive(!SP_IS_ANCHOR(_item));
mi->show();
append(*mi);
bool ClipRefOK=false;
bool MaskRefOK=false;
if (_item){
if (_item->clip_ref){
if (_item->clip_ref->getObject()){
ClipRefOK=true;
}
}
}
if (_item){
if (_item->mask_ref){
if (_item->mask_ref->getObject()){
MaskRefOK=true;
}
}
}
/* Set mask */
mi = new Gtk::MenuItem(_("Set Mask"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SetMask));
if (ClipRefOK || MaskRefOK) {
mi->set_sensitive(FALSE);
} else {
mi->set_sensitive(TRUE);
}
mi->show();
append(*mi);
/* Release mask */
mi = new Gtk::MenuItem(_("Release Mask"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ReleaseMask));
if (MaskRefOK) {
mi->set_sensitive(TRUE);
} else {
mi->set_sensitive(FALSE);
}
mi->show();
append(*mi);
/* Set Clip */
mi = new Gtk::MenuItem(_("Set _Clip"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SetClip));
if (ClipRefOK || MaskRefOK) {
mi->set_sensitive(FALSE);
} else {
mi->set_sensitive(TRUE);
}
mi->show();
append(*mi);
/* Release Clip */
mi = new Gtk::MenuItem(_("Release C_lip"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ReleaseClip));
if (ClipRefOK) {
mi->set_sensitive(TRUE);
} else {
mi->set_sensitive(FALSE);
}
mi->show();
append(*mi);
}
void ContextMenu::SelectSameFillStroke(void)
{
sp_select_same_fill_stroke_style(_desktop, true, true, true);
}
void ContextMenu::ItemProperties(void)
{
_desktop->selection->set(_item);
_desktop->_dlg_mgr->showDialog("ObjectProperties");
}
void ContextMenu::ItemSelectThis(void)
{
_desktop->selection->set(_item);
}
void ContextMenu::ItemCreateLink(void)
{
Inkscape::XML::Document *xml_doc = _desktop->doc()->getReprDoc();
Inkscape::XML::Node *repr = xml_doc->createElement("svg:a");
_item->parent->getRepr()->addChild(repr, _item->getRepr());
SPObject *object = _item->document->getObjectByRepr(repr);
g_return_if_fail(SP_IS_ANCHOR(object));
const char *id = _item->getRepr()->attribute("id");
Inkscape::XML::Node *child = _item->getRepr()->duplicate(xml_doc);
_item->deleteObject(false);
repr->addChild(child, NULL);
child->setAttribute("id", id);
Inkscape::GC::release(repr);
Inkscape::GC::release(child);
DocumentUndo::done(object->document, SP_VERB_NONE, _("Create link"));
_desktop->selection->set(SP_ITEM(object));
_desktop->_dlg_mgr->showDialog("ObjectAttributes");
}
void ContextMenu::SetMask(void)
{
sp_selection_set_mask(_desktop, false, false);
}
void ContextMenu::ReleaseMask(void)
{
sp_selection_unset_mask(_desktop, false);
}
void ContextMenu::SetClip(void)
{
sp_selection_set_mask(_desktop, true, false);
}
void ContextMenu::ReleaseClip(void)
{
sp_selection_unset_mask(_desktop, true);
}
void ContextMenu::MakeGroupMenu(void)
{
/* Ungroup */
Gtk::MenuItem* mi = new Gtk::MenuItem(_("_Ungroup"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ActivateUngroup));
mi->show();
append(*mi);
}
void ContextMenu::ActivateUngroup(void)
{
GSList *children = NULL;
sp_item_group_ungroup(static_cast<SPGroup*>(_item), &children);
_desktop->selection->setList(children);
g_slist_free(children);
}
void ContextMenu::MakeAnchorMenu(void)
{
Gtk::MenuItem* mi;
/* Link dialog */
mi = new Gtk::MenuItem(_("Link _Properties..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::AnchorLinkProperties));
mi->show();
insert(*mi,positionOfLastDialog++);
/* Select item */
mi = new Gtk::MenuItem(_("_Follow Link"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::AnchorLinkFollow));
mi->show();
append(*mi);
/* Reset transformations */
mi = new Gtk::MenuItem(_("_Remove Link"),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::AnchorLinkRemove));
mi->show();
append(*mi);
}
void ContextMenu::AnchorLinkProperties(void)
{
_desktop->_dlg_mgr->showDialog("ObjectAttributes");
}
void ContextMenu::AnchorLinkFollow(void)
{
/* shell out to an external browser here */
}
void ContextMenu::AnchorLinkRemove(void)
{
GSList *children = NULL;
sp_item_group_ungroup(static_cast<SPAnchor*>(_item), &children);
g_slist_free(children);
}
void ContextMenu::MakeImageMenu (void)
{
Gtk::MenuItem* mi;
Inkscape::XML::Node *ir = _object->getRepr();
const gchar *href = ir->attribute("xlink:href");
/* Image properties */
mi = new Gtk::MenuItem(_("Image _Properties..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageProperties));
mi->show();
insert(*mi,positionOfLastDialog++);
/* Edit externally */
mi = new Gtk::MenuItem(_("Edit Externally..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageEdit));
mi->show();
insert(*mi,positionOfLastDialog++);
if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
mi->set_sensitive( FALSE );
}
/* Embed image */
if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) {
mi = new Gtk::MenuItem(C_("Context menu", "Embed Image"));
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageEmbed));
mi->show();
insert(*mi,positionOfLastDialog++);
if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
mi->set_sensitive( FALSE );
}
}
/* Extract image */
if (Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" )) {
mi = new Gtk::MenuItem(C_("Context menu", "Extract Image..."));
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::ImageExtract));
mi->show();
insert(*mi,positionOfLastDialog++);
if ( (!href) || ((strncmp(href, "data:", 5) != 0)) ) {
mi->set_sensitive( FALSE );
}
}
}
void ContextMenu::ImageProperties(void)
{
_desktop->_dlg_mgr->showDialog("ObjectAttributes");
}
Glib::ustring ContextMenu::getImageEditorName() {
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
Glib::ustring value;
Glib::ustring choices = prefs->getString("/options/bitmapeditor/value");
if (!choices.empty()) {
value = choices;
}
else {
value = "gimp";
}
return value;
}
void ContextMenu::ImageEdit(void)
{
if (_desktop->selection->isEmpty()) {
_desktop->selection->set(_item);
}
GSList const *selected = _desktop->selection->itemList();
GError* errThing = 0;
Glib::ustring cmdline = getImageEditorName();
Glib::ustring name;
Glib::ustring fullname;
#ifdef WIN32
// g_spawn_command_line_sync parsing is done according to Unix shell rules,
// not Windows command interpreter rules. Thus we need to enclose the
// executable path with single quotes.
int index = cmdline.find(".exe");
if ( index < 0 ) index = cmdline.find(".bat");
if ( index < 0 ) index = cmdline.find(".com");
if ( index >= 0 ) {
Glib::ustring editorBin = cmdline.substr(0, index + 4).c_str();
Glib::ustring args = cmdline.substr(index + 4, cmdline.length()).c_str();
editorBin.insert(0, "'");
editorBin.append("'");
cmdline = editorBin;
cmdline.append(args);
} else {
// Enclose the whole command line if no executable path can be extracted.
cmdline.insert(0, "'");
cmdline.append("'");
}
#endif
for (GSList const *iter = selected; iter != NULL; iter = iter->next) {
Inkscape::XML::Node *ir = SP_ITEM(iter->data)->getRepr();
const gchar *href = ir->attribute("xlink:href");
if (strncmp (href,"file:",5) == 0) {
// URI to filename conversion
name = g_filename_from_uri(href, NULL, NULL);
} else {
name.append(href);
}
if (Glib::path_is_absolute(name)) {
fullname = name;
} else if (SP_ACTIVE_DOCUMENT->getBase()) {
fullname = Glib::build_filename(SP_ACTIVE_DOCUMENT->getBase(), name);
} else {
fullname = Glib::build_filename(Glib::get_current_dir(), name);
}
cmdline.append(" '");
cmdline.append(fullname.c_str());
cmdline.append("'");
}
//g_warning("##Command line: %s\n", cmdline.c_str());
g_spawn_command_line_async(cmdline.c_str(), &errThing);
if ( errThing ) {
g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message);
(_desktop->messageStack())->flash(Inkscape::ERROR_MESSAGE, errThing->message);
g_error_free(errThing);
errThing = 0;
}
}
void ContextMenu::ImageEmbed(void)
{
if (_desktop->selection->isEmpty()) {
_desktop->selection->set(_item);
}
Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" );
if (verb) {
SPAction *action = verb->get_action(_desktop);
if (action) {
sp_action_perform(action, NULL);
}
}
}
void ContextMenu::ImageExtract(void)
{
if (_desktop->selection->isEmpty()) {
_desktop->selection->set(_item);
}
Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" );
if (verb) {
SPAction *action = verb->get_action(_desktop);
if (action) {
sp_action_perform(action, NULL);
}
}
}
void ContextMenu::MakeShapeMenu (void)
{
Gtk::MenuItem* mi;
/* Item dialog */
mi = new Gtk::MenuItem(_("_Fill and Stroke..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::FillSettings));
mi->show();
insert(*mi,positionOfLastDialog++);
}
void ContextMenu::FillSettings(void)
{
if (_desktop->selection->isEmpty()) {
_desktop->selection->set(_item);
}
_desktop->_dlg_mgr->showDialog("FillAndStroke");
}
void ContextMenu::MakeTextMenu (void)
{
Gtk::MenuItem* mi;
/* Fill and Stroke dialog */
mi = new Gtk::MenuItem(_("_Fill and Stroke..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::FillSettings));
mi->show();
insert(*mi,positionOfLastDialog++);
/* Edit Text dialog */
mi = new Gtk::MenuItem(_("_Text and Font..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::TextSettings));
mi->show();
insert(*mi,positionOfLastDialog++);
/* Spellcheck dialog */
mi = new Gtk::MenuItem(_("Check Spellin_g..."),1);
mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::SpellcheckSettings));
mi->show();
insert(*mi,positionOfLastDialog++);
}
void ContextMenu::TextSettings (void)
{
if (_desktop->selection->isEmpty()) {
_desktop->selection->set(_item);
}
_desktop->_dlg_mgr->showDialog("TextFont");
}
void ContextMenu::SpellcheckSettings (void)
{
if (_desktop->selection->isEmpty()) {
_desktop->selection->set(_item);
}
_desktop->_dlg_mgr->showDialog("SpellCheck");
}
/*
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:fileencoding=utf-8:textwidth=99 :