Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / interface.cpp
1 /** @file
2  * @brief Main UI stuff
3  */
4 /* Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   Frank Felfe <innerspace@iname.com>
7  *   bulia byak <buliabyak@users.sf.net>
8  *   Jon A. Cruz <jon@joncruz.org>
9  *   Abhishek Sharma
10  *
11  * Copyright (C) 2010 authors
12  * Copyright (C) 1999-2005 authors
13  * Copyright (C) 2001-2002 Ximian, Inc.
14  * Copyright (C) 2004 David Turner
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <glib.h>
26 #include "inkscape-private.h"
27 #include "extension/db.h"
28 #include "extension/effect.h"
29 #include "extension/input.h"
30 #include "widgets/icon.h"
31 #include "preferences.h"
32 #include "path-prefix.h"
33 #include "shortcuts.h"
34 #include "document.h"
35 #include "desktop-handles.h"
36 #include "file.h"
37 #include "interface.h"
38 #include "desktop.h"
39 #include "ui/context-menu.h"
40 #include "selection.h"
41 #include "selection-chemistry.h"
42 #include "svg-view-widget.h"
43 #include "widgets/desktop-widget.h"
44 #include "sp-item-group.h"
45 #include "sp-text.h"
46 #include "sp-gradient-fns.h"
47 #include "sp-gradient.h"
48 #include "sp-flowtext.h"
49 #include "sp-namedview.h"
50 #include "ui/view/view.h"
51 #include "helper/action.h"
52 #include "helper/gnome-utils.h"
53 #include "helper/window.h"
54 #include "io/sys.h"
55 #include "dialogs/dialog-events.h"
56 #include "message-context.h"
57 #include "ui/uxmanager.h"
59 // Added for color drag-n-drop
60 #if ENABLE_LCMS
61 #include "lcms.h"
62 #endif // ENABLE_LCMS
63 #include "display/sp-canvas.h"
64 #include "color.h"
65 #include "svg/svg-color.h"
66 #include "desktop-style.h"
67 #include "style.h"
68 #include "event-context.h"
69 #include "gradient-drag.h"
70 #include "widgets/ege-paint-def.h"
72 // Include Mac OS X menu synchronization on native OSX build
73 #ifdef GDK_WINDOWING_QUARTZ
74 #include "ige-mac-menu.h"
75 #endif
77 using Inkscape::DocumentUndo;
79 /* Drag and Drop */
80 typedef enum {
81     URI_LIST,
82     SVG_XML_DATA,
83     SVG_DATA,
84     PNG_DATA,
85     JPEG_DATA,
86     IMAGE_DATA,
87     APP_X_INKY_COLOR,
88     APP_X_COLOR,
89     APP_OSWB_COLOR,
90 } ui_drop_target_info;
92 static GtkTargetEntry ui_drop_target_entries [] = {
93     {(gchar *)"text/uri-list",                0, URI_LIST        },
94     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
95     {(gchar *)"image/svg",                    0, SVG_DATA        },
96     {(gchar *)"image/png",                    0, PNG_DATA        },
97     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
98 #if ENABLE_MAGIC_COLORS
99     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
100 #endif // ENABLE_MAGIC_COLORS
101     {(gchar *)"application/x-oswb-color",     0, APP_OSWB_COLOR  },
102     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
103 };
105 static GtkTargetEntry *completeDropTargets = 0;
106 static int completeDropTargetsCount = 0;
107 static bool temporarily_block_actions = false;
109 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
110 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
111 static void sp_ui_import_files(gchar *buffer);
112 static void sp_ui_import_one_file(char const *filename);
113 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
114 static void sp_ui_drag_data_received(GtkWidget *widget,
115                                      GdkDragContext *drag_context,
116                                      gint x, gint y,
117                                      GtkSelectionData *data,
118                                      guint info,
119                                      guint event_time,
120                                      gpointer user_data);
121 static void sp_ui_drag_motion( GtkWidget *widget,
122                                GdkDragContext *drag_context,
123                                gint x, gint y,
124                                GtkSelectionData *data,
125                                guint info,
126                                guint event_time,
127                                gpointer user_data );
128 static void sp_ui_drag_leave( GtkWidget *widget,
129                               GdkDragContext *drag_context,
130                               guint event_time,
131                               gpointer user_data );
132 static void sp_ui_menu_item_set_sensitive(SPAction *action,
133                                           unsigned int sensitive,
134                                           void *data);
135 static void sp_ui_menu_item_set_name(SPAction *action,
136                                      Glib::ustring name,
137                                      void *data);
138 static void sp_recent_open(GtkRecentChooser *, gpointer);
140 static void injectRenamedIcons();
142 SPActionEventVector menu_item_event_vector = {
143     {NULL},
144     NULL,
145     NULL, /* set_active */
146     sp_ui_menu_item_set_sensitive, /* set_sensitive */
147     NULL, /* set_shortcut */
148     sp_ui_menu_item_set_name /* set_name */
149 };
151 static const int MIN_ONSCREEN_DISTANCE = 50;
153 void
154 sp_create_window(SPViewWidget *vw, gboolean editable)
156     g_return_if_fail(vw != NULL);
157     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
159     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
161     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
162     gtk_widget_show(GTK_WIDGET(vw));
164     if (editable) {
165         g_object_set_data(G_OBJECT(vw), "window", win);
167         SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
168         SPDesktop* desktop = desktop_widget->desktop;
170         desktop_widget->window = win;
172         win->set_data("desktop", desktop);
173         win->set_data("desktopwidget", desktop_widget);
175         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
176         win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
177         win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
179         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
180         gint prefs_geometry =
181             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
182         if (prefs_geometry) {
183             gint pw = prefs->getInt("/desktop/geometry/width", -1);
184             gint ph = prefs->getInt("/desktop/geometry/height", -1);
185             gint px = prefs->getInt("/desktop/geometry/x", -1);
186             gint py = prefs->getInt("/desktop/geometry/y", -1);
187             gint full = prefs->getBool("/desktop/geometry/fullscreen");
188             gint maxed = prefs->getBool("/desktop/geometry/maximized");
189             if (pw>0 && ph>0) {
190                 gint w = MIN(gdk_screen_width(), pw);
191                 gint h = MIN(gdk_screen_height(), ph);
192                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
193                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
194                 if (w>0 && h>0) {
195                     x = MIN(gdk_screen_width() - w, x);
196                     y = MIN(gdk_screen_height() - h, y);
197                     desktop->setWindowSize(w, h);
198                 }
200                 // Only restore xy for the first window so subsequent windows don't overlap exactly
201                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
202                 // other desktops?)
204                 // Empirically it seems that active_desktop==this desktop only the first time a
205                 // desktop is created.
206                 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
207                 if (active_desktop == desktop || active_desktop==NULL) {
208                     desktop->setWindowPosition(Geom::Point(x, y));
209                 }
210             }
211             if (maxed) {
212                 win->maximize();
213             }
214             if (full) {
215                 win->fullscreen();
216             }
217         }
219     } else {
220         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
221     }
223     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
224     {
225         std::vector<gchar*> types;
227         GSList *list = gdk_pixbuf_get_formats();
228         while ( list ) {
229             int i = 0;
230             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
231             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
232             for ( i = 0; typesXX[i]; i++ ) {
233                 types.push_back(g_strdup(typesXX[i]));
234             }
235             g_strfreev(typesXX);
237             list = g_slist_next(list);
238         }
239         completeDropTargetsCount = nui_drop_target_entries + types.size();
240         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
241         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
242             completeDropTargets[i] = ui_drop_target_entries[i];
243         }
244         int pos = nui_drop_target_entries;
246         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
247             completeDropTargets[pos].target = *it;
248             completeDropTargets[pos].flags = 0;
249             completeDropTargets[pos].info = IMAGE_DATA;
250             pos++;
251         }
252     }
254     gtk_drag_dest_set((GtkWidget*)win->gobj(),
255                       GTK_DEST_DEFAULT_ALL,
256                       completeDropTargets,
257                       completeDropTargetsCount,
258                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
261     g_signal_connect(G_OBJECT(win->gobj()),
262                      "drag_data_received",
263                      G_CALLBACK(sp_ui_drag_data_received),
264                      NULL);
265     g_signal_connect(G_OBJECT(win->gobj()),
266                      "drag_motion",
267                      G_CALLBACK(sp_ui_drag_motion),
268                      NULL);
269     g_signal_connect(G_OBJECT(win->gobj()),
270                      "drag_leave",
271                      G_CALLBACK(sp_ui_drag_leave),
272                      NULL);
273     win->show();
275     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
276     if ( SP_IS_DESKTOP_WIDGET(vw) ) {
277         inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
278     }
281 void
282 sp_ui_new_view()
284     SPDocument *document;
285     SPViewWidget *dtw;
287     document = SP_ACTIVE_DOCUMENT;
288     if (!document) return;
290     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
291     g_return_if_fail(dtw != NULL);
293     sp_create_window(dtw, TRUE);
294     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
295     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
298 /* TODO: not yet working */
299 /* To be re-enabled (by adding to menu) once it works. */
300 void sp_ui_new_view_preview()
302     SPDocument *document = SP_ACTIVE_DOCUMENT;
303     if ( document ) {
304         SPViewWidget *dtw = reinterpret_cast<SPViewWidget *>(sp_svg_view_widget_new(document));
305         g_return_if_fail(dtw != NULL);
306         SP_SVG_VIEW_WIDGET(dtw)->setResize(true, 400.0, 400.0);
308         sp_create_window(dtw, FALSE);
309     }
312 /**
313  * \param widget unused
314  */
315 void
316 sp_ui_close_view(GtkWidget */*widget*/)
318         SPDesktop *dt = SP_ACTIVE_DESKTOP;
320         if (dt == NULL) {
321         return;
322     }
324     if (dt->shutdown()) {
325         return; // Shutdown operation has been canceled, so do nothing
326     }
328     // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
329     // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
330     dt->destroyWidget();
334 /**
335  *  sp_ui_close_all
336  *
337  *  This function is called to exit the program, and iterates through all
338  *  open document view windows, attempting to close each in turn.  If the
339  *  view has unsaved information, the user will be prompted to save,
340  *  discard, or cancel.
341  *
342  *  Returns FALSE if the user cancels the close_all operation, TRUE
343  *  otherwise.
344  */
345 unsigned int
346 sp_ui_close_all(void)
348     /* Iterate through all the windows, destroying each in the order they
349        become active */
350     while (SP_ACTIVE_DESKTOP) {
351         SPDesktop *dt = SP_ACTIVE_DESKTOP;
352         if (dt->shutdown()) {
353             /* The user canceled the operation, so end doing the close */
354             return FALSE;
355         }
356         // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
357         // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
358         dt->destroyWidget();
359     }
361     return TRUE;
364 /*
365  * Some day when the right-click menus are ready to start working
366  * smarter with the verbs, we'll need to change this NULL being
367  * sent to sp_action_perform to something useful, or set some kind
368  * of global "right-clicked position" variable for actions to
369  * investigate when they're called.
370  */
371 static void
372 sp_ui_menu_activate(void */*object*/, SPAction *action)
374     if (!temporarily_block_actions) {
375         sp_action_perform(action, NULL);
376     }
379 static void
380 sp_ui_menu_select_action(void */*object*/, SPAction *action)
382     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
385 static void
386 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
388     action->view->tipsMessageContext()->clear();
391 static void
392 sp_ui_menu_select(gpointer object, gpointer tip)
394     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
395     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
398 static void
399 sp_ui_menu_deselect(gpointer object)
401     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
402     view->tipsMessageContext()->clear();
405 /**
406  * sp_ui_menuitem_add_icon
407  *
408  * Creates and attaches a scaled icon to the given menu item.
409  *
410  */
411 void
412 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
414     static bool iconsInjected = false;
415     if ( !iconsInjected ) {
416         iconsInjected = true;
417         injectRenamedIcons();
418     }
419     GtkWidget *icon;
421     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
422     gtk_widget_show(icon);
423     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
424 } // end of sp_ui_menu_add_icon
426 /**
427  * sp_ui_menu_append_item
428  *
429  * Appends a UI item with specific info for Inkscape/Sodipodi.
430  *
431  */
432 static GtkWidget *
433 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
434                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
435                         gpointer data, gboolean with_mnemonic = TRUE )
437     GtkWidget *item;
439     if (stock) {
440         item = gtk_image_menu_item_new_from_stock(stock, NULL);
441     } else if (label) {
442         item = (with_mnemonic)
443             ? gtk_image_menu_item_new_with_mnemonic(label) :
444             gtk_image_menu_item_new_with_label(label);
445     } else {
446         item = gtk_separator_menu_item_new();
447     }
449     gtk_widget_show(item);
451     if (callback) {
452         g_signal_connect(G_OBJECT(item), "activate", callback, data);
453     }
455     if (tip && view) {
456         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
457         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
458         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
459     }
461     gtk_menu_append(GTK_MENU(menu), item);
463     return item;
465 } // end of sp_ui_menu_append_item()
467 void
468 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
470     SPAction     *action;
471     unsigned int shortcut;
472     gchar        *s;
473     gchar        *atitle;
475     action = verb->get_action(NULL);
476     if (!action)
477         return;
479     atitle = sp_action_get_title(action);
481     s = g_stpcpy(c, atitle);
483     g_free(atitle);
485     shortcut = sp_shortcut_get_primary(verb);
486     if (shortcut!=GDK_VoidSymbol) {
487         gchar* key = sp_shortcut_get_label(shortcut);
488         s = g_stpcpy(s, " (");
489         s = g_stpcpy(s, key);
490         s = g_stpcpy(s, ")");
491         g_free(key);
492     }
496 /**
497  * sp_ui_menu_append_item_from_verb
498  *
499  * Appends a custom menu UI from a verb.
500  *
501  */
503 static GtkWidget *
504 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
506     SPAction *action;
507     GtkWidget *item;
509     if (verb->get_code() == SP_VERB_NONE) {
511         item = gtk_separator_menu_item_new();
513     } else {
514         unsigned int shortcut;
516         action = verb->get_action(view);
518         if (!action) return NULL;
520         shortcut = sp_shortcut_get_primary(verb);
521         if (shortcut!=GDK_VoidSymbol) {
522             gchar* c = sp_shortcut_get_label(shortcut);
523             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
524             GtkWidget *const name_lbl = gtk_label_new("");
525             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
526             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
527             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
528             GtkWidget *const accel_lbl = gtk_label_new(c);
529             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
530             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
531             gtk_widget_show_all(hb);
532             if (radio) {
533                 item = gtk_radio_menu_item_new (group);
534             } else {
535                 item = gtk_image_menu_item_new();
536             }
537             gtk_container_add((GtkContainer *) item, hb);
538             g_free(c);
539         } else {
540             if (radio) {
541                 item = gtk_radio_menu_item_new (group);
542             } else {
543                 item = gtk_image_menu_item_new ();
544             }
545             GtkWidget *const name_lbl = gtk_label_new("");
546             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
547             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
548             gtk_container_add((GtkContainer *) item, name_lbl);
549         }
551         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
552         if (!action->sensitive) {
553             gtk_widget_set_sensitive(item, FALSE);
554         }
556         if (action->image) {
557             sp_ui_menuitem_add_icon(item, action->image);
558         }
559         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
560         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
561         g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
562         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
563         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
564     }
566     gtk_widget_show(item);
567     gtk_menu_append(GTK_MENU(menu), item);
569     return item;
571 } // end of sp_ui_menu_append_item_from_verb
574 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
576     Glib::ustring prefPath;
578     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
579         prefPath = "/focus/";
580     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
581         prefPath = "/fullscreen/";
582     } else {
583         prefPath = "/window/";
584     }
586     return prefPath;
589 static void
590 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
592     gchar const *pref = (gchar const *) user_data;
593     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
595     Glib::ustring pref_path = getLayoutPrefPath( view );
596     pref_path += pref;
597     pref_path += "/state";
599     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
600     gboolean checked = gtk_check_menu_item_get_active(menuitem);
601     prefs->setBool(pref_path, checked);
603     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
606 static gboolean
607 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
609     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
611     gchar const *pref = (gchar const *) user_data;
612     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
614     Glib::ustring pref_path = getLayoutPrefPath( view );
615     pref_path += pref;
616     pref_path += "/state";
618     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
619     bool ison = prefs->getBool(pref_path, true);
621     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
622     gtk_check_menu_item_set_active(menuitem, ison);
623     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
625     return FALSE;
628 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
630     if ( gtk_check_menu_item_get_active(menuitem) ) {
631         gint taskNum = GPOINTER_TO_INT(userData);
632         taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
634         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
636         // note: this will change once more options are in the task set support:
637         Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
638     }
642 /**
643  *  \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
644  */
646 static gboolean
647 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
649         SPAction *action = (SPAction *) user_data;
650         g_assert(action->id != NULL);
652         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
653     SPDesktop *dt = static_cast<SPDesktop*>(view);
654         Inkscape::RenderMode mode = dt->getMode();
656         bool new_state = false;
657         if (!strcmp(action->id, "ViewModeNormal")) {
658         new_state = mode == Inkscape::RENDERMODE_NORMAL;
659         } else if (!strcmp(action->id, "ViewModeNoFilters")) {
660         new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
661     } else if (!strcmp(action->id, "ViewModeOutline")) {
662         new_state = mode == Inkscape::RENDERMODE_OUTLINE;
663     } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
664         new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
665     } else {
666         g_warning("update_view_menu does not handle this verb");
667     }
669         if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
670                 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
671                         // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
672                         // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
673                         // This emission however should not invoke any actions, hence we block it here:
674                         temporarily_block_actions = true;
675                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
676                         temporarily_block_actions = false;
677                 }
678         }
680         return FALSE;
683 void
684 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
685                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
686                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
687                                        Inkscape::Verb *verb)
689     unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
690     SPAction *action = (verb) ? verb->get_action(view) : 0;
691     GtkWidget *item = gtk_check_menu_item_new();
693     if (verb && shortcut!=GDK_VoidSymbol) {
694         gchar* c = sp_shortcut_get_label(shortcut);
696         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
698         {
699             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
700             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
701             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
702         }
704         {
705             GtkWidget *l = gtk_label_new(c);
706             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
707             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
708         }
710         gtk_widget_show_all(hb);
712         gtk_container_add((GtkContainer *) item, hb);
713         g_free(c);
714     } else {
715         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
716         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
717         gtk_container_add((GtkContainer *) item, l);
718     }
719 #if 0
720     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
721     if (!action->sensitive) {
722         gtk_widget_set_sensitive(item, FALSE);
723     }
724 #endif
725     gtk_widget_show(item);
727     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
729     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
731     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
732     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
734     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
735     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
738 static void
739 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
741     // dealing with the bizarre filename convention in Inkscape for now
742     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
743     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
744     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
745     sp_file_open(utf8_fn, NULL);
746     g_free(utf8_fn);
747     g_free(local_fn);
748     g_free(uri);
751 static void
752 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
754     sp_file_new(uri);
757 void
758 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
760     std::list<gchar *> sources;
761     sources.push_back( profile_path("templates") ); // first try user's local dir
762     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
764     // Use this loop to iterate through a list of possible document locations.
765     while (!sources.empty()) {
766         gchar *dirname = sources.front();
768         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
769             GError *err = 0;
770             GDir *dir = g_dir_open(dirname, 0, &err);
772             if (dir) {
773                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
774                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
775                         continue; // skip non-svg files
777                     gchar *basename = g_path_get_basename(file);
778                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
779                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
781                     gchar const *filepath = g_build_filename(dirname, file, NULL);
782                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
783                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
784                     g_free(dupfile);
785                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
786                     g_free(filename);
788                     gtk_widget_show(item);
789                     // how does "filepath" ever get freed?
790                     g_signal_connect(G_OBJECT(item),
791                                      "activate",
792                                      G_CALLBACK(sp_file_new_from_template),
793                                      (gpointer) filepath);
795                     if (view) {
796                         // set null tip for now; later use a description from the template file
797                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
798                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
799                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
800                     }
802                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
803                 }
804                 g_dir_close(dir);
805             }
806         }
808         // toss the dirname
809         g_free(dirname);
810         sources.pop_front();
811     }
814 void
815 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
817     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
818     //                                       checkitem_toggled, checkitem_update, 0);
819     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
820                                            checkitem_toggled, checkitem_update, 0);
821     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
822                                            checkitem_toggled, checkitem_update, 0);
823     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
824                                            checkitem_toggled, checkitem_update, 0);
825     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
826                                            checkitem_toggled, checkitem_update, 0);
827     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
828                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
829     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
830                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
831     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
832                                            checkitem_toggled, checkitem_update, 0);
833     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
834                                            checkitem_toggled, checkitem_update, 0);
838 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
840     gchar const* data[] = {
841         _("Default"), _("Default interface setup"),
842         _("Custom"), _("Set the custom task"),
843         _("Wide"), _("Setup for widescreen work"),
844         0, 0
845     };
847     GSList *group = 0;
848     int count = 0;
849     gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
850     for (gchar const **strs = data; strs[0]; strs += 2, count++)
851     {
852         GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
853         group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
854         if ( count == active )
855         {
856             gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
857         }
859         g_object_set_data( G_OBJECT(item), "view", view );
860         g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
861         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
862         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
864         gtk_widget_show( item );
865         gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
866     }
870 /** @brief Observer that updates the recent list's max document count */
871 class MaxRecentObserver : public Inkscape::Preferences::Observer {
872 public:
873     MaxRecentObserver(GtkWidget *recent_menu) :
874         Observer("/options/maxrecentdocuments/value"),
875         _rm(recent_menu)
876     {}
877     virtual void notify(Inkscape::Preferences::Entry const &e) {
878         gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
879         // hack: the recent menu doesn't repopulate after changing the limit, so we force it
880         g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
881     }
882 private:
883     GtkWidget *_rm;
884 };
886 /** \brief  This function turns XML into a menu
887     \param  menus  This is the XML that defines the menu
888     \param  menu   Menu to be added to
889     \param  view   The View that this menu is being built for
891     This function is realitively simple as it just goes through the XML
892     and parses the individual elements.  In the case of a submenu, it
893     just calls itself recursively.  Because it is only reasonable to have
894     a couple of submenus, it is unlikely this will go more than two or
895     three times.
897     In the case of an unrecognized verb, a menu item is made to identify
898     the verb that is missing, and display that.  The menu item is also made
899     insensitive.
900 */
901 void
902 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
904     if (menus == NULL) return;
905     if (menu == NULL)  return;
906     GSList *group = NULL;
908     for (Inkscape::XML::Node *menu_pntr = menus;
909          menu_pntr != NULL;
910          menu_pntr = menu_pntr->next()) {
911         if (!strcmp(menu_pntr->name(), "submenu")) {
912             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
913             GtkWidget *submenu = gtk_menu_new();
914             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
915             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
916             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
917             continue;
918         }
919         if (!strcmp(menu_pntr->name(), "verb")) {
920             gchar const *verb_name = menu_pntr->attribute("verb-id");
921             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
923             if (verb != NULL) {
924                 if (menu_pntr->attribute("radio") != NULL) {
925                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
926                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
927                     if (menu_pntr->attribute("default") != NULL) {
928                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
929                     }
930                     if (verb->get_code() != SP_VERB_NONE) {
931                         SPAction *action = verb->get_action(view);
932                         g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
933                     }
934                 } else {
935                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
936                     group = NULL;
937                 }
938             } else {
939                 gchar string[120];
940                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
941                 string[119] = '\0'; /* may not be terminated */
942                 GtkWidget *item = gtk_menu_item_new_with_label(string);
943                 gtk_widget_set_sensitive(item, false);
944                 gtk_widget_show(item);
945                 gtk_menu_append(GTK_MENU(menu), item);
946             }
947             continue;
948         }
949         if (!strcmp(menu_pntr->name(), "separator")
950                 // This was spelt wrong in the original version
951                 // and so this is for backward compatibility.  It can
952                 // probably be dropped after the 0.44 release.
953              || !strcmp(menu_pntr->name(), "seperator")) {
954             GtkWidget *item = gtk_separator_menu_item_new();
955             gtk_widget_show(item);
956             gtk_menu_append(GTK_MENU(menu), item);
957             continue;
958         }
959         if (!strcmp(menu_pntr->name(), "template-list")) {
960             sp_menu_append_new_templates(menu, view);
961             continue;
962         }
963         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
964             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
966             // create recent files menu
967             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
968             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
969             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
970             // sort most recently used documents first to preserve previous behavior
971             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
972             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
974             // add filter to only open files added by Inkscape
975             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
976             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
977             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
979             gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
980             gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
982             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
983             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
985             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
986             // this will just sit and update the list's item count
987             static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
988             prefs->addObserver(*mro);
989             continue;
990         }
991         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
992             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
993             continue;
994         }
995         if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
996             addTaskMenuItems(GTK_MENU(menu), view);
997             continue;
998         }
999     }
1002 /** \brief  Build the main tool bar
1003     \param  view  View to build the bar for
1005     Currently the main tool bar is built as a dynamic XML menu using
1006     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
1007     pass it to get items attached to it.
1008 */
1009 GtkWidget *
1010 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1012     GtkWidget *mbar = gtk_menu_bar_new();
1014 #ifdef GDK_WINDOWING_QUARTZ
1015     ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1016 #endif
1018     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1020 #ifdef GDK_WINDOWING_QUARTZ
1021     return NULL;
1022 #else
1023     return mbar;
1024 #endif
1027 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1028     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1031 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1032     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1033     sp_desktop_selection(desktop)->clear();
1036 GtkWidget *
1037 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1039     GtkWidget *m;
1040     SPDesktop *dt;
1042     dt = static_cast<SPDesktop*>(view);
1044     m = gtk_menu_new();
1046     /* Undo and Redo */
1047     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1048     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1050     /* Separator */
1051     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1053     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1054     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1055     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1057     /* Separator */
1058     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1060     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1061     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1063     /* Item menu */
1064     if (item) {
1065         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1066         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1067     }
1069     /* layer menu */
1070     SPGroup *group=NULL;
1071     if (item) {
1072         if (SP_IS_GROUP(item)) {
1073             group = SP_GROUP(item);
1074         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1075             group = SP_GROUP(SP_OBJECT_PARENT(item));
1076         }
1077     }
1079     if (( group && group != dt->currentLayer() ) ||
1080         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1081         /* Separator */
1082         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1083     }
1085     if ( group && group != dt->currentLayer() ) {
1086         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1087         gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1088         GtkWidget *w = gtk_menu_item_new_with_label(label);
1089         g_free(label);
1090         g_object_set_data(G_OBJECT(w), "group", group);
1091         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1092         gtk_widget_show(w);
1093         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1094     }
1096     if ( dt->currentLayer() != dt->currentRoot() ) {
1097         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1098             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1099             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1100             gtk_widget_show(w);
1101             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1103         }
1104     }
1106     return m;
1109 /* Drag and Drop */
1110 void
1111 sp_ui_drag_data_received(GtkWidget *widget,
1112                          GdkDragContext *drag_context,
1113                          gint x, gint y,
1114                          GtkSelectionData *data,
1115                          guint info,
1116                          guint /*event_time*/,
1117                          gpointer /*user_data*/)
1119     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1120     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1122     switch (info) {
1123 #if ENABLE_MAGIC_COLORS
1124         case APP_X_INKY_COLOR:
1125         {
1126             int destX = 0;
1127             int destY = 0;
1128             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1129             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1131             SPItem *item = desktop->getItemAtPoint( where, true );
1132             if ( item )
1133             {
1134                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1136                 if ( data->length >= 8 ) {
1137                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1139                     gchar c[64] = {0};
1140                     // Careful about endian issues.
1141                     guint16* dataVals = (guint16*)data->data;
1142                     sp_svg_write_color( c, sizeof(c),
1143                                         SP_RGBA32_U_COMPOSE(
1144                                             0x0ff & (dataVals[0] >> 8),
1145                                             0x0ff & (dataVals[1] >> 8),
1146                                             0x0ff & (dataVals[2] >> 8),
1147                                             0xff // can't have transparency in the color itself
1148                                             //0x0ff & (data->data[3] >> 8),
1149                                             ));
1150                     SPCSSAttr *css = sp_repr_css_attr_new();
1151                     bool updatePerformed = false;
1153                     if ( data->length > 14 ) {
1154                         int flags = dataVals[4];
1156                         // piggie-backed palette entry info
1157                         int index = dataVals[5];
1158                         Glib::ustring palName;
1159                         for ( int i = 0; i < dataVals[6]; i++ ) {
1160                             palName += (gunichar)dataVals[7+i];
1161                         }
1163                         // Now hook in a magic tag of some sort.
1164                         if ( !palName.empty() && (flags & 1) ) {
1165                             gchar* str = g_strdup_printf("%d|", index);
1166                             palName.insert( 0, str );
1167                             g_free(str);
1168                             str = 0;
1170                             SP_OBJECT(item)->setAttribute( 
1171                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1172                                                     palName.c_str(),
1173                                                     false );
1174                             item->updateRepr();
1176                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1177                             updatePerformed = true;
1178                         }
1179                     }
1181                     if ( !updatePerformed ) {
1182                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1183                     }
1185                     sp_desktop_apply_css_recursive( item, css, true );
1186                     item->updateRepr();
1188                     SPDocumentUndo::done( doc , SP_VERB_NONE,
1189                                       _("Drop color"));
1191                     if ( srgbProf ) {
1192                         cmsCloseProfile( srgbProf );
1193                     }
1194                 }
1195             }
1196         }
1197         break;
1198 #endif // ENABLE_MAGIC_COLORS
1200         case APP_X_COLOR:
1201         {
1202             int destX = 0;
1203             int destY = 0;
1204             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1205             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1206             Geom::Point const button_dt(desktop->w2d(where));
1207             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1209             if ( data->length == 8 ) {
1210                 gchar colorspec[64] = {0};
1211                 // Careful about endian issues.
1212                 guint16* dataVals = (guint16*)data->data;
1213                 sp_svg_write_color( colorspec, sizeof(colorspec),
1214                                     SP_RGBA32_U_COMPOSE(
1215                                         0x0ff & (dataVals[0] >> 8),
1216                                         0x0ff & (dataVals[1] >> 8),
1217                                         0x0ff & (dataVals[2] >> 8),
1218                                         0xff // can't have transparency in the color itself
1219                                         //0x0ff & (data->data[3] >> 8),
1220                                         ));
1222                 SPItem *item = desktop->getItemAtPoint( where, true );
1224                 bool consumed = false;
1225                 if (desktop->event_context && desktop->event_context->get_drag()) {
1226                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1227                     if (consumed) {
1228                         DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1229                         desktop->event_context->get_drag()->updateDraggers();
1230                     }
1231                 }
1233                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1234                 //    consumed = sp_text_context_drop_color(c, button_doc);
1235                 //    if (consumed) {
1236                 //        SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1237                 //    }
1238                 //}
1240                 if (!consumed && item) {
1241                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1242                     if (fillnotstroke &&
1243                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1244                         Path *livarot_path = Path_for_item(item, true, true);
1245                         livarot_path->ConvertWithBackData(0.04);
1247                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1248                         if (position) {
1249                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1250                             Geom::Point delta = nearest - button_doc;
1251                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1252                             delta = desktop->d2w(delta);
1253                             double stroke_tolerance =
1254                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1255                                   desktop->current_zoom() *
1256                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1257                                   to_2geom(item->i2d_affine()).descrim() * 0.5
1258                                   : 0.0)
1259                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1261                             if (Geom::L2 (delta) < stroke_tolerance) {
1262                                 fillnotstroke = false;
1263                             }
1264                         }
1265                         delete livarot_path;
1266                     }
1268                     SPCSSAttr *css = sp_repr_css_attr_new();
1269                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1271                     sp_desktop_apply_css_recursive( item, css, true );
1272                     item->updateRepr();
1274                     DocumentUndo::done( doc , SP_VERB_NONE,
1275                                         _("Drop color") );
1276                 }
1277             }
1278         }
1279         break;
1281         case APP_OSWB_COLOR:
1282         {
1283             bool worked = false;
1284             Glib::ustring colorspec;
1285             if ( data->format == 8 ) {
1286                 ege::PaintDef color;
1287                 worked = color.fromMIMEData("application/x-oswb-color",
1288                                             reinterpret_cast<char*>(data->data),
1289                                             data->length,
1290                                             data->format);
1291                 if ( worked ) {
1292                     if ( color.getType() == ege::PaintDef::CLEAR ) {
1293                         colorspec = ""; // TODO check if this is sufficient
1294                     } else if ( color.getType() == ege::PaintDef::NONE ) {
1295                         colorspec = "none";
1296                     } else {
1297                         unsigned int r = color.getR();
1298                         unsigned int g = color.getG();
1299                         unsigned int b = color.getB();
1301                         SPGradient* matches = 0;
1302                         const GSList *gradients = doc->getResourceList("gradient");
1303                         for (const GSList *item = gradients; item; item = item->next) {
1304                             SPGradient* grad = SP_GRADIENT(item->data);
1305                             if ( color.descr == grad->getId() ) {
1306                                 if ( grad->hasStops() ) {
1307                                     matches = grad;
1308                                     break;
1309                                 }
1310                             }
1311                         }
1312                         if (matches) {
1313                             colorspec = "url(#";
1314                             colorspec += matches->getId();
1315                             colorspec += ")";
1316                         } else {
1317                             gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1318                             colorspec = tmp;
1319                             g_free(tmp);
1320                         }
1321                     }
1322                 }
1323             }
1324             if ( worked ) {
1325                 int destX = 0;
1326                 int destY = 0;
1327                 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1328                 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1329                 Geom::Point const button_dt(desktop->w2d(where));
1330                 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1332                 SPItem *item = desktop->getItemAtPoint( where, true );
1334                 bool consumed = false;
1335                 if (desktop->event_context && desktop->event_context->get_drag()) {
1336                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1337                     if (consumed) {
1338                         DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1339                         desktop->event_context->get_drag()->updateDraggers();
1340                     }
1341                 }
1343                 if (!consumed && item) {
1344                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1345                     if (fillnotstroke &&
1346                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1347                         Path *livarot_path = Path_for_item(item, true, true);
1348                         livarot_path->ConvertWithBackData(0.04);
1350                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1351                         if (position) {
1352                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1353                             Geom::Point delta = nearest - button_doc;
1354                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1355                             delta = desktop->d2w(delta);
1356                             double stroke_tolerance =
1357                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1358                                   desktop->current_zoom() *
1359                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1360                                   to_2geom(item->i2d_affine()).descrim() * 0.5
1361                                   : 0.0)
1362                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1364                             if (Geom::L2 (delta) < stroke_tolerance) {
1365                                 fillnotstroke = false;
1366                             }
1367                         }
1368                         delete livarot_path;
1369                     }
1371                     SPCSSAttr *css = sp_repr_css_attr_new();
1372                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1374                     sp_desktop_apply_css_recursive( item, css, true );
1375                     item->updateRepr();
1377                     DocumentUndo::done( doc , SP_VERB_NONE,
1378                                         _("Drop color") );
1379                 }
1380             }
1381         }
1382         break;
1384         case SVG_DATA:
1385         case SVG_XML_DATA: {
1386             gchar *svgdata = (gchar *)data->data;
1388             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1390             if (rnewdoc == NULL) {
1391                 sp_ui_error_dialog(_("Could not parse SVG data"));
1392                 return;
1393             }
1395             Inkscape::XML::Node *repr = rnewdoc->root();
1396             gchar const *style = repr->attribute("style");
1398             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1399             newgroup->setAttribute("style", style);
1401             Inkscape::XML::Document * xml_doc =  doc->getReprDoc();
1402             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1403                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1404                 newgroup->appendChild(newchild);
1405             }
1407             Inkscape::GC::release(rnewdoc);
1409             // Add it to the current layer
1411             // Greg's edits to add intelligent positioning of svg drops
1412             SPObject *new_obj = NULL;
1413             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1415             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1416             selection->set(SP_ITEM(new_obj));
1418             // move to mouse pointer
1419             {
1420                 sp_desktop_document(desktop)->ensureUpToDate();
1421                 Geom::OptRect sel_bbox = selection->bounds();
1422                 if (sel_bbox) {
1423                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1424                     sp_selection_move_relative(selection, m, false);
1425                 }
1426             }
1428             Inkscape::GC::release(newgroup);
1429             DocumentUndo::done( doc, SP_VERB_NONE,
1430                              _("Drop SVG") );
1431             break;
1432         }
1434         case URI_LIST: {
1435             gchar *uri = (gchar *)data->data;
1436             sp_ui_import_files(uri);
1437             break;
1438         }
1440         case PNG_DATA:
1441         case JPEG_DATA:
1442         case IMAGE_DATA: {
1443             const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1445             Inkscape::Extension::DB::InputList o;
1446             Inkscape::Extension::db.get_input_list(o);
1447             Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1448             while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1449                 ++i;
1450             }
1451             Inkscape::Extension::Extension *ext = *i;
1452             bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1453             ext->set_param_optiongroup("link", "embed");
1454             ext->set_gui(false);
1456             gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1457             g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1458             file_import(doc, filename, ext);
1459             g_free(filename);
1461             ext->set_param_optiongroup("link", save ? "embed" : "link");
1462             ext->set_gui(true);
1463             DocumentUndo::done( doc , SP_VERB_NONE,
1464                                 _("Drop bitmap image") );
1465             break;
1466         }
1467     }
1470 #include "gradient-context.h"
1472 void sp_ui_drag_motion( GtkWidget */*widget*/,
1473                         GdkDragContext */*drag_context*/,
1474                         gint /*x*/, gint /*y*/,
1475                         GtkSelectionData */*data*/,
1476                         guint /*info*/,
1477                         guint /*event_time*/,
1478                         gpointer /*user_data*/)
1480 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1481 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1484 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1487 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1488                               GdkDragContext */*drag_context*/,
1489                               guint /*event_time*/,
1490                               gpointer /*user_data*/ )
1492 //     g_message("drag-n-drop leave                at %d", event_time);
1495 static void
1496 sp_ui_import_files(gchar *buffer)
1498     GList *list = gnome_uri_list_extract_filenames(buffer);
1499     if (!list)
1500         return;
1501     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1502     g_list_foreach(list, (GFunc) g_free, NULL);
1503     g_list_free(list);
1506 static void
1507 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1509     if (filename) {
1510         if (strlen((char const *)filename) > 2)
1511             sp_ui_import_one_file((char const *)filename);
1512     }
1515 static void
1516 sp_ui_import_one_file(char const *filename)
1518     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1519     if (!doc) return;
1521     if (filename == NULL) return;
1523     // Pass off to common implementation
1524     // TODO might need to get the proper type of Inkscape::Extension::Extension
1525     file_import( doc, filename, NULL );
1528 void
1529 sp_ui_error_dialog(gchar const *message)
1531     GtkWidget *dlg;
1532     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1534     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1535                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1536     sp_transientize(dlg);
1537     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1538     gtk_dialog_run(GTK_DIALOG(dlg));
1539     gtk_widget_destroy(dlg);
1540     g_free(safeMsg);
1543 bool
1544 sp_ui_overwrite_file(gchar const *filename)
1546     bool return_value = FALSE;
1548     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1549         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1550         gchar* baseName = g_path_get_basename( filename );
1551         gchar* dirName = g_path_get_dirname( filename );
1552         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1553                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1554                                                                 GTK_MESSAGE_QUESTION,
1555                                                                 GTK_BUTTONS_NONE,
1556                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1557                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1558                                                                 baseName,
1559                                                                 dirName
1560             );
1561         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1562                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1563                                 _("Replace"), GTK_RESPONSE_YES,
1564                                 NULL );
1565         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1567         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1568             return_value = TRUE;
1569         } else {
1570             return_value = FALSE;
1571         }
1572         gtk_widget_destroy(dialog);
1573         g_free( baseName );
1574         g_free( dirName );
1575     } else {
1576         return_value = TRUE;
1577     }
1579     return return_value;
1582 static void
1583 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1585     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1588 static void
1589 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1591     void *child = GTK_BIN (data)->child;
1592     //child is either
1593     //- a GtkHBox, whose first child is a label displaying name if the menu
1594     //item has an accel key
1595     //- a GtkLabel if the menu has no accel key
1596     if (GTK_IS_LABEL(child)) {
1597         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1598     } else if (GTK_IS_HBOX(child)) {
1599         gtk_label_set_markup_with_mnemonic(
1600         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1601         name.c_str());
1602     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1603     //a menu item in yet another way...
1606 void injectRenamedIcons()
1608     Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
1610     std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed;
1611     renamed.push_back(std::make_pair("gtk-file", "document-x-generic"));
1612     renamed.push_back(std::make_pair("gtk-directory", "folder"));
1614     for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) {
1615         bool hasIcon = iconTheme->has_icon(it->first);
1616         bool hasSecondIcon = iconTheme->has_icon(it->second);
1618         if ( !hasIcon && hasSecondIcon ) {
1619             Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second);
1620             for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
1621                 Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
1622                 if ( pb ) {
1623                     // install a private copy of the pixbuf to avoid pinning a theme
1624                     Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
1625                     Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
1626                 }
1627             }
1628         }
1629     }
1633 /*
1634   Local Variables:
1635   mode:c++
1636   c-file-style:"stroustrup"
1637   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1638   indent-tabs-mode:nil
1639   fill-column:99
1640   End:
1641 */
1642 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :