Code

fix misunderstood path-closed-flag in LWPOLYLINE (Bug 656899)
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /** @file
4  * @brief Main UI stuff
5  */
6 /* Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 1999-2005 authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  * Copyright (C) 2004 David Turner
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include <glib.h>
25 #include "inkscape-private.h"
26 #include "extension/db.h"
27 #include "extension/effect.h"
28 #include "extension/input.h"
29 #include "widgets/icon.h"
30 #include "preferences.h"
31 #include "path-prefix.h"
32 #include "shortcuts.h"
33 #include "document.h"
34 #include "desktop-handles.h"
35 #include "file.h"
36 #include "interface.h"
37 #include "desktop.h"
38 #include "ui/context-menu.h"
39 #include "selection.h"
40 #include "selection-chemistry.h"
41 #include "svg-view-widget.h"
42 #include "widgets/desktop-widget.h"
43 #include "sp-item-group.h"
44 #include "sp-text.h"
45 #include "sp-gradient-fns.h"
46 #include "sp-gradient.h"
47 #include "sp-flowtext.h"
48 #include "sp-namedview.h"
49 #include "ui/view/view.h"
50 #include "helper/action.h"
51 #include "helper/gnome-utils.h"
52 #include "helper/window.h"
53 #include "io/sys.h"
54 #include "dialogs/dialog-events.h"
55 #include "message-context.h"
56 #include "ui/uxmanager.h"
58 // Added for color drag-n-drop
59 #if ENABLE_LCMS
60 #include "lcms.h"
61 #endif // ENABLE_LCMS
62 #include "display/sp-canvas.h"
63 #include "color.h"
64 #include "svg/svg-color.h"
65 #include "desktop-style.h"
66 #include "style.h"
67 #include "event-context.h"
68 #include "gradient-drag.h"
69 #include "widgets/ege-paint-def.h"
71 // Include Mac OS X menu synchronization on native OSX build
72 #ifdef GDK_WINDOWING_QUARTZ
73 #include "ige-mac-menu.h"
74 #endif
76 /* Drag and Drop */
77 typedef enum {
78     URI_LIST,
79     SVG_XML_DATA,
80     SVG_DATA,
81     PNG_DATA,
82     JPEG_DATA,
83     IMAGE_DATA,
84     APP_X_INKY_COLOR,
85     APP_X_COLOR,
86     APP_OSWB_COLOR,
87 } ui_drop_target_info;
89 static GtkTargetEntry ui_drop_target_entries [] = {
90     {(gchar *)"text/uri-list",                0, URI_LIST        },
91     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
92     {(gchar *)"image/svg",                    0, SVG_DATA        },
93     {(gchar *)"image/png",                    0, PNG_DATA        },
94     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
95 #if ENABLE_MAGIC_COLORS
96     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
97 #endif // ENABLE_MAGIC_COLORS
98     {(gchar *)"application/x-oswb-color",     0, APP_OSWB_COLOR  },
99     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
100 };
102 static GtkTargetEntry *completeDropTargets = 0;
103 static int completeDropTargetsCount = 0;
104 static bool temporarily_block_actions = false;
106 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
107 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
108 static void sp_ui_import_files(gchar *buffer);
109 static void sp_ui_import_one_file(char const *filename);
110 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
111 static void sp_ui_drag_data_received(GtkWidget *widget,
112                                      GdkDragContext *drag_context,
113                                      gint x, gint y,
114                                      GtkSelectionData *data,
115                                      guint info,
116                                      guint event_time,
117                                      gpointer user_data);
118 static void sp_ui_drag_motion( GtkWidget *widget,
119                                GdkDragContext *drag_context,
120                                gint x, gint y,
121                                GtkSelectionData *data,
122                                guint info,
123                                guint event_time,
124                                gpointer user_data );
125 static void sp_ui_drag_leave( GtkWidget *widget,
126                               GdkDragContext *drag_context,
127                               guint event_time,
128                               gpointer user_data );
129 static void sp_ui_menu_item_set_sensitive(SPAction *action,
130                                           unsigned int sensitive,
131                                           void *data);
132 static void sp_ui_menu_item_set_name(SPAction *action,
133                                      Glib::ustring name,
134                                      void *data);
135 static void sp_recent_open(GtkRecentChooser *, gpointer);
137 SPActionEventVector menu_item_event_vector = {
138     {NULL},
139     NULL,
140     NULL, /* set_active */
141     sp_ui_menu_item_set_sensitive, /* set_sensitive */
142     NULL, /* set_shortcut */
143     sp_ui_menu_item_set_name /* set_name */
144 };
146 static const int MIN_ONSCREEN_DISTANCE = 50;
148 void
149 sp_create_window(SPViewWidget *vw, gboolean editable)
151     g_return_if_fail(vw != NULL);
152     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
154     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
156     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
157     gtk_widget_show(GTK_WIDGET(vw));
159     if (editable) {
160         g_object_set_data(G_OBJECT(vw), "window", win);
162         SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
163         SPDesktop* desktop = desktop_widget->desktop;
165         desktop_widget->window = win;
167         win->set_data("desktop", desktop);
168         win->set_data("desktopwidget", desktop_widget);
170         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
171         win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
172         win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
174         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
175         gint prefs_geometry =
176             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
177         if (prefs_geometry) {
178             gint pw = prefs->getInt("/desktop/geometry/width", -1);
179             gint ph = prefs->getInt("/desktop/geometry/height", -1);
180             gint px = prefs->getInt("/desktop/geometry/x", -1);
181             gint py = prefs->getInt("/desktop/geometry/y", -1);
182             gint full = prefs->getBool("/desktop/geometry/fullscreen");
183             gint maxed = prefs->getBool("/desktop/geometry/maximized");
184             if (pw>0 && ph>0) {
185                 gint w = MIN(gdk_screen_width(), pw);
186                 gint h = MIN(gdk_screen_height(), ph);
187                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
188                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
189                 if (w>0 && h>0) {
190                     x = MIN(gdk_screen_width() - w, x);
191                     y = MIN(gdk_screen_height() - h, y);
192                     desktop->setWindowSize(w, h);
193                 }
195                 // Only restore xy for the first window so subsequent windows don't overlap exactly
196                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
197                 // other desktops?)
199                 // Empirically it seems that active_desktop==this desktop only the first time a
200                 // desktop is created.
201                 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
202                 if (active_desktop == desktop || active_desktop==NULL) {
203                     desktop->setWindowPosition(Geom::Point(x, y));
204                 }
205             }
206             if (maxed) {
207                 win->maximize();
208             }
209             if (full) {
210                 win->fullscreen();
211             }
212         }
214     } else {
215         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
216     }
218     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
219     {
220         std::vector<gchar*> types;
222         GSList *list = gdk_pixbuf_get_formats();
223         while ( list ) {
224             int i = 0;
225             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
226             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
227             for ( i = 0; typesXX[i]; i++ ) {
228                 types.push_back(g_strdup(typesXX[i]));
229             }
230             g_strfreev(typesXX);
232             list = g_slist_next(list);
233         }
234         completeDropTargetsCount = nui_drop_target_entries + types.size();
235         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
236         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
237             completeDropTargets[i] = ui_drop_target_entries[i];
238         }
239         int pos = nui_drop_target_entries;
241         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
242             completeDropTargets[pos].target = *it;
243             completeDropTargets[pos].flags = 0;
244             completeDropTargets[pos].info = IMAGE_DATA;
245             pos++;
246         }
247     }
249     gtk_drag_dest_set((GtkWidget*)win->gobj(),
250                       GTK_DEST_DEFAULT_ALL,
251                       completeDropTargets,
252                       completeDropTargetsCount,
253                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
256     g_signal_connect(G_OBJECT(win->gobj()),
257                      "drag_data_received",
258                      G_CALLBACK(sp_ui_drag_data_received),
259                      NULL);
260     g_signal_connect(G_OBJECT(win->gobj()),
261                      "drag_motion",
262                      G_CALLBACK(sp_ui_drag_motion),
263                      NULL);
264     g_signal_connect(G_OBJECT(win->gobj()),
265                      "drag_leave",
266                      G_CALLBACK(sp_ui_drag_leave),
267                      NULL);
268     win->show();
270     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
271     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
274 void
275 sp_ui_new_view()
277     SPDocument *document;
278     SPViewWidget *dtw;
280     document = SP_ACTIVE_DOCUMENT;
281     if (!document) return;
283     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
284     g_return_if_fail(dtw != NULL);
286     sp_create_window(dtw, TRUE);
287     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
288     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
291 /* TODO: not yet working */
292 /* To be re-enabled (by adding to menu) once it works. */
293 void
294 sp_ui_new_view_preview()
296     SPDocument *document;
297     SPViewWidget *dtw;
299     document = SP_ACTIVE_DOCUMENT;
300     if (!document) return;
302     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
303     g_return_if_fail(dtw != NULL);
304     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
306     sp_create_window(dtw, FALSE);
309 /**
310  * \param widget unused
311  */
312 void
313 sp_ui_close_view(GtkWidget */*widget*/)
315         SPDesktop *dt = SP_ACTIVE_DESKTOP;
317         if (dt == NULL) {
318         return;
319     }
321     if (dt->shutdown()) {
322         return; // Shutdown operation has been canceled, so do nothing
323     }
325     // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
326     // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
327     dt->destroyWidget();
331 /**
332  *  sp_ui_close_all
333  *
334  *  This function is called to exit the program, and iterates through all
335  *  open document view windows, attempting to close each in turn.  If the
336  *  view has unsaved information, the user will be prompted to save,
337  *  discard, or cancel.
338  *
339  *  Returns FALSE if the user cancels the close_all operation, TRUE
340  *  otherwise.
341  */
342 unsigned int
343 sp_ui_close_all(void)
345     /* Iterate through all the windows, destroying each in the order they
346        become active */
347     while (SP_ACTIVE_DESKTOP) {
348         SPDesktop *dt = SP_ACTIVE_DESKTOP;
349         if (dt->shutdown()) {
350             /* The user canceled the operation, so end doing the close */
351             return FALSE;
352         }
353         // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
354         // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
355         dt->destroyWidget();
356     }
358     return TRUE;
361 /*
362  * Some day when the right-click menus are ready to start working
363  * smarter with the verbs, we'll need to change this NULL being
364  * sent to sp_action_perform to something useful, or set some kind
365  * of global "right-clicked position" variable for actions to
366  * investigate when they're called.
367  */
368 static void
369 sp_ui_menu_activate(void */*object*/, SPAction *action)
371     if (!temporarily_block_actions) {
372         sp_action_perform(action, NULL);
373     }
376 static void
377 sp_ui_menu_select_action(void */*object*/, SPAction *action)
379     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
382 static void
383 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
385     action->view->tipsMessageContext()->clear();
388 static void
389 sp_ui_menu_select(gpointer object, gpointer tip)
391     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
392     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
395 static void
396 sp_ui_menu_deselect(gpointer object)
398     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
399     view->tipsMessageContext()->clear();
402 /**
403  * sp_ui_menuitem_add_icon
404  *
405  * Creates and attaches a scaled icon to the given menu item.
406  *
407  */
408 void
409 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
411     GtkWidget *icon;
413     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
414     gtk_widget_show(icon);
415     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
416 } // end of sp_ui_menu_add_icon
418 /**
419  * sp_ui_menu_append_item
420  *
421  * Appends a UI item with specific info for Inkscape/Sodipodi.
422  *
423  */
424 static GtkWidget *
425 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
426                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
427                         gpointer data, gboolean with_mnemonic = TRUE )
429     GtkWidget *item;
431     if (stock) {
432         item = gtk_image_menu_item_new_from_stock(stock, NULL);
433     } else if (label) {
434         item = (with_mnemonic)
435             ? gtk_image_menu_item_new_with_mnemonic(label) :
436             gtk_image_menu_item_new_with_label(label);
437     } else {
438         item = gtk_separator_menu_item_new();
439     }
441     gtk_widget_show(item);
443     if (callback) {
444         g_signal_connect(G_OBJECT(item), "activate", callback, data);
445     }
447     if (tip && view) {
448         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
449         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
450         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
451     }
453     gtk_menu_append(GTK_MENU(menu), item);
455     return item;
457 } // end of sp_ui_menu_append_item()
459 void
460 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
462     SPAction     *action;
463     unsigned int shortcut;
464     gchar        *s;
465     gchar        *atitle;
467     action = verb->get_action(NULL);
468     if (!action)
469         return;
471     atitle = sp_action_get_title(action);
473     s = g_stpcpy(c, atitle);
475     g_free(atitle);
477     shortcut = sp_shortcut_get_primary(verb);
478     if (shortcut!=GDK_VoidSymbol) {
479         gchar* key = sp_shortcut_get_label(shortcut);
480         s = g_stpcpy(s, " (");
481         s = g_stpcpy(s, key);
482         s = g_stpcpy(s, ")");
483         g_free(key);
484     }
488 /**
489  * sp_ui_menu_append_item_from_verb
490  *
491  * Appends a custom menu UI from a verb.
492  *
493  */
495 static GtkWidget *
496 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
498     SPAction *action;
499     GtkWidget *item;
501     if (verb->get_code() == SP_VERB_NONE) {
503         item = gtk_separator_menu_item_new();
505     } else {
506         unsigned int shortcut;
508         action = verb->get_action(view);
510         if (!action) return NULL;
512         shortcut = sp_shortcut_get_primary(verb);
513         if (shortcut!=GDK_VoidSymbol) {
514             gchar* c = sp_shortcut_get_label(shortcut);
515             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
516             GtkWidget *const name_lbl = gtk_label_new("");
517             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
518             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
519             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
520             GtkWidget *const accel_lbl = gtk_label_new(c);
521             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
522             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
523             gtk_widget_show_all(hb);
524             if (radio) {
525                 item = gtk_radio_menu_item_new (group);
526             } else {
527                 item = gtk_image_menu_item_new();
528             }
529             gtk_container_add((GtkContainer *) item, hb);
530             g_free(c);
531         } else {
532             if (radio) {
533                 item = gtk_radio_menu_item_new (group);
534             } else {
535                 item = gtk_image_menu_item_new ();
536             }
537             GtkWidget *const name_lbl = gtk_label_new("");
538             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
539             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
540             gtk_container_add((GtkContainer *) item, name_lbl);
541         }
543         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
544         if (!action->sensitive) {
545             gtk_widget_set_sensitive(item, FALSE);
546         }
548         if (action->image) {
549             sp_ui_menuitem_add_icon(item, action->image);
550         }
551         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
552         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
553         g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
554         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
555         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
556     }
558     gtk_widget_show(item);
559     gtk_menu_append(GTK_MENU(menu), item);
561     return item;
563 } // end of sp_ui_menu_append_item_from_verb
566 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
568     Glib::ustring prefPath;
570     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
571         prefPath = "/focus/";
572     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
573         prefPath = "/fullscreen/";
574     } else {
575         prefPath = "/window/";
576     }
578     return prefPath;
581 static void
582 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
584     gchar const *pref = (gchar const *) user_data;
585     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
587     Glib::ustring pref_path = getLayoutPrefPath( view );
588     pref_path += pref;
589     pref_path += "/state";
591     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
592     gboolean checked = gtk_check_menu_item_get_active(menuitem);
593     prefs->setBool(pref_path, checked);
595     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
598 static gboolean
599 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
601     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
603     gchar const *pref = (gchar const *) user_data;
604     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
606     Glib::ustring pref_path = getLayoutPrefPath( view );
607     pref_path += pref;
608     pref_path += "/state";
610     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
611     bool ison = prefs->getBool(pref_path, true);
613     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
614     gtk_check_menu_item_set_active(menuitem, ison);
615     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
617     return FALSE;
620 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
622     if ( gtk_check_menu_item_get_active(menuitem) ) {
623         gint taskNum = GPOINTER_TO_INT(userData);
624         taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
626         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
628         // note: this will change once more options are in the task set support:
629         Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
630     }
634 /**
635  *  \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
636  */
638 static gboolean
639 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
641         SPAction *action = (SPAction *) user_data;
642         g_assert(action->id != NULL);
644         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
645     SPDesktop *dt = static_cast<SPDesktop*>(view);
646         Inkscape::RenderMode mode = dt->getMode();
648         bool new_state = false;
649         if (!strcmp(action->id, "ViewModeNormal")) {
650         new_state = mode == Inkscape::RENDERMODE_NORMAL;
651         } else if (!strcmp(action->id, "ViewModeNoFilters")) {
652         new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
653     } else if (!strcmp(action->id, "ViewModeOutline")) {
654         new_state = mode == Inkscape::RENDERMODE_OUTLINE;
655     } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
656         new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
657     } else {
658         g_warning("update_view_menu does not handle this verb");
659     }
661         if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
662                 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
663                         // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
664                         // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
665                         // This emission however should not invoke any actions, hence we block it here:
666                         temporarily_block_actions = true;
667                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
668                         temporarily_block_actions = false;
669                 }
670         }
672         return FALSE;
675 void
676 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
677                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
678                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
679                                        Inkscape::Verb *verb)
681     unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
682     SPAction *action = (verb) ? verb->get_action(view) : 0;
683     GtkWidget *item = gtk_check_menu_item_new();
685     if (verb && shortcut!=GDK_VoidSymbol) {
686         gchar* c = sp_shortcut_get_label(shortcut);
688         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
690         {
691             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
692             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
693             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
694         }
696         {
697             GtkWidget *l = gtk_label_new(c);
698             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
699             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
700         }
702         gtk_widget_show_all(hb);
704         gtk_container_add((GtkContainer *) item, hb);
705         g_free(c);
706     } else {
707         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
708         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
709         gtk_container_add((GtkContainer *) item, l);
710     }
711 #if 0
712     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
713     if (!action->sensitive) {
714         gtk_widget_set_sensitive(item, FALSE);
715     }
716 #endif
717     gtk_widget_show(item);
719     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
721     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
723     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
724     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
726     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
727     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
730 static void
731 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
733     // dealing with the bizarre filename convention in Inkscape for now
734     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
735     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
736     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
737     sp_file_open(utf8_fn, NULL);
738     g_free(utf8_fn);
739     g_free(local_fn);
740     g_free(uri);
743 static void
744 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
746     sp_file_new(uri);
749 void
750 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
752     std::list<gchar *> sources;
753     sources.push_back( profile_path("templates") ); // first try user's local dir
754     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
756     // Use this loop to iterate through a list of possible document locations.
757     while (!sources.empty()) {
758         gchar *dirname = sources.front();
760         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
761             GError *err = 0;
762             GDir *dir = g_dir_open(dirname, 0, &err);
764             if (dir) {
765                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
766                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
767                         continue; // skip non-svg files
769                     gchar *basename = g_path_get_basename(file);
770                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
771                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
773                     gchar const *filepath = g_build_filename(dirname, file, NULL);
774                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
775                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
776                     g_free(dupfile);
777                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
778                     g_free(filename);
780                     gtk_widget_show(item);
781                     // how does "filepath" ever get freed?
782                     g_signal_connect(G_OBJECT(item),
783                                      "activate",
784                                      G_CALLBACK(sp_file_new_from_template),
785                                      (gpointer) filepath);
787                     if (view) {
788                         // set null tip for now; later use a description from the template file
789                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
790                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
791                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
792                     }
794                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
795                 }
796                 g_dir_close(dir);
797             }
798         }
800         // toss the dirname
801         g_free(dirname);
802         sources.pop_front();
803     }
806 void
807 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
809     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
810     //                                       checkitem_toggled, checkitem_update, 0);
811     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
812                                            checkitem_toggled, checkitem_update, 0);
813     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
814                                            checkitem_toggled, checkitem_update, 0);
815     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
816                                            checkitem_toggled, checkitem_update, 0);
817     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
818                                            checkitem_toggled, checkitem_update, 0);
819     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
820                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
821     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
822                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
823     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
824                                            checkitem_toggled, checkitem_update, 0);
825     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
826                                            checkitem_toggled, checkitem_update, 0);
830 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
832     gchar const* data[] = {
833         _("Default"), _("Default interface setup"),
834         _("Custom"), _("Set the custom task"),
835         _("Wide"), _("Setup for widescreen work"),
836         0, 0
837     };
839     GSList *group = 0;
840     int count = 0;
841     gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
842     for (gchar const **strs = data; strs[0]; strs += 2, count++)
843     {
844         GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
845         group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
846         if ( count == active )
847         {
848             gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
849         }
851         g_object_set_data( G_OBJECT(item), "view", view );
852         g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
853         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
854         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
856         gtk_widget_show( item );
857         gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
858     }
862 /** @brief Observer that updates the recent list's max document count */
863 class MaxRecentObserver : public Inkscape::Preferences::Observer {
864 public:
865     MaxRecentObserver(GtkWidget *recent_menu) :
866         Observer("/options/maxrecentdocuments/value"),
867         _rm(recent_menu)
868     {}
869     virtual void notify(Inkscape::Preferences::Entry const &e) {
870         gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
871         // hack: the recent menu doesn't repopulate after changing the limit, so we force it
872         g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
873     }
874 private:
875     GtkWidget *_rm;
876 };
878 /** \brief  This function turns XML into a menu
879     \param  menus  This is the XML that defines the menu
880     \param  menu   Menu to be added to
881     \param  view   The View that this menu is being built for
883     This function is realitively simple as it just goes through the XML
884     and parses the individual elements.  In the case of a submenu, it
885     just calls itself recursively.  Because it is only reasonable to have
886     a couple of submenus, it is unlikely this will go more than two or
887     three times.
889     In the case of an unrecognized verb, a menu item is made to identify
890     the verb that is missing, and display that.  The menu item is also made
891     insensitive.
892 */
893 void
894 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
896     if (menus == NULL) return;
897     if (menu == NULL)  return;
898     GSList *group = NULL;
900     for (Inkscape::XML::Node *menu_pntr = menus;
901          menu_pntr != NULL;
902          menu_pntr = menu_pntr->next()) {
903         if (!strcmp(menu_pntr->name(), "submenu")) {
904             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
905             GtkWidget *submenu = gtk_menu_new();
906             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
907             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
908             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
909             continue;
910         }
911         if (!strcmp(menu_pntr->name(), "verb")) {
912             gchar const *verb_name = menu_pntr->attribute("verb-id");
913             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
915             if (verb != NULL) {
916                 if (menu_pntr->attribute("radio") != NULL) {
917                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
918                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
919                     if (menu_pntr->attribute("default") != NULL) {
920                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
921                     }
922                     if (verb->get_code() != SP_VERB_NONE) {
923                         SPAction *action = verb->get_action(view);
924                         g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
925                     }
926                 } else {
927                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
928                     group = NULL;
929                 }
930             } else {
931                 gchar string[120];
932                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
933                 string[119] = '\0'; /* may not be terminated */
934                 GtkWidget *item = gtk_menu_item_new_with_label(string);
935                 gtk_widget_set_sensitive(item, false);
936                 gtk_widget_show(item);
937                 gtk_menu_append(GTK_MENU(menu), item);
938             }
939             continue;
940         }
941         if (!strcmp(menu_pntr->name(), "separator")
942                 // This was spelt wrong in the original version
943                 // and so this is for backward compatibility.  It can
944                 // probably be dropped after the 0.44 release.
945              || !strcmp(menu_pntr->name(), "seperator")) {
946             GtkWidget *item = gtk_separator_menu_item_new();
947             gtk_widget_show(item);
948             gtk_menu_append(GTK_MENU(menu), item);
949             continue;
950         }
951         if (!strcmp(menu_pntr->name(), "template-list")) {
952             sp_menu_append_new_templates(menu, view);
953             continue;
954         }
955         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
956             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
958             // create recent files menu
959             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
960             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
961             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
962             // sort most recently used documents first to preserve previous behavior
963             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
964             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
966             // add filter to only open files added by Inkscape
967             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
968             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
969             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
971             gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
972             gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
974             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
975             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
977             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
978             // this will just sit and update the list's item count
979             static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
980             prefs->addObserver(*mro);
981             continue;
982         }
983         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
984             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
985             continue;
986         }
987         if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
988             addTaskMenuItems(GTK_MENU(menu), view);
989             continue;
990         }
991     }
994 /** \brief  Build the main tool bar
995     \param  view  View to build the bar for
997     Currently the main tool bar is built as a dynamic XML menu using
998     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
999     pass it to get items attached to it.
1000 */
1001 GtkWidget *
1002 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1004     GtkWidget *mbar = gtk_menu_bar_new();
1006 #ifdef GDK_WINDOWING_QUARTZ
1007     ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1008 #endif
1010     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1012 #ifdef GDK_WINDOWING_QUARTZ
1013     return NULL;
1014 #else
1015     return mbar;
1016 #endif
1019 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1020     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1023 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1024     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1025     sp_desktop_selection(desktop)->clear();
1028 GtkWidget *
1029 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1031     GtkWidget *m;
1032     SPDesktop *dt;
1034     dt = static_cast<SPDesktop*>(view);
1036     m = gtk_menu_new();
1038     /* Undo and Redo */
1039     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1040     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1042     /* Separator */
1043     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1045     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1046     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1047     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1049     /* Separator */
1050     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1052     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1053     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1055     /* Item menu */
1056     if (item) {
1057         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1058         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1059     }
1061     /* layer menu */
1062     SPGroup *group=NULL;
1063     if (item) {
1064         if (SP_IS_GROUP(item)) {
1065             group = SP_GROUP(item);
1066         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1067             group = SP_GROUP(SP_OBJECT_PARENT(item));
1068         }
1069     }
1071     if (( group && group != dt->currentLayer() ) ||
1072         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1073         /* Separator */
1074         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1075     }
1077     if ( group && group != dt->currentLayer() ) {
1078         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1079         gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1080         GtkWidget *w = gtk_menu_item_new_with_label(label);
1081         g_free(label);
1082         g_object_set_data(G_OBJECT(w), "group", group);
1083         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1084         gtk_widget_show(w);
1085         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1086     }
1088     if ( dt->currentLayer() != dt->currentRoot() ) {
1089         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1090             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1091             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1092             gtk_widget_show(w);
1093             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1095         }
1096     }
1098     return m;
1101 /* Drag and Drop */
1102 void
1103 sp_ui_drag_data_received(GtkWidget *widget,
1104                          GdkDragContext *drag_context,
1105                          gint x, gint y,
1106                          GtkSelectionData *data,
1107                          guint info,
1108                          guint /*event_time*/,
1109                          gpointer /*user_data*/)
1111     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1112     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1114     switch (info) {
1115 #if ENABLE_MAGIC_COLORS
1116         case APP_X_INKY_COLOR:
1117         {
1118             int destX = 0;
1119             int destY = 0;
1120             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1121             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1123             SPItem *item = desktop->item_at_point( where, true );
1124             if ( item )
1125             {
1126                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1128                 if ( data->length >= 8 ) {
1129                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1131                     gchar c[64] = {0};
1132                     // Careful about endian issues.
1133                     guint16* dataVals = (guint16*)data->data;
1134                     sp_svg_write_color( c, sizeof(c),
1135                                         SP_RGBA32_U_COMPOSE(
1136                                             0x0ff & (dataVals[0] >> 8),
1137                                             0x0ff & (dataVals[1] >> 8),
1138                                             0x0ff & (dataVals[2] >> 8),
1139                                             0xff // can't have transparency in the color itself
1140                                             //0x0ff & (data->data[3] >> 8),
1141                                             ));
1142                     SPCSSAttr *css = sp_repr_css_attr_new();
1143                     bool updatePerformed = false;
1145                     if ( data->length > 14 ) {
1146                         int flags = dataVals[4];
1148                         // piggie-backed palette entry info
1149                         int index = dataVals[5];
1150                         Glib::ustring palName;
1151                         for ( int i = 0; i < dataVals[6]; i++ ) {
1152                             palName += (gunichar)dataVals[7+i];
1153                         }
1155                         // Now hook in a magic tag of some sort.
1156                         if ( !palName.empty() && (flags & 1) ) {
1157                             gchar* str = g_strdup_printf("%d|", index);
1158                             palName.insert( 0, str );
1159                             g_free(str);
1160                             str = 0;
1162                             sp_object_setAttribute( SP_OBJECT(item),
1163                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1164                                                     palName.c_str(),
1165                                                     false );
1166                             item->updateRepr();
1168                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1169                             updatePerformed = true;
1170                         }
1171                     }
1173                     if ( !updatePerformed ) {
1174                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1175                     }
1177                     sp_desktop_apply_css_recursive( item, css, true );
1178                     item->updateRepr();
1180                     sp_document_done( doc , SP_VERB_NONE,
1181                                       _("Drop color"));
1183                     if ( srgbProf ) {
1184                         cmsCloseProfile( srgbProf );
1185                     }
1186                 }
1187             }
1188         }
1189         break;
1190 #endif // ENABLE_MAGIC_COLORS
1192         case APP_X_COLOR:
1193         {
1194             int destX = 0;
1195             int destY = 0;
1196             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1197             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1198             Geom::Point const button_dt(desktop->w2d(where));
1199             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1201             if ( data->length == 8 ) {
1202                 gchar colorspec[64] = {0};
1203                 // Careful about endian issues.
1204                 guint16* dataVals = (guint16*)data->data;
1205                 sp_svg_write_color( colorspec, sizeof(colorspec),
1206                                     SP_RGBA32_U_COMPOSE(
1207                                         0x0ff & (dataVals[0] >> 8),
1208                                         0x0ff & (dataVals[1] >> 8),
1209                                         0x0ff & (dataVals[2] >> 8),
1210                                         0xff // can't have transparency in the color itself
1211                                         //0x0ff & (data->data[3] >> 8),
1212                                         ));
1214                 SPItem *item = desktop->item_at_point( where, true );
1216                 bool consumed = false;
1217                 if (desktop->event_context && desktop->event_context->get_drag()) {
1218                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1219                     if (consumed) {
1220                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1221                         desktop->event_context->get_drag()->updateDraggers();
1222                     }
1223                 }
1225                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1226                 //    consumed = sp_text_context_drop_color(c, button_doc);
1227                 //    if (consumed) {
1228                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1229                 //    }
1230                 //}
1232                 if (!consumed && item) {
1233                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1234                     if (fillnotstroke &&
1235                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1236                         Path *livarot_path = Path_for_item(item, true, true);
1237                         livarot_path->ConvertWithBackData(0.04);
1239                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1240                         if (position) {
1241                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1242                             Geom::Point delta = nearest - button_doc;
1243                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1244                             delta = desktop->d2w(delta);
1245                             double stroke_tolerance =
1246                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1247                                   desktop->current_zoom() *
1248                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1249                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1250                                   : 0.0)
1251                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1253                             if (Geom::L2 (delta) < stroke_tolerance) {
1254                                 fillnotstroke = false;
1255                             }
1256                         }
1257                         delete livarot_path;
1258                     }
1260                     SPCSSAttr *css = sp_repr_css_attr_new();
1261                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1263                     sp_desktop_apply_css_recursive( item, css, true );
1264                     item->updateRepr();
1266                     sp_document_done( doc , SP_VERB_NONE,
1267                                       _("Drop color"));
1268                 }
1269             }
1270         }
1271         break;
1273         case APP_OSWB_COLOR:
1274         {
1275             bool worked = false;
1276             Glib::ustring colorspec;
1277             if ( data->format == 8 ) {
1278                 ege::PaintDef color;
1279                 worked = color.fromMIMEData("application/x-oswb-color",
1280                                             reinterpret_cast<char*>(data->data),
1281                                             data->length,
1282                                             data->format);
1283                 if ( worked ) {
1284                     if ( color.getType() == ege::PaintDef::CLEAR ) {
1285                         colorspec = ""; // TODO check if this is sufficient
1286                     } else if ( color.getType() == ege::PaintDef::NONE ) {
1287                         colorspec = "none";
1288                     } else {
1289                         unsigned int r = color.getR();
1290                         unsigned int g = color.getG();
1291                         unsigned int b = color.getB();
1293                         SPGradient* matches = 0;
1294                         const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
1295                         for (const GSList *item = gradients; item; item = item->next) {
1296                             SPGradient* grad = SP_GRADIENT(item->data);
1297                             if ( color.descr == grad->getId() ) {
1298                                 if ( grad->hasStops() ) {
1299                                     matches = grad;
1300                                     break;
1301                                 }
1302                             }
1303                         }
1304                         if (matches) {
1305                             colorspec = "url(#";
1306                             colorspec += matches->getId();
1307                             colorspec += ")";
1308                         } else {
1309                             gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1310                             colorspec = tmp;
1311                             g_free(tmp);
1312                         }
1313                     }
1314                 }
1315             }
1316             if ( worked ) {
1317                 int destX = 0;
1318                 int destY = 0;
1319                 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1320                 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1321                 Geom::Point const button_dt(desktop->w2d(where));
1322                 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1324                 SPItem *item = desktop->item_at_point( where, true );
1326                 bool consumed = false;
1327                 if (desktop->event_context && desktop->event_context->get_drag()) {
1328                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1329                     if (consumed) {
1330                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1331                         desktop->event_context->get_drag()->updateDraggers();
1332                     }
1333                 }
1335                 if (!consumed && item) {
1336                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1337                     if (fillnotstroke &&
1338                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1339                         Path *livarot_path = Path_for_item(item, true, true);
1340                         livarot_path->ConvertWithBackData(0.04);
1342                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1343                         if (position) {
1344                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1345                             Geom::Point delta = nearest - button_doc;
1346                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1347                             delta = desktop->d2w(delta);
1348                             double stroke_tolerance =
1349                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1350                                   desktop->current_zoom() *
1351                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1352                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1353                                   : 0.0)
1354                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1356                             if (Geom::L2 (delta) < stroke_tolerance) {
1357                                 fillnotstroke = false;
1358                             }
1359                         }
1360                         delete livarot_path;
1361                     }
1363                     SPCSSAttr *css = sp_repr_css_attr_new();
1364                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1366                     sp_desktop_apply_css_recursive( item, css, true );
1367                     item->updateRepr();
1369                     sp_document_done( doc , SP_VERB_NONE,
1370                                       _("Drop color"));
1371                 }
1372             }
1373         }
1374         break;
1376         case SVG_DATA:
1377         case SVG_XML_DATA: {
1378             gchar *svgdata = (gchar *)data->data;
1380             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1382             if (rnewdoc == NULL) {
1383                 sp_ui_error_dialog(_("Could not parse SVG data"));
1384                 return;
1385             }
1387             Inkscape::XML::Node *repr = rnewdoc->root();
1388             gchar const *style = repr->attribute("style");
1390             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1391             newgroup->setAttribute("style", style);
1393             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1394             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1395                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1396                 newgroup->appendChild(newchild);
1397             }
1399             Inkscape::GC::release(rnewdoc);
1401             // Add it to the current layer
1403             // Greg's edits to add intelligent positioning of svg drops
1404             SPObject *new_obj = NULL;
1405             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1407             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1408             selection->set(SP_ITEM(new_obj));
1410             // move to mouse pointer
1411             {
1412                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1413                 Geom::OptRect sel_bbox = selection->bounds();
1414                 if (sel_bbox) {
1415                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1416                     sp_selection_move_relative(selection, m, false);
1417                 }
1418             }
1420             Inkscape::GC::release(newgroup);
1421             sp_document_done(doc, SP_VERB_NONE,
1422                              _("Drop SVG"));
1423             break;
1424         }
1426         case URI_LIST: {
1427             gchar *uri = (gchar *)data->data;
1428             sp_ui_import_files(uri);
1429             break;
1430         }
1432         case PNG_DATA:
1433         case JPEG_DATA:
1434         case IMAGE_DATA: {
1435             const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1437             Inkscape::Extension::DB::InputList o;
1438             Inkscape::Extension::db.get_input_list(o);
1439             Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1440             while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1441                 ++i;
1442             }
1443             Inkscape::Extension::Extension *ext = *i;
1444             bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1445             ext->set_param_optiongroup("link", "embed");
1446             ext->set_gui(false);
1448             gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1449             g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1450             file_import(doc, filename, ext);
1451             g_free(filename);
1453             ext->set_param_optiongroup("link", save ? "embed" : "link");
1454             ext->set_gui(true);
1455             sp_document_done( doc , SP_VERB_NONE,
1456                               _("Drop bitmap image"));
1457             break;
1458         }
1459     }
1462 #include "gradient-context.h"
1464 void sp_ui_drag_motion( GtkWidget */*widget*/,
1465                         GdkDragContext */*drag_context*/,
1466                         gint /*x*/, gint /*y*/,
1467                         GtkSelectionData */*data*/,
1468                         guint /*info*/,
1469                         guint /*event_time*/,
1470                         gpointer /*user_data*/)
1472 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1473 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1476 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1479 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1480                               GdkDragContext */*drag_context*/,
1481                               guint /*event_time*/,
1482                               gpointer /*user_data*/ )
1484 //     g_message("drag-n-drop leave                at %d", event_time);
1487 static void
1488 sp_ui_import_files(gchar *buffer)
1490     GList *list = gnome_uri_list_extract_filenames(buffer);
1491     if (!list)
1492         return;
1493     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1494     g_list_foreach(list, (GFunc) g_free, NULL);
1495     g_list_free(list);
1498 static void
1499 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1501     if (filename) {
1502         if (strlen((char const *)filename) > 2)
1503             sp_ui_import_one_file((char const *)filename);
1504     }
1507 static void
1508 sp_ui_import_one_file(char const *filename)
1510     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1511     if (!doc) return;
1513     if (filename == NULL) return;
1515     // Pass off to common implementation
1516     // TODO might need to get the proper type of Inkscape::Extension::Extension
1517     file_import( doc, filename, NULL );
1520 void
1521 sp_ui_error_dialog(gchar const *message)
1523     GtkWidget *dlg;
1524     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1526     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1527                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1528     sp_transientize(dlg);
1529     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1530     gtk_dialog_run(GTK_DIALOG(dlg));
1531     gtk_widget_destroy(dlg);
1532     g_free(safeMsg);
1535 bool
1536 sp_ui_overwrite_file(gchar const *filename)
1538     bool return_value = FALSE;
1540     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1541         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1542         gchar* baseName = g_path_get_basename( filename );
1543         gchar* dirName = g_path_get_dirname( filename );
1544         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1545                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1546                                                                 GTK_MESSAGE_QUESTION,
1547                                                                 GTK_BUTTONS_NONE,
1548                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1549                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1550                                                                 baseName,
1551                                                                 dirName
1552             );
1553         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1554                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1555                                 _("Replace"), GTK_RESPONSE_YES,
1556                                 NULL );
1557         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1559         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1560             return_value = TRUE;
1561         } else {
1562             return_value = FALSE;
1563         }
1564         gtk_widget_destroy(dialog);
1565         g_free( baseName );
1566         g_free( dirName );
1567     } else {
1568         return_value = TRUE;
1569     }
1571     return return_value;
1574 static void
1575 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1577     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1580 static void
1581 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1583     void *child = GTK_BIN (data)->child;
1584     //child is either
1585     //- a GtkHBox, whose first child is a label displaying name if the menu
1586     //item has an accel key
1587     //- a GtkLabel if the menu has no accel key
1588     if (GTK_IS_LABEL(child)) {
1589         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1590     } else if (GTK_IS_HBOX(child)) {
1591         gtk_label_set_markup_with_mnemonic(
1592         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1593         name.c_str());
1594     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1595     //a menu item in yet another way...
1599 /*
1600   Local Variables:
1601   mode:c++
1602   c-file-style:"stroustrup"
1603   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1604   indent-tabs-mode:nil
1605   fill-column:99
1606   End:
1607 */
1608 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :