Code

Merge and cleanup of GSoC C++-ification project.
[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) 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 using Inkscape::DocumentUndo;
78 /* Drag and Drop */
79 typedef enum {
80     URI_LIST,
81     SVG_XML_DATA,
82     SVG_DATA,
83     PNG_DATA,
84     JPEG_DATA,
85     IMAGE_DATA,
86     APP_X_INKY_COLOR,
87     APP_X_COLOR,
88     APP_OSWB_COLOR,
89 } ui_drop_target_info;
91 static GtkTargetEntry ui_drop_target_entries [] = {
92     {(gchar *)"text/uri-list",                0, URI_LIST        },
93     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
94     {(gchar *)"image/svg",                    0, SVG_DATA        },
95     {(gchar *)"image/png",                    0, PNG_DATA        },
96     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
97 #if ENABLE_MAGIC_COLORS
98     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
99 #endif // ENABLE_MAGIC_COLORS
100     {(gchar *)"application/x-oswb-color",     0, APP_OSWB_COLOR  },
101     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
102 };
104 static GtkTargetEntry *completeDropTargets = 0;
105 static int completeDropTargetsCount = 0;
106 static bool temporarily_block_actions = false;
108 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
109 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
110 static void sp_ui_import_files(gchar *buffer);
111 static void sp_ui_import_one_file(char const *filename);
112 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
113 static void sp_ui_drag_data_received(GtkWidget *widget,
114                                      GdkDragContext *drag_context,
115                                      gint x, gint y,
116                                      GtkSelectionData *data,
117                                      guint info,
118                                      guint event_time,
119                                      gpointer user_data);
120 static void sp_ui_drag_motion( GtkWidget *widget,
121                                GdkDragContext *drag_context,
122                                gint x, gint y,
123                                GtkSelectionData *data,
124                                guint info,
125                                guint event_time,
126                                gpointer user_data );
127 static void sp_ui_drag_leave( GtkWidget *widget,
128                               GdkDragContext *drag_context,
129                               guint event_time,
130                               gpointer user_data );
131 static void sp_ui_menu_item_set_sensitive(SPAction *action,
132                                           unsigned int sensitive,
133                                           void *data);
134 static void sp_ui_menu_item_set_name(SPAction *action,
135                                      Glib::ustring name,
136                                      void *data);
137 static void sp_recent_open(GtkRecentChooser *, gpointer);
139 static void injectRenamedIcons();
141 SPActionEventVector menu_item_event_vector = {
142     {NULL},
143     NULL,
144     NULL, /* set_active */
145     sp_ui_menu_item_set_sensitive, /* set_sensitive */
146     NULL, /* set_shortcut */
147     sp_ui_menu_item_set_name /* set_name */
148 };
150 static const int MIN_ONSCREEN_DISTANCE = 50;
152 void
153 sp_create_window(SPViewWidget *vw, gboolean editable)
155     g_return_if_fail(vw != NULL);
156     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
158     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
160     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
161     gtk_widget_show(GTK_WIDGET(vw));
163     if (editable) {
164         g_object_set_data(G_OBJECT(vw), "window", win);
166         SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
167         SPDesktop* desktop = desktop_widget->desktop;
169         desktop_widget->window = win;
171         win->set_data("desktop", desktop);
172         win->set_data("desktopwidget", desktop_widget);
174         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
175         win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
176         win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
178         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
179         gint prefs_geometry =
180             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
181         if (prefs_geometry) {
182             gint pw = prefs->getInt("/desktop/geometry/width", -1);
183             gint ph = prefs->getInt("/desktop/geometry/height", -1);
184             gint px = prefs->getInt("/desktop/geometry/x", -1);
185             gint py = prefs->getInt("/desktop/geometry/y", -1);
186             gint full = prefs->getBool("/desktop/geometry/fullscreen");
187             gint maxed = prefs->getBool("/desktop/geometry/maximized");
188             if (pw>0 && ph>0) {
189                 gint w = MIN(gdk_screen_width(), pw);
190                 gint h = MIN(gdk_screen_height(), ph);
191                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
192                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
193                 if (w>0 && h>0) {
194                     x = MIN(gdk_screen_width() - w, x);
195                     y = MIN(gdk_screen_height() - h, y);
196                     desktop->setWindowSize(w, h);
197                 }
199                 // Only restore xy for the first window so subsequent windows don't overlap exactly
200                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
201                 // other desktops?)
203                 // Empirically it seems that active_desktop==this desktop only the first time a
204                 // desktop is created.
205                 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
206                 if (active_desktop == desktop || active_desktop==NULL) {
207                     desktop->setWindowPosition(Geom::Point(x, y));
208                 }
209             }
210             if (maxed) {
211                 win->maximize();
212             }
213             if (full) {
214                 win->fullscreen();
215             }
216         }
218     } else {
219         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
220     }
222     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
223     {
224         std::vector<gchar*> types;
226         GSList *list = gdk_pixbuf_get_formats();
227         while ( list ) {
228             int i = 0;
229             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
230             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
231             for ( i = 0; typesXX[i]; i++ ) {
232                 types.push_back(g_strdup(typesXX[i]));
233             }
234             g_strfreev(typesXX);
236             list = g_slist_next(list);
237         }
238         completeDropTargetsCount = nui_drop_target_entries + types.size();
239         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
240         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
241             completeDropTargets[i] = ui_drop_target_entries[i];
242         }
243         int pos = nui_drop_target_entries;
245         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
246             completeDropTargets[pos].target = *it;
247             completeDropTargets[pos].flags = 0;
248             completeDropTargets[pos].info = IMAGE_DATA;
249             pos++;
250         }
251     }
253     gtk_drag_dest_set((GtkWidget*)win->gobj(),
254                       GTK_DEST_DEFAULT_ALL,
255                       completeDropTargets,
256                       completeDropTargetsCount,
257                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
260     g_signal_connect(G_OBJECT(win->gobj()),
261                      "drag_data_received",
262                      G_CALLBACK(sp_ui_drag_data_received),
263                      NULL);
264     g_signal_connect(G_OBJECT(win->gobj()),
265                      "drag_motion",
266                      G_CALLBACK(sp_ui_drag_motion),
267                      NULL);
268     g_signal_connect(G_OBJECT(win->gobj()),
269                      "drag_leave",
270                      G_CALLBACK(sp_ui_drag_leave),
271                      NULL);
272     win->show();
274     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
275     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
278 void
279 sp_ui_new_view()
281     SPDocument *document;
282     SPViewWidget *dtw;
284     document = SP_ACTIVE_DOCUMENT;
285     if (!document) return;
287     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
288     g_return_if_fail(dtw != NULL);
290     sp_create_window(dtw, TRUE);
291     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
292     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
295 /* TODO: not yet working */
296 /* To be re-enabled (by adding to menu) once it works. */
297 void
298 sp_ui_new_view_preview()
300     SPDocument *document;
301     SPViewWidget *dtw;
303     document = SP_ACTIVE_DOCUMENT;
304     if (!document) return;
306     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
307     g_return_if_fail(dtw != NULL);
308     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
310     sp_create_window(dtw, FALSE);
313 /**
314  * \param widget unused
315  */
316 void
317 sp_ui_close_view(GtkWidget */*widget*/)
319         SPDesktop *dt = SP_ACTIVE_DESKTOP;
321         if (dt == NULL) {
322         return;
323     }
325     if (dt->shutdown()) {
326         return; // Shutdown operation has been canceled, so do nothing
327     }
329     // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
330     // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
331     dt->destroyWidget();
335 /**
336  *  sp_ui_close_all
337  *
338  *  This function is called to exit the program, and iterates through all
339  *  open document view windows, attempting to close each in turn.  If the
340  *  view has unsaved information, the user will be prompted to save,
341  *  discard, or cancel.
342  *
343  *  Returns FALSE if the user cancels the close_all operation, TRUE
344  *  otherwise.
345  */
346 unsigned int
347 sp_ui_close_all(void)
349     /* Iterate through all the windows, destroying each in the order they
350        become active */
351     while (SP_ACTIVE_DESKTOP) {
352         SPDesktop *dt = SP_ACTIVE_DESKTOP;
353         if (dt->shutdown()) {
354             /* The user canceled the operation, so end doing the close */
355             return FALSE;
356         }
357         // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
358         // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
359         dt->destroyWidget();
360     }
362     return TRUE;
365 /*
366  * Some day when the right-click menus are ready to start working
367  * smarter with the verbs, we'll need to change this NULL being
368  * sent to sp_action_perform to something useful, or set some kind
369  * of global "right-clicked position" variable for actions to
370  * investigate when they're called.
371  */
372 static void
373 sp_ui_menu_activate(void */*object*/, SPAction *action)
375     if (!temporarily_block_actions) {
376         sp_action_perform(action, NULL);
377     }
380 static void
381 sp_ui_menu_select_action(void */*object*/, SPAction *action)
383     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
386 static void
387 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
389     action->view->tipsMessageContext()->clear();
392 static void
393 sp_ui_menu_select(gpointer object, gpointer tip)
395     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
396     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
399 static void
400 sp_ui_menu_deselect(gpointer object)
402     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
403     view->tipsMessageContext()->clear();
406 /**
407  * sp_ui_menuitem_add_icon
408  *
409  * Creates and attaches a scaled icon to the given menu item.
410  *
411  */
412 void
413 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
415     static bool iconsInjected = false;
416     if ( !iconsInjected ) {
417         iconsInjected = true;
418         injectRenamedIcons();
419     }
420     GtkWidget *icon;
422     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
423     gtk_widget_show(icon);
424     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
425 } // end of sp_ui_menu_add_icon
427 /**
428  * sp_ui_menu_append_item
429  *
430  * Appends a UI item with specific info for Inkscape/Sodipodi.
431  *
432  */
433 static GtkWidget *
434 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
435                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
436                         gpointer data, gboolean with_mnemonic = TRUE )
438     GtkWidget *item;
440     if (stock) {
441         item = gtk_image_menu_item_new_from_stock(stock, NULL);
442     } else if (label) {
443         item = (with_mnemonic)
444             ? gtk_image_menu_item_new_with_mnemonic(label) :
445             gtk_image_menu_item_new_with_label(label);
446     } else {
447         item = gtk_separator_menu_item_new();
448     }
450     gtk_widget_show(item);
452     if (callback) {
453         g_signal_connect(G_OBJECT(item), "activate", callback, data);
454     }
456     if (tip && view) {
457         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
458         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
459         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
460     }
462     gtk_menu_append(GTK_MENU(menu), item);
464     return item;
466 } // end of sp_ui_menu_append_item()
468 void
469 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
471     SPAction     *action;
472     unsigned int shortcut;
473     gchar        *s;
474     gchar        *atitle;
476     action = verb->get_action(NULL);
477     if (!action)
478         return;
480     atitle = sp_action_get_title(action);
482     s = g_stpcpy(c, atitle);
484     g_free(atitle);
486     shortcut = sp_shortcut_get_primary(verb);
487     if (shortcut!=GDK_VoidSymbol) {
488         gchar* key = sp_shortcut_get_label(shortcut);
489         s = g_stpcpy(s, " (");
490         s = g_stpcpy(s, key);
491         s = g_stpcpy(s, ")");
492         g_free(key);
493     }
497 /**
498  * sp_ui_menu_append_item_from_verb
499  *
500  * Appends a custom menu UI from a verb.
501  *
502  */
504 static GtkWidget *
505 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
507     SPAction *action;
508     GtkWidget *item;
510     if (verb->get_code() == SP_VERB_NONE) {
512         item = gtk_separator_menu_item_new();
514     } else {
515         unsigned int shortcut;
517         action = verb->get_action(view);
519         if (!action) return NULL;
521         shortcut = sp_shortcut_get_primary(verb);
522         if (shortcut!=GDK_VoidSymbol) {
523             gchar* c = sp_shortcut_get_label(shortcut);
524             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
525             GtkWidget *const name_lbl = gtk_label_new("");
526             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
527             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
528             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
529             GtkWidget *const accel_lbl = gtk_label_new(c);
530             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
531             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
532             gtk_widget_show_all(hb);
533             if (radio) {
534                 item = gtk_radio_menu_item_new (group);
535             } else {
536                 item = gtk_image_menu_item_new();
537             }
538             gtk_container_add((GtkContainer *) item, hb);
539             g_free(c);
540         } else {
541             if (radio) {
542                 item = gtk_radio_menu_item_new (group);
543             } else {
544                 item = gtk_image_menu_item_new ();
545             }
546             GtkWidget *const name_lbl = gtk_label_new("");
547             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
548             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
549             gtk_container_add((GtkContainer *) item, name_lbl);
550         }
552         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
553         if (!action->sensitive) {
554             gtk_widget_set_sensitive(item, FALSE);
555         }
557         if (action->image) {
558             sp_ui_menuitem_add_icon(item, action->image);
559         }
560         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
561         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
562         g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
563         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
564         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
565     }
567     gtk_widget_show(item);
568     gtk_menu_append(GTK_MENU(menu), item);
570     return item;
572 } // end of sp_ui_menu_append_item_from_verb
575 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
577     Glib::ustring prefPath;
579     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
580         prefPath = "/focus/";
581     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
582         prefPath = "/fullscreen/";
583     } else {
584         prefPath = "/window/";
585     }
587     return prefPath;
590 static void
591 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
593     gchar const *pref = (gchar const *) user_data;
594     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
596     Glib::ustring pref_path = getLayoutPrefPath( view );
597     pref_path += pref;
598     pref_path += "/state";
600     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
601     gboolean checked = gtk_check_menu_item_get_active(menuitem);
602     prefs->setBool(pref_path, checked);
604     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
607 static gboolean
608 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
610     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
612     gchar const *pref = (gchar const *) user_data;
613     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
615     Glib::ustring pref_path = getLayoutPrefPath( view );
616     pref_path += pref;
617     pref_path += "/state";
619     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
620     bool ison = prefs->getBool(pref_path, true);
622     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
623     gtk_check_menu_item_set_active(menuitem, ison);
624     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
626     return FALSE;
629 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
631     if ( gtk_check_menu_item_get_active(menuitem) ) {
632         gint taskNum = GPOINTER_TO_INT(userData);
633         taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
635         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
637         // note: this will change once more options are in the task set support:
638         Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
639     }
643 /**
644  *  \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
645  */
647 static gboolean
648 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
650         SPAction *action = (SPAction *) user_data;
651         g_assert(action->id != NULL);
653         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
654     SPDesktop *dt = static_cast<SPDesktop*>(view);
655         Inkscape::RenderMode mode = dt->getMode();
657         bool new_state = false;
658         if (!strcmp(action->id, "ViewModeNormal")) {
659         new_state = mode == Inkscape::RENDERMODE_NORMAL;
660         } else if (!strcmp(action->id, "ViewModeNoFilters")) {
661         new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
662     } else if (!strcmp(action->id, "ViewModeOutline")) {
663         new_state = mode == Inkscape::RENDERMODE_OUTLINE;
664     } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
665         new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
666     } else {
667         g_warning("update_view_menu does not handle this verb");
668     }
670         if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
671                 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
672                         // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
673                         // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
674                         // This emission however should not invoke any actions, hence we block it here:
675                         temporarily_block_actions = true;
676                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
677                         temporarily_block_actions = false;
678                 }
679         }
681         return FALSE;
684 void
685 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
686                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
687                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
688                                        Inkscape::Verb *verb)
690     unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
691     SPAction *action = (verb) ? verb->get_action(view) : 0;
692     GtkWidget *item = gtk_check_menu_item_new();
694     if (verb && shortcut!=GDK_VoidSymbol) {
695         gchar* c = sp_shortcut_get_label(shortcut);
697         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
699         {
700             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
701             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
702             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
703         }
705         {
706             GtkWidget *l = gtk_label_new(c);
707             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
708             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
709         }
711         gtk_widget_show_all(hb);
713         gtk_container_add((GtkContainer *) item, hb);
714         g_free(c);
715     } else {
716         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
717         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
718         gtk_container_add((GtkContainer *) item, l);
719     }
720 #if 0
721     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
722     if (!action->sensitive) {
723         gtk_widget_set_sensitive(item, FALSE);
724     }
725 #endif
726     gtk_widget_show(item);
728     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
730     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
732     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
733     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
735     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
736     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
739 static void
740 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
742     // dealing with the bizarre filename convention in Inkscape for now
743     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
744     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
745     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
746     sp_file_open(utf8_fn, NULL);
747     g_free(utf8_fn);
748     g_free(local_fn);
749     g_free(uri);
752 static void
753 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
755     sp_file_new(uri);
758 void
759 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
761     std::list<gchar *> sources;
762     sources.push_back( profile_path("templates") ); // first try user's local dir
763     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
765     // Use this loop to iterate through a list of possible document locations.
766     while (!sources.empty()) {
767         gchar *dirname = sources.front();
769         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
770             GError *err = 0;
771             GDir *dir = g_dir_open(dirname, 0, &err);
773             if (dir) {
774                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
775                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
776                         continue; // skip non-svg files
778                     gchar *basename = g_path_get_basename(file);
779                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
780                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
782                     gchar const *filepath = g_build_filename(dirname, file, NULL);
783                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
784                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
785                     g_free(dupfile);
786                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
787                     g_free(filename);
789                     gtk_widget_show(item);
790                     // how does "filepath" ever get freed?
791                     g_signal_connect(G_OBJECT(item),
792                                      "activate",
793                                      G_CALLBACK(sp_file_new_from_template),
794                                      (gpointer) filepath);
796                     if (view) {
797                         // set null tip for now; later use a description from the template file
798                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
799                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
800                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
801                     }
803                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
804                 }
805                 g_dir_close(dir);
806             }
807         }
809         // toss the dirname
810         g_free(dirname);
811         sources.pop_front();
812     }
815 void
816 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
818     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
819     //                                       checkitem_toggled, checkitem_update, 0);
820     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
821                                            checkitem_toggled, checkitem_update, 0);
822     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
823                                            checkitem_toggled, checkitem_update, 0);
824     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
825                                            checkitem_toggled, checkitem_update, 0);
826     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
827                                            checkitem_toggled, checkitem_update, 0);
828     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
829                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
830     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
831                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
832     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
833                                            checkitem_toggled, checkitem_update, 0);
834     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
835                                            checkitem_toggled, checkitem_update, 0);
839 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
841     gchar const* data[] = {
842         _("Default"), _("Default interface setup"),
843         _("Custom"), _("Set the custom task"),
844         _("Wide"), _("Setup for widescreen work"),
845         0, 0
846     };
848     GSList *group = 0;
849     int count = 0;
850     gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
851     for (gchar const **strs = data; strs[0]; strs += 2, count++)
852     {
853         GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
854         group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
855         if ( count == active )
856         {
857             gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
858         }
860         g_object_set_data( G_OBJECT(item), "view", view );
861         g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
862         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
863         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
865         gtk_widget_show( item );
866         gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
867     }
871 /** @brief Observer that updates the recent list's max document count */
872 class MaxRecentObserver : public Inkscape::Preferences::Observer {
873 public:
874     MaxRecentObserver(GtkWidget *recent_menu) :
875         Observer("/options/maxrecentdocuments/value"),
876         _rm(recent_menu)
877     {}
878     virtual void notify(Inkscape::Preferences::Entry const &e) {
879         gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
880         // hack: the recent menu doesn't repopulate after changing the limit, so we force it
881         g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
882     }
883 private:
884     GtkWidget *_rm;
885 };
887 /** \brief  This function turns XML into a menu
888     \param  menus  This is the XML that defines the menu
889     \param  menu   Menu to be added to
890     \param  view   The View that this menu is being built for
892     This function is realitively simple as it just goes through the XML
893     and parses the individual elements.  In the case of a submenu, it
894     just calls itself recursively.  Because it is only reasonable to have
895     a couple of submenus, it is unlikely this will go more than two or
896     three times.
898     In the case of an unrecognized verb, a menu item is made to identify
899     the verb that is missing, and display that.  The menu item is also made
900     insensitive.
901 */
902 void
903 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
905     if (menus == NULL) return;
906     if (menu == NULL)  return;
907     GSList *group = NULL;
909     for (Inkscape::XML::Node *menu_pntr = menus;
910          menu_pntr != NULL;
911          menu_pntr = menu_pntr->next()) {
912         if (!strcmp(menu_pntr->name(), "submenu")) {
913             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
914             GtkWidget *submenu = gtk_menu_new();
915             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
916             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
917             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
918             continue;
919         }
920         if (!strcmp(menu_pntr->name(), "verb")) {
921             gchar const *verb_name = menu_pntr->attribute("verb-id");
922             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
924             if (verb != NULL) {
925                 if (menu_pntr->attribute("radio") != NULL) {
926                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
927                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
928                     if (menu_pntr->attribute("default") != NULL) {
929                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
930                     }
931                     if (verb->get_code() != SP_VERB_NONE) {
932                         SPAction *action = verb->get_action(view);
933                         g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
934                     }
935                 } else {
936                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
937                     group = NULL;
938                 }
939             } else {
940                 gchar string[120];
941                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
942                 string[119] = '\0'; /* may not be terminated */
943                 GtkWidget *item = gtk_menu_item_new_with_label(string);
944                 gtk_widget_set_sensitive(item, false);
945                 gtk_widget_show(item);
946                 gtk_menu_append(GTK_MENU(menu), item);
947             }
948             continue;
949         }
950         if (!strcmp(menu_pntr->name(), "separator")
951                 // This was spelt wrong in the original version
952                 // and so this is for backward compatibility.  It can
953                 // probably be dropped after the 0.44 release.
954              || !strcmp(menu_pntr->name(), "seperator")) {
955             GtkWidget *item = gtk_separator_menu_item_new();
956             gtk_widget_show(item);
957             gtk_menu_append(GTK_MENU(menu), item);
958             continue;
959         }
960         if (!strcmp(menu_pntr->name(), "template-list")) {
961             sp_menu_append_new_templates(menu, view);
962             continue;
963         }
964         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
965             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
967             // create recent files menu
968             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
969             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
970             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
971             // sort most recently used documents first to preserve previous behavior
972             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
973             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
975             // add filter to only open files added by Inkscape
976             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
977             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
978             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
980             gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
981             gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
983             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
984             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
986             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
987             // this will just sit and update the list's item count
988             static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
989             prefs->addObserver(*mro);
990             continue;
991         }
992         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
993             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
994             continue;
995         }
996         if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
997             addTaskMenuItems(GTK_MENU(menu), view);
998             continue;
999         }
1000     }
1003 /** \brief  Build the main tool bar
1004     \param  view  View to build the bar for
1006     Currently the main tool bar is built as a dynamic XML menu using
1007     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
1008     pass it to get items attached to it.
1009 */
1010 GtkWidget *
1011 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1013     GtkWidget *mbar = gtk_menu_bar_new();
1015 #ifdef GDK_WINDOWING_QUARTZ
1016     ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1017 #endif
1019     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1021 #ifdef GDK_WINDOWING_QUARTZ
1022     return NULL;
1023 #else
1024     return mbar;
1025 #endif
1028 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1029     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1032 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1033     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1034     sp_desktop_selection(desktop)->clear();
1037 GtkWidget *
1038 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1040     GtkWidget *m;
1041     SPDesktop *dt;
1043     dt = static_cast<SPDesktop*>(view);
1045     m = gtk_menu_new();
1047     /* Undo and Redo */
1048     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1049     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1051     /* Separator */
1052     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1054     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1055     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1056     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1058     /* Separator */
1059     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1061     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1062     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1064     /* Item menu */
1065     if (item) {
1066         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1067         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1068     }
1070     /* layer menu */
1071     SPGroup *group=NULL;
1072     if (item) {
1073         if (SP_IS_GROUP(item)) {
1074             group = SP_GROUP(item);
1075         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1076             group = SP_GROUP(SP_OBJECT_PARENT(item));
1077         }
1078     }
1080     if (( group && group != dt->currentLayer() ) ||
1081         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1082         /* Separator */
1083         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1084     }
1086     if ( group && group != dt->currentLayer() ) {
1087         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1088         gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1089         GtkWidget *w = gtk_menu_item_new_with_label(label);
1090         g_free(label);
1091         g_object_set_data(G_OBJECT(w), "group", group);
1092         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1093         gtk_widget_show(w);
1094         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1095     }
1097     if ( dt->currentLayer() != dt->currentRoot() ) {
1098         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1099             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1100             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1101             gtk_widget_show(w);
1102             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1104         }
1105     }
1107     return m;
1110 /* Drag and Drop */
1111 void
1112 sp_ui_drag_data_received(GtkWidget *widget,
1113                          GdkDragContext *drag_context,
1114                          gint x, gint y,
1115                          GtkSelectionData *data,
1116                          guint info,
1117                          guint /*event_time*/,
1118                          gpointer /*user_data*/)
1120     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1121     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1123     switch (info) {
1124 #if ENABLE_MAGIC_COLORS
1125         case APP_X_INKY_COLOR:
1126         {
1127             int destX = 0;
1128             int destY = 0;
1129             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1130             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1132             SPItem *item = desktop->getItemAtPoint( where, true );
1133             if ( item )
1134             {
1135                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1137                 if ( data->length >= 8 ) {
1138                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1140                     gchar c[64] = {0};
1141                     // Careful about endian issues.
1142                     guint16* dataVals = (guint16*)data->data;
1143                     sp_svg_write_color( c, sizeof(c),
1144                                         SP_RGBA32_U_COMPOSE(
1145                                             0x0ff & (dataVals[0] >> 8),
1146                                             0x0ff & (dataVals[1] >> 8),
1147                                             0x0ff & (dataVals[2] >> 8),
1148                                             0xff // can't have transparency in the color itself
1149                                             //0x0ff & (data->data[3] >> 8),
1150                                             ));
1151                     SPCSSAttr *css = sp_repr_css_attr_new();
1152                     bool updatePerformed = false;
1154                     if ( data->length > 14 ) {
1155                         int flags = dataVals[4];
1157                         // piggie-backed palette entry info
1158                         int index = dataVals[5];
1159                         Glib::ustring palName;
1160                         for ( int i = 0; i < dataVals[6]; i++ ) {
1161                             palName += (gunichar)dataVals[7+i];
1162                         }
1164                         // Now hook in a magic tag of some sort.
1165                         if ( !palName.empty() && (flags & 1) ) {
1166                             gchar* str = g_strdup_printf("%d|", index);
1167                             palName.insert( 0, str );
1168                             g_free(str);
1169                             str = 0;
1171                             SP_OBJECT(item)->setAttribute( 
1172                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1173                                                     palName.c_str(),
1174                                                     false );
1175                             item->updateRepr();
1177                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1178                             updatePerformed = true;
1179                         }
1180                     }
1182                     if ( !updatePerformed ) {
1183                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1184                     }
1186                     sp_desktop_apply_css_recursive( item, css, true );
1187                     item->updateRepr();
1189                     SPDocumentUndo::done( doc , SP_VERB_NONE,
1190                                       _("Drop color"));
1192                     if ( srgbProf ) {
1193                         cmsCloseProfile( srgbProf );
1194                     }
1195                 }
1196             }
1197         }
1198         break;
1199 #endif // ENABLE_MAGIC_COLORS
1201         case APP_X_COLOR:
1202         {
1203             int destX = 0;
1204             int destY = 0;
1205             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1206             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1207             Geom::Point const button_dt(desktop->w2d(where));
1208             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1210             if ( data->length == 8 ) {
1211                 gchar colorspec[64] = {0};
1212                 // Careful about endian issues.
1213                 guint16* dataVals = (guint16*)data->data;
1214                 sp_svg_write_color( colorspec, sizeof(colorspec),
1215                                     SP_RGBA32_U_COMPOSE(
1216                                         0x0ff & (dataVals[0] >> 8),
1217                                         0x0ff & (dataVals[1] >> 8),
1218                                         0x0ff & (dataVals[2] >> 8),
1219                                         0xff // can't have transparency in the color itself
1220                                         //0x0ff & (data->data[3] >> 8),
1221                                         ));
1223                 SPItem *item = desktop->getItemAtPoint( where, true );
1225                 bool consumed = false;
1226                 if (desktop->event_context && desktop->event_context->get_drag()) {
1227                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1228                     if (consumed) {
1229                         DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1230                         desktop->event_context->get_drag()->updateDraggers();
1231                     }
1232                 }
1234                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1235                 //    consumed = sp_text_context_drop_color(c, button_doc);
1236                 //    if (consumed) {
1237                 //        SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1238                 //    }
1239                 //}
1241                 if (!consumed && item) {
1242                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1243                     if (fillnotstroke &&
1244                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1245                         Path *livarot_path = Path_for_item(item, true, true);
1246                         livarot_path->ConvertWithBackData(0.04);
1248                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1249                         if (position) {
1250                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1251                             Geom::Point delta = nearest - button_doc;
1252                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1253                             delta = desktop->d2w(delta);
1254                             double stroke_tolerance =
1255                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1256                                   desktop->current_zoom() *
1257                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1258                                   to_2geom(item->i2d_affine()).descrim() * 0.5
1259                                   : 0.0)
1260                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1262                             if (Geom::L2 (delta) < stroke_tolerance) {
1263                                 fillnotstroke = false;
1264                             }
1265                         }
1266                         delete livarot_path;
1267                     }
1269                     SPCSSAttr *css = sp_repr_css_attr_new();
1270                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1272                     sp_desktop_apply_css_recursive( item, css, true );
1273                     item->updateRepr();
1275                     DocumentUndo::done( doc , SP_VERB_NONE,
1276                                         _("Drop color") );
1277                 }
1278             }
1279         }
1280         break;
1282         case APP_OSWB_COLOR:
1283         {
1284             bool worked = false;
1285             Glib::ustring colorspec;
1286             if ( data->format == 8 ) {
1287                 ege::PaintDef color;
1288                 worked = color.fromMIMEData("application/x-oswb-color",
1289                                             reinterpret_cast<char*>(data->data),
1290                                             data->length,
1291                                             data->format);
1292                 if ( worked ) {
1293                     if ( color.getType() == ege::PaintDef::CLEAR ) {
1294                         colorspec = ""; // TODO check if this is sufficient
1295                     } else if ( color.getType() == ege::PaintDef::NONE ) {
1296                         colorspec = "none";
1297                     } else {
1298                         unsigned int r = color.getR();
1299                         unsigned int g = color.getG();
1300                         unsigned int b = color.getB();
1302                         SPGradient* matches = 0;
1303                         const GSList *gradients = doc->getResourceList("gradient");
1304                         for (const GSList *item = gradients; item; item = item->next) {
1305                             SPGradient* grad = SP_GRADIENT(item->data);
1306                             if ( color.descr == grad->getId() ) {
1307                                 if ( grad->hasStops() ) {
1308                                     matches = grad;
1309                                     break;
1310                                 }
1311                             }
1312                         }
1313                         if (matches) {
1314                             colorspec = "url(#";
1315                             colorspec += matches->getId();
1316                             colorspec += ")";
1317                         } else {
1318                             gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1319                             colorspec = tmp;
1320                             g_free(tmp);
1321                         }
1322                     }
1323                 }
1324             }
1325             if ( worked ) {
1326                 int destX = 0;
1327                 int destY = 0;
1328                 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1329                 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1330                 Geom::Point const button_dt(desktop->w2d(where));
1331                 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1333                 SPItem *item = desktop->getItemAtPoint( where, true );
1335                 bool consumed = false;
1336                 if (desktop->event_context && desktop->event_context->get_drag()) {
1337                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1338                     if (consumed) {
1339                         DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1340                         desktop->event_context->get_drag()->updateDraggers();
1341                     }
1342                 }
1344                 if (!consumed && item) {
1345                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1346                     if (fillnotstroke &&
1347                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1348                         Path *livarot_path = Path_for_item(item, true, true);
1349                         livarot_path->ConvertWithBackData(0.04);
1351                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1352                         if (position) {
1353                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1354                             Geom::Point delta = nearest - button_doc;
1355                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1356                             delta = desktop->d2w(delta);
1357                             double stroke_tolerance =
1358                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1359                                   desktop->current_zoom() *
1360                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1361                                   to_2geom(item->i2d_affine()).descrim() * 0.5
1362                                   : 0.0)
1363                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1365                             if (Geom::L2 (delta) < stroke_tolerance) {
1366                                 fillnotstroke = false;
1367                             }
1368                         }
1369                         delete livarot_path;
1370                     }
1372                     SPCSSAttr *css = sp_repr_css_attr_new();
1373                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1375                     sp_desktop_apply_css_recursive( item, css, true );
1376                     item->updateRepr();
1378                     DocumentUndo::done( doc , SP_VERB_NONE,
1379                                         _("Drop color") );
1380                 }
1381             }
1382         }
1383         break;
1385         case SVG_DATA:
1386         case SVG_XML_DATA: {
1387             gchar *svgdata = (gchar *)data->data;
1389             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1391             if (rnewdoc == NULL) {
1392                 sp_ui_error_dialog(_("Could not parse SVG data"));
1393                 return;
1394             }
1396             Inkscape::XML::Node *repr = rnewdoc->root();
1397             gchar const *style = repr->attribute("style");
1399             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1400             newgroup->setAttribute("style", style);
1402             Inkscape::XML::Document * xml_doc =  doc->getReprDoc();
1403             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1404                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1405                 newgroup->appendChild(newchild);
1406             }
1408             Inkscape::GC::release(rnewdoc);
1410             // Add it to the current layer
1412             // Greg's edits to add intelligent positioning of svg drops
1413             SPObject *new_obj = NULL;
1414             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1416             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1417             selection->set(SP_ITEM(new_obj));
1419             // move to mouse pointer
1420             {
1421                 sp_desktop_document(desktop)->ensureUpToDate();
1422                 Geom::OptRect sel_bbox = selection->bounds();
1423                 if (sel_bbox) {
1424                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1425                     sp_selection_move_relative(selection, m, false);
1426                 }
1427             }
1429             Inkscape::GC::release(newgroup);
1430             DocumentUndo::done( doc, SP_VERB_NONE,
1431                              _("Drop SVG") );
1432             break;
1433         }
1435         case URI_LIST: {
1436             gchar *uri = (gchar *)data->data;
1437             sp_ui_import_files(uri);
1438             break;
1439         }
1441         case PNG_DATA:
1442         case JPEG_DATA:
1443         case IMAGE_DATA: {
1444             const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1446             Inkscape::Extension::DB::InputList o;
1447             Inkscape::Extension::db.get_input_list(o);
1448             Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1449             while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1450                 ++i;
1451             }
1452             Inkscape::Extension::Extension *ext = *i;
1453             bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1454             ext->set_param_optiongroup("link", "embed");
1455             ext->set_gui(false);
1457             gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1458             g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1459             file_import(doc, filename, ext);
1460             g_free(filename);
1462             ext->set_param_optiongroup("link", save ? "embed" : "link");
1463             ext->set_gui(true);
1464             DocumentUndo::done( doc , SP_VERB_NONE,
1465                                 _("Drop bitmap image") );
1466             break;
1467         }
1468     }
1471 #include "gradient-context.h"
1473 void sp_ui_drag_motion( GtkWidget */*widget*/,
1474                         GdkDragContext */*drag_context*/,
1475                         gint /*x*/, gint /*y*/,
1476                         GtkSelectionData */*data*/,
1477                         guint /*info*/,
1478                         guint /*event_time*/,
1479                         gpointer /*user_data*/)
1481 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1482 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1485 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1488 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1489                               GdkDragContext */*drag_context*/,
1490                               guint /*event_time*/,
1491                               gpointer /*user_data*/ )
1493 //     g_message("drag-n-drop leave                at %d", event_time);
1496 static void
1497 sp_ui_import_files(gchar *buffer)
1499     GList *list = gnome_uri_list_extract_filenames(buffer);
1500     if (!list)
1501         return;
1502     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1503     g_list_foreach(list, (GFunc) g_free, NULL);
1504     g_list_free(list);
1507 static void
1508 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1510     if (filename) {
1511         if (strlen((char const *)filename) > 2)
1512             sp_ui_import_one_file((char const *)filename);
1513     }
1516 static void
1517 sp_ui_import_one_file(char const *filename)
1519     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1520     if (!doc) return;
1522     if (filename == NULL) return;
1524     // Pass off to common implementation
1525     // TODO might need to get the proper type of Inkscape::Extension::Extension
1526     file_import( doc, filename, NULL );
1529 void
1530 sp_ui_error_dialog(gchar const *message)
1532     GtkWidget *dlg;
1533     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1535     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1536                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1537     sp_transientize(dlg);
1538     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1539     gtk_dialog_run(GTK_DIALOG(dlg));
1540     gtk_widget_destroy(dlg);
1541     g_free(safeMsg);
1544 bool
1545 sp_ui_overwrite_file(gchar const *filename)
1547     bool return_value = FALSE;
1549     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1550         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1551         gchar* baseName = g_path_get_basename( filename );
1552         gchar* dirName = g_path_get_dirname( filename );
1553         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1554                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1555                                                                 GTK_MESSAGE_QUESTION,
1556                                                                 GTK_BUTTONS_NONE,
1557                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1558                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1559                                                                 baseName,
1560                                                                 dirName
1561             );
1562         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1563                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1564                                 _("Replace"), GTK_RESPONSE_YES,
1565                                 NULL );
1566         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1568         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1569             return_value = TRUE;
1570         } else {
1571             return_value = FALSE;
1572         }
1573         gtk_widget_destroy(dialog);
1574         g_free( baseName );
1575         g_free( dirName );
1576     } else {
1577         return_value = TRUE;
1578     }
1580     return return_value;
1583 static void
1584 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1586     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1589 static void
1590 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1592     void *child = GTK_BIN (data)->child;
1593     //child is either
1594     //- a GtkHBox, whose first child is a label displaying name if the menu
1595     //item has an accel key
1596     //- a GtkLabel if the menu has no accel key
1597     if (GTK_IS_LABEL(child)) {
1598         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1599     } else if (GTK_IS_HBOX(child)) {
1600         gtk_label_set_markup_with_mnemonic(
1601         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1602         name.c_str());
1603     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1604     //a menu item in yet another way...
1607 void injectRenamedIcons()
1609     Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
1611     std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed;
1612     renamed.push_back(std::make_pair("gtk-file", "document-x-generic"));
1613     renamed.push_back(std::make_pair("gtk-directory", "folder"));
1615     for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) {
1616         bool hasIcon = iconTheme->has_icon(it->first);
1617         bool hasSecondIcon = iconTheme->has_icon(it->second);
1619         if ( !hasIcon && hasSecondIcon ) {
1620             Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second);
1621             for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
1622                 Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
1623                 if ( pb ) {
1624                     // install a private copy of the pixbuf to avoid pinning a theme
1625                     Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
1626                     Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
1627                 }
1628             }
1629         }
1630     }
1634 /*
1635   Local Variables:
1636   mode:c++
1637   c-file-style:"stroustrup"
1638   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1639   indent-tabs-mode:nil
1640   fill-column:99
1641   End:
1642 */
1643 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :