Code

Pot and Dutch translation update
[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  *
9  * Copyright (C) 1999-2005 authors
10  * Copyright (C) 2001-2002 Ximian, Inc.
11  * Copyright (C) 2004 David Turner
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <gtk/gtk.h>
21 #include <glib.h>
23 #include "inkscape-private.h"
24 #include "extension/db.h"
25 #include "extension/effect.h"
26 #include "extension/input.h"
27 #include "widgets/icon.h"
28 #include "preferences.h"
29 #include "path-prefix.h"
30 #include "shortcuts.h"
31 #include "document.h"
32 #include "desktop-handles.h"
33 #include "file.h"
34 #include "interface.h"
35 #include "desktop.h"
36 #include "ui/context-menu.h"
37 #include "selection.h"
38 #include "selection-chemistry.h"
39 #include "svg-view-widget.h"
40 #include "widgets/desktop-widget.h"
41 #include "sp-item-group.h"
42 #include "sp-text.h"
43 #include "sp-gradient-fns.h"
44 #include "sp-gradient.h"
45 #include "sp-flowtext.h"
46 #include "sp-namedview.h"
47 #include "ui/view/view.h"
48 #include "helper/action.h"
49 #include "helper/gnome-utils.h"
50 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "dialogs/dialog-events.h"
53 #include "message-context.h"
54 #include "ui/uxmanager.h"
56 // Added for color drag-n-drop
57 #if ENABLE_LCMS
58 #include "lcms.h"
59 #endif // ENABLE_LCMS
60 #include "display/sp-canvas.h"
61 #include "color.h"
62 #include "svg/svg-color.h"
63 #include "desktop-style.h"
64 #include "style.h"
65 #include "event-context.h"
66 #include "gradient-drag.h"
67 #include "widgets/ege-paint-def.h"
69 // Include Mac OS X menu synchronization on native OSX build
70 #ifdef GDK_WINDOWING_QUARTZ
71 #include "ige-mac-menu.h"
72 #endif
74 /* Drag and Drop */
75 typedef enum {
76     URI_LIST,
77     SVG_XML_DATA,
78     SVG_DATA,
79     PNG_DATA,
80     JPEG_DATA,
81     IMAGE_DATA,
82     APP_X_INKY_COLOR,
83     APP_X_COLOR,
84     APP_OSWB_COLOR,
85 } ui_drop_target_info;
87 static GtkTargetEntry ui_drop_target_entries [] = {
88     {(gchar *)"text/uri-list",                0, URI_LIST        },
89     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
90     {(gchar *)"image/svg",                    0, SVG_DATA        },
91     {(gchar *)"image/png",                    0, PNG_DATA        },
92     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
93 #if ENABLE_MAGIC_COLORS
94     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
95 #endif // ENABLE_MAGIC_COLORS
96     {(gchar *)"application/x-oswb-color",     0, APP_OSWB_COLOR  },
97     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
98 };
100 static GtkTargetEntry *completeDropTargets = 0;
101 static int completeDropTargetsCount = 0;
102 static bool temporarily_block_actions = false;
104 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
105 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
106 static void sp_ui_import_files(gchar *buffer);
107 static void sp_ui_import_one_file(char const *filename);
108 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
109 static void sp_ui_drag_data_received(GtkWidget *widget,
110                                      GdkDragContext *drag_context,
111                                      gint x, gint y,
112                                      GtkSelectionData *data,
113                                      guint info,
114                                      guint event_time,
115                                      gpointer user_data);
116 static void sp_ui_drag_motion( GtkWidget *widget,
117                                GdkDragContext *drag_context,
118                                gint x, gint y,
119                                GtkSelectionData *data,
120                                guint info,
121                                guint event_time,
122                                gpointer user_data );
123 static void sp_ui_drag_leave( GtkWidget *widget,
124                               GdkDragContext *drag_context,
125                               guint event_time,
126                               gpointer user_data );
127 static void sp_ui_menu_item_set_sensitive(SPAction *action,
128                                           unsigned int sensitive,
129                                           void *data);
130 static void sp_ui_menu_item_set_name(SPAction *action,
131                                      Glib::ustring name,
132                                      void *data);
133 static void sp_recent_open(GtkRecentChooser *, gpointer);
135 static void injectRenamedIcons();
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     static bool iconsInjected = false;
412     if ( !iconsInjected ) {
413         iconsInjected = true;
414         injectRenamedIcons();
415     }
416     GtkWidget *icon;
418     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
419     gtk_widget_show(icon);
420     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
421 } // end of sp_ui_menu_add_icon
423 /**
424  * sp_ui_menu_append_item
425  *
426  * Appends a UI item with specific info for Inkscape/Sodipodi.
427  *
428  */
429 static GtkWidget *
430 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
431                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
432                         gpointer data, gboolean with_mnemonic = TRUE )
434     GtkWidget *item;
436     if (stock) {
437         item = gtk_image_menu_item_new_from_stock(stock, NULL);
438     } else if (label) {
439         item = (with_mnemonic)
440             ? gtk_image_menu_item_new_with_mnemonic(label) :
441             gtk_image_menu_item_new_with_label(label);
442     } else {
443         item = gtk_separator_menu_item_new();
444     }
446     gtk_widget_show(item);
448     if (callback) {
449         g_signal_connect(G_OBJECT(item), "activate", callback, data);
450     }
452     if (tip && view) {
453         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
454         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
455         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
456     }
458     gtk_menu_append(GTK_MENU(menu), item);
460     return item;
462 } // end of sp_ui_menu_append_item()
464 void
465 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
467     SPAction     *action;
468     unsigned int shortcut;
469     gchar        *s;
470     gchar        *atitle;
472     action = verb->get_action(NULL);
473     if (!action)
474         return;
476     atitle = sp_action_get_title(action);
478     s = g_stpcpy(c, atitle);
480     g_free(atitle);
482     shortcut = sp_shortcut_get_primary(verb);
483     if (shortcut!=GDK_VoidSymbol) {
484         gchar* key = sp_shortcut_get_label(shortcut);
485         s = g_stpcpy(s, " (");
486         s = g_stpcpy(s, key);
487         s = g_stpcpy(s, ")");
488         g_free(key);
489     }
493 /**
494  * sp_ui_menu_append_item_from_verb
495  *
496  * Appends a custom menu UI from a verb.
497  *
498  */
500 static GtkWidget *
501 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
503     SPAction *action;
504     GtkWidget *item;
506     if (verb->get_code() == SP_VERB_NONE) {
508         item = gtk_separator_menu_item_new();
510     } else {
511         unsigned int shortcut;
513         action = verb->get_action(view);
515         if (!action) return NULL;
517         shortcut = sp_shortcut_get_primary(verb);
518         if (shortcut!=GDK_VoidSymbol) {
519             gchar* c = sp_shortcut_get_label(shortcut);
520             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
521             GtkWidget *const name_lbl = gtk_label_new("");
522             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
523             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
524             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
525             GtkWidget *const accel_lbl = gtk_label_new(c);
526             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
527             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
528             gtk_widget_show_all(hb);
529             if (radio) {
530                 item = gtk_radio_menu_item_new (group);
531             } else {
532                 item = gtk_image_menu_item_new();
533             }
534             gtk_container_add((GtkContainer *) item, hb);
535             g_free(c);
536         } else {
537             if (radio) {
538                 item = gtk_radio_menu_item_new (group);
539             } else {
540                 item = gtk_image_menu_item_new ();
541             }
542             GtkWidget *const name_lbl = gtk_label_new("");
543             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
544             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
545             gtk_container_add((GtkContainer *) item, name_lbl);
546         }
548         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
549         if (!action->sensitive) {
550             gtk_widget_set_sensitive(item, FALSE);
551         }
553         if (action->image) {
554             sp_ui_menuitem_add_icon(item, action->image);
555         }
556         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
557         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
558         g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
559         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
560         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
561     }
563     gtk_widget_show(item);
564     gtk_menu_append(GTK_MENU(menu), item);
566     return item;
568 } // end of sp_ui_menu_append_item_from_verb
571 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
573     Glib::ustring prefPath;
575     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
576         prefPath = "/focus/";
577     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
578         prefPath = "/fullscreen/";
579     } else {
580         prefPath = "/window/";
581     }
583     return prefPath;
586 static void
587 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
589     gchar const *pref = (gchar const *) user_data;
590     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
592     Glib::ustring pref_path = getLayoutPrefPath( view );
593     pref_path += pref;
594     pref_path += "/state";
596     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
597     gboolean checked = gtk_check_menu_item_get_active(menuitem);
598     prefs->setBool(pref_path, checked);
600     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
603 static gboolean
604 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
606     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
608     gchar const *pref = (gchar const *) user_data;
609     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
611     Glib::ustring pref_path = getLayoutPrefPath( view );
612     pref_path += pref;
613     pref_path += "/state";
615     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
616     bool ison = prefs->getBool(pref_path, true);
618     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
619     gtk_check_menu_item_set_active(menuitem, ison);
620     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
622     return FALSE;
625 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
627     if ( gtk_check_menu_item_get_active(menuitem) ) {
628         gint taskNum = GPOINTER_TO_INT(userData);
629         taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
631         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
633         // note: this will change once more options are in the task set support:
634         Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
635     }
639 /**
640  *  \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
641  */
643 static gboolean
644 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
646         SPAction *action = (SPAction *) user_data;
647         g_assert(action->id != NULL);
649         Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
650     SPDesktop *dt = static_cast<SPDesktop*>(view);
651         Inkscape::RenderMode mode = dt->getMode();
653         bool new_state = false;
654         if (!strcmp(action->id, "ViewModeNormal")) {
655         new_state = mode == Inkscape::RENDERMODE_NORMAL;
656         } else if (!strcmp(action->id, "ViewModeNoFilters")) {
657         new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
658     } else if (!strcmp(action->id, "ViewModeOutline")) {
659         new_state = mode == Inkscape::RENDERMODE_OUTLINE;
660     } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
661         new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
662     } else {
663         g_warning("update_view_menu does not handle this verb");
664     }
666         if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
667                 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
668                         // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
669                         // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
670                         // This emission however should not invoke any actions, hence we block it here:
671                         temporarily_block_actions = true;
672                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
673                         temporarily_block_actions = false;
674                 }
675         }
677         return FALSE;
680 void
681 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
682                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
683                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
684                                        Inkscape::Verb *verb)
686     unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
687     SPAction *action = (verb) ? verb->get_action(view) : 0;
688     GtkWidget *item = gtk_check_menu_item_new();
690     if (verb && shortcut!=GDK_VoidSymbol) {
691         gchar* c = sp_shortcut_get_label(shortcut);
693         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
695         {
696             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
697             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
698             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
699         }
701         {
702             GtkWidget *l = gtk_label_new(c);
703             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
704             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
705         }
707         gtk_widget_show_all(hb);
709         gtk_container_add((GtkContainer *) item, hb);
710         g_free(c);
711     } else {
712         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
713         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
714         gtk_container_add((GtkContainer *) item, l);
715     }
716 #if 0
717     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
718     if (!action->sensitive) {
719         gtk_widget_set_sensitive(item, FALSE);
720     }
721 #endif
722     gtk_widget_show(item);
724     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
726     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
728     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
729     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
731     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
732     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
735 static void
736 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
738     // dealing with the bizarre filename convention in Inkscape for now
739     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
740     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
741     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
742     sp_file_open(utf8_fn, NULL);
743     g_free(utf8_fn);
744     g_free(local_fn);
745     g_free(uri);
748 static void
749 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
751     sp_file_new(uri);
754 void
755 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
757     std::list<gchar *> sources;
758     sources.push_back( profile_path("templates") ); // first try user's local dir
759     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
761     // Use this loop to iterate through a list of possible document locations.
762     while (!sources.empty()) {
763         gchar *dirname = sources.front();
765         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
766             GError *err = 0;
767             GDir *dir = g_dir_open(dirname, 0, &err);
769             if (dir) {
770                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
771                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
772                         continue; // skip non-svg files
774                     gchar *basename = g_path_get_basename(file);
775                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
776                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
778                     gchar const *filepath = g_build_filename(dirname, file, NULL);
779                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
780                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
781                     g_free(dupfile);
782                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
783                     g_free(filename);
785                     gtk_widget_show(item);
786                     // how does "filepath" ever get freed?
787                     g_signal_connect(G_OBJECT(item),
788                                      "activate",
789                                      G_CALLBACK(sp_file_new_from_template),
790                                      (gpointer) filepath);
792                     if (view) {
793                         // set null tip for now; later use a description from the template file
794                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
795                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
796                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
797                     }
799                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
800                 }
801                 g_dir_close(dir);
802             }
803         }
805         // toss the dirname
806         g_free(dirname);
807         sources.pop_front();
808     }
811 void
812 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
814     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
815     //                                       checkitem_toggled, checkitem_update, 0);
816     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
817                                            checkitem_toggled, checkitem_update, 0);
818     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
819                                            checkitem_toggled, checkitem_update, 0);
820     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
821                                            checkitem_toggled, checkitem_update, 0);
822     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
823                                            checkitem_toggled, checkitem_update, 0);
824     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
825                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
826     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
827                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
828     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
829                                            checkitem_toggled, checkitem_update, 0);
830     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
831                                            checkitem_toggled, checkitem_update, 0);
835 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
837     gchar const* data[] = {
838         _("Default"), _("Default interface setup"),
839         _("Custom"), _("Set the custom task"),
840         _("Wide"), _("Setup for widescreen work"),
841         0, 0
842     };
844     GSList *group = 0;
845     int count = 0;
846     gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
847     for (gchar const **strs = data; strs[0]; strs += 2, count++)
848     {
849         GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
850         group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
851         if ( count == active )
852         {
853             gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
854         }
856         g_object_set_data( G_OBJECT(item), "view", view );
857         g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
858         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
859         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
861         gtk_widget_show( item );
862         gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
863     }
867 /** @brief Observer that updates the recent list's max document count */
868 class MaxRecentObserver : public Inkscape::Preferences::Observer {
869 public:
870     MaxRecentObserver(GtkWidget *recent_menu) :
871         Observer("/options/maxrecentdocuments/value"),
872         _rm(recent_menu)
873     {}
874     virtual void notify(Inkscape::Preferences::Entry const &e) {
875         gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
876         // hack: the recent menu doesn't repopulate after changing the limit, so we force it
877         g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
878     }
879 private:
880     GtkWidget *_rm;
881 };
883 /** \brief  This function turns XML into a menu
884     \param  menus  This is the XML that defines the menu
885     \param  menu   Menu to be added to
886     \param  view   The View that this menu is being built for
888     This function is realitively simple as it just goes through the XML
889     and parses the individual elements.  In the case of a submenu, it
890     just calls itself recursively.  Because it is only reasonable to have
891     a couple of submenus, it is unlikely this will go more than two or
892     three times.
894     In the case of an unrecognized verb, a menu item is made to identify
895     the verb that is missing, and display that.  The menu item is also made
896     insensitive.
897 */
898 void
899 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
901     if (menus == NULL) return;
902     if (menu == NULL)  return;
903     GSList *group = NULL;
905     for (Inkscape::XML::Node *menu_pntr = menus;
906          menu_pntr != NULL;
907          menu_pntr = menu_pntr->next()) {
908         if (!strcmp(menu_pntr->name(), "submenu")) {
909             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
910             GtkWidget *submenu = gtk_menu_new();
911             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
912             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
913             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
914             continue;
915         }
916         if (!strcmp(menu_pntr->name(), "verb")) {
917             gchar const *verb_name = menu_pntr->attribute("verb-id");
918             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
920             if (verb != NULL) {
921                 if (menu_pntr->attribute("radio") != NULL) {
922                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
923                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
924                     if (menu_pntr->attribute("default") != NULL) {
925                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
926                     }
927                     if (verb->get_code() != SP_VERB_NONE) {
928                         SPAction *action = verb->get_action(view);
929                         g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
930                     }
931                 } else {
932                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
933                     group = NULL;
934                 }
935             } else {
936                 gchar string[120];
937                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
938                 string[119] = '\0'; /* may not be terminated */
939                 GtkWidget *item = gtk_menu_item_new_with_label(string);
940                 gtk_widget_set_sensitive(item, false);
941                 gtk_widget_show(item);
942                 gtk_menu_append(GTK_MENU(menu), item);
943             }
944             continue;
945         }
946         if (!strcmp(menu_pntr->name(), "separator")
947                 // This was spelt wrong in the original version
948                 // and so this is for backward compatibility.  It can
949                 // probably be dropped after the 0.44 release.
950              || !strcmp(menu_pntr->name(), "seperator")) {
951             GtkWidget *item = gtk_separator_menu_item_new();
952             gtk_widget_show(item);
953             gtk_menu_append(GTK_MENU(menu), item);
954             continue;
955         }
956         if (!strcmp(menu_pntr->name(), "template-list")) {
957             sp_menu_append_new_templates(menu, view);
958             continue;
959         }
960         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
961             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
963             // create recent files menu
964             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
965             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
966             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
967             // sort most recently used documents first to preserve previous behavior
968             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
969             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
971             // add filter to only open files added by Inkscape
972             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
973             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
974             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
976             gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
977             gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
979             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
980             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
982             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
983             // this will just sit and update the list's item count
984             static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
985             prefs->addObserver(*mro);
986             continue;
987         }
988         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
989             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
990             continue;
991         }
992         if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
993             addTaskMenuItems(GTK_MENU(menu), view);
994             continue;
995         }
996     }
999 /** \brief  Build the main tool bar
1000     \param  view  View to build the bar for
1002     Currently the main tool bar is built as a dynamic XML menu using
1003     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
1004     pass it to get items attached to it.
1005 */
1006 GtkWidget *
1007 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1009     GtkWidget *mbar = gtk_menu_bar_new();
1011 #ifdef GDK_WINDOWING_QUARTZ
1012     ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1013 #endif
1015     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1017 #ifdef GDK_WINDOWING_QUARTZ
1018     return NULL;
1019 #else
1020     return mbar;
1021 #endif
1024 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1025     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1028 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1029     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1030     sp_desktop_selection(desktop)->clear();
1033 GtkWidget *
1034 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1036     GtkWidget *m;
1037     SPDesktop *dt;
1039     dt = static_cast<SPDesktop*>(view);
1041     m = gtk_menu_new();
1043     /* Undo and Redo */
1044     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1045     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1047     /* Separator */
1048     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1050     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1051     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1052     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1054     /* Separator */
1055     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1057     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1058     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1060     /* Item menu */
1061     if (item) {
1062         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1063         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1064     }
1066     /* layer menu */
1067     SPGroup *group=NULL;
1068     if (item) {
1069         if (SP_IS_GROUP(item)) {
1070             group = SP_GROUP(item);
1071         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1072             group = SP_GROUP(SP_OBJECT_PARENT(item));
1073         }
1074     }
1076     if (( group && group != dt->currentLayer() ) ||
1077         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1078         /* Separator */
1079         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1080     }
1082     if ( group && group != dt->currentLayer() ) {
1083         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1084         gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1085         GtkWidget *w = gtk_menu_item_new_with_label(label);
1086         g_free(label);
1087         g_object_set_data(G_OBJECT(w), "group", group);
1088         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1089         gtk_widget_show(w);
1090         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1091     }
1093     if ( dt->currentLayer() != dt->currentRoot() ) {
1094         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1095             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1096             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1097             gtk_widget_show(w);
1098             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1100         }
1101     }
1103     return m;
1106 /* Drag and Drop */
1107 void
1108 sp_ui_drag_data_received(GtkWidget *widget,
1109                          GdkDragContext *drag_context,
1110                          gint x, gint y,
1111                          GtkSelectionData *data,
1112                          guint info,
1113                          guint /*event_time*/,
1114                          gpointer /*user_data*/)
1116     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1117     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1119     switch (info) {
1120 #if ENABLE_MAGIC_COLORS
1121         case APP_X_INKY_COLOR:
1122         {
1123             int destX = 0;
1124             int destY = 0;
1125             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1126             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1128             SPItem *item = desktop->item_at_point( where, true );
1129             if ( item )
1130             {
1131                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1133                 if ( data->length >= 8 ) {
1134                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1136                     gchar c[64] = {0};
1137                     // Careful about endian issues.
1138                     guint16* dataVals = (guint16*)data->data;
1139                     sp_svg_write_color( c, sizeof(c),
1140                                         SP_RGBA32_U_COMPOSE(
1141                                             0x0ff & (dataVals[0] >> 8),
1142                                             0x0ff & (dataVals[1] >> 8),
1143                                             0x0ff & (dataVals[2] >> 8),
1144                                             0xff // can't have transparency in the color itself
1145                                             //0x0ff & (data->data[3] >> 8),
1146                                             ));
1147                     SPCSSAttr *css = sp_repr_css_attr_new();
1148                     bool updatePerformed = false;
1150                     if ( data->length > 14 ) {
1151                         int flags = dataVals[4];
1153                         // piggie-backed palette entry info
1154                         int index = dataVals[5];
1155                         Glib::ustring palName;
1156                         for ( int i = 0; i < dataVals[6]; i++ ) {
1157                             palName += (gunichar)dataVals[7+i];
1158                         }
1160                         // Now hook in a magic tag of some sort.
1161                         if ( !palName.empty() && (flags & 1) ) {
1162                             gchar* str = g_strdup_printf("%d|", index);
1163                             palName.insert( 0, str );
1164                             g_free(str);
1165                             str = 0;
1167                             sp_object_setAttribute( SP_OBJECT(item),
1168                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1169                                                     palName.c_str(),
1170                                                     false );
1171                             item->updateRepr();
1173                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1174                             updatePerformed = true;
1175                         }
1176                     }
1178                     if ( !updatePerformed ) {
1179                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1180                     }
1182                     sp_desktop_apply_css_recursive( item, css, true );
1183                     item->updateRepr();
1185                     sp_document_done( doc , SP_VERB_NONE,
1186                                       _("Drop color"));
1188                     if ( srgbProf ) {
1189                         cmsCloseProfile( srgbProf );
1190                     }
1191                 }
1192             }
1193         }
1194         break;
1195 #endif // ENABLE_MAGIC_COLORS
1197         case APP_X_COLOR:
1198         {
1199             int destX = 0;
1200             int destY = 0;
1201             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1202             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1203             Geom::Point const button_dt(desktop->w2d(where));
1204             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1206             if ( data->length == 8 ) {
1207                 gchar colorspec[64] = {0};
1208                 // Careful about endian issues.
1209                 guint16* dataVals = (guint16*)data->data;
1210                 sp_svg_write_color( colorspec, sizeof(colorspec),
1211                                     SP_RGBA32_U_COMPOSE(
1212                                         0x0ff & (dataVals[0] >> 8),
1213                                         0x0ff & (dataVals[1] >> 8),
1214                                         0x0ff & (dataVals[2] >> 8),
1215                                         0xff // can't have transparency in the color itself
1216                                         //0x0ff & (data->data[3] >> 8),
1217                                         ));
1219                 SPItem *item = desktop->item_at_point( where, true );
1221                 bool consumed = false;
1222                 if (desktop->event_context && desktop->event_context->get_drag()) {
1223                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1224                     if (consumed) {
1225                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1226                         desktop->event_context->get_drag()->updateDraggers();
1227                     }
1228                 }
1230                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1231                 //    consumed = sp_text_context_drop_color(c, button_doc);
1232                 //    if (consumed) {
1233                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1234                 //    }
1235                 //}
1237                 if (!consumed && item) {
1238                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1239                     if (fillnotstroke &&
1240                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1241                         Path *livarot_path = Path_for_item(item, true, true);
1242                         livarot_path->ConvertWithBackData(0.04);
1244                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1245                         if (position) {
1246                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1247                             Geom::Point delta = nearest - button_doc;
1248                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1249                             delta = desktop->d2w(delta);
1250                             double stroke_tolerance =
1251                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1252                                   desktop->current_zoom() *
1253                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1254                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1255                                   : 0.0)
1256                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1258                             if (Geom::L2 (delta) < stroke_tolerance) {
1259                                 fillnotstroke = false;
1260                             }
1261                         }
1262                         delete livarot_path;
1263                     }
1265                     SPCSSAttr *css = sp_repr_css_attr_new();
1266                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1268                     sp_desktop_apply_css_recursive( item, css, true );
1269                     item->updateRepr();
1271                     sp_document_done( doc , SP_VERB_NONE,
1272                                       _("Drop color"));
1273                 }
1274             }
1275         }
1276         break;
1278         case APP_OSWB_COLOR:
1279         {
1280             bool worked = false;
1281             Glib::ustring colorspec;
1282             if ( data->format == 8 ) {
1283                 ege::PaintDef color;
1284                 worked = color.fromMIMEData("application/x-oswb-color",
1285                                             reinterpret_cast<char*>(data->data),
1286                                             data->length,
1287                                             data->format);
1288                 if ( worked ) {
1289                     if ( color.getType() == ege::PaintDef::CLEAR ) {
1290                         colorspec = ""; // TODO check if this is sufficient
1291                     } else if ( color.getType() == ege::PaintDef::NONE ) {
1292                         colorspec = "none";
1293                     } else {
1294                         unsigned int r = color.getR();
1295                         unsigned int g = color.getG();
1296                         unsigned int b = color.getB();
1298                         SPGradient* matches = 0;
1299                         const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
1300                         for (const GSList *item = gradients; item; item = item->next) {
1301                             SPGradient* grad = SP_GRADIENT(item->data);
1302                             if ( color.descr == grad->getId() ) {
1303                                 if ( grad->hasStops() ) {
1304                                     matches = grad;
1305                                     break;
1306                                 }
1307                             }
1308                         }
1309                         if (matches) {
1310                             colorspec = "url(#";
1311                             colorspec += matches->getId();
1312                             colorspec += ")";
1313                         } else {
1314                             gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1315                             colorspec = tmp;
1316                             g_free(tmp);
1317                         }
1318                     }
1319                 }
1320             }
1321             if ( worked ) {
1322                 int destX = 0;
1323                 int destY = 0;
1324                 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1325                 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1326                 Geom::Point const button_dt(desktop->w2d(where));
1327                 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1329                 SPItem *item = desktop->item_at_point( where, true );
1331                 bool consumed = false;
1332                 if (desktop->event_context && desktop->event_context->get_drag()) {
1333                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1334                     if (consumed) {
1335                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1336                         desktop->event_context->get_drag()->updateDraggers();
1337                     }
1338                 }
1340                 if (!consumed && item) {
1341                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1342                     if (fillnotstroke &&
1343                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1344                         Path *livarot_path = Path_for_item(item, true, true);
1345                         livarot_path->ConvertWithBackData(0.04);
1347                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1348                         if (position) {
1349                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1350                             Geom::Point delta = nearest - button_doc;
1351                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1352                             delta = desktop->d2w(delta);
1353                             double stroke_tolerance =
1354                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1355                                   desktop->current_zoom() *
1356                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1357                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1358                                   : 0.0)
1359                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1361                             if (Geom::L2 (delta) < stroke_tolerance) {
1362                                 fillnotstroke = false;
1363                             }
1364                         }
1365                         delete livarot_path;
1366                     }
1368                     SPCSSAttr *css = sp_repr_css_attr_new();
1369                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1371                     sp_desktop_apply_css_recursive( item, css, true );
1372                     item->updateRepr();
1374                     sp_document_done( doc , SP_VERB_NONE,
1375                                       _("Drop color"));
1376                 }
1377             }
1378         }
1379         break;
1381         case SVG_DATA:
1382         case SVG_XML_DATA: {
1383             gchar *svgdata = (gchar *)data->data;
1385             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1387             if (rnewdoc == NULL) {
1388                 sp_ui_error_dialog(_("Could not parse SVG data"));
1389                 return;
1390             }
1392             Inkscape::XML::Node *repr = rnewdoc->root();
1393             gchar const *style = repr->attribute("style");
1395             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1396             newgroup->setAttribute("style", style);
1398             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1399             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1400                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1401                 newgroup->appendChild(newchild);
1402             }
1404             Inkscape::GC::release(rnewdoc);
1406             // Add it to the current layer
1408             // Greg's edits to add intelligent positioning of svg drops
1409             SPObject *new_obj = NULL;
1410             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1412             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1413             selection->set(SP_ITEM(new_obj));
1415             // move to mouse pointer
1416             {
1417                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1418                 Geom::OptRect sel_bbox = selection->bounds();
1419                 if (sel_bbox) {
1420                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1421                     sp_selection_move_relative(selection, m, false);
1422                 }
1423             }
1425             Inkscape::GC::release(newgroup);
1426             sp_document_done(doc, SP_VERB_NONE,
1427                              _("Drop SVG"));
1428             break;
1429         }
1431         case URI_LIST: {
1432             gchar *uri = (gchar *)data->data;
1433             sp_ui_import_files(uri);
1434             break;
1435         }
1437         case PNG_DATA:
1438         case JPEG_DATA:
1439         case IMAGE_DATA: {
1440             const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1442             Inkscape::Extension::DB::InputList o;
1443             Inkscape::Extension::db.get_input_list(o);
1444             Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1445             while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1446                 ++i;
1447             }
1448             Inkscape::Extension::Extension *ext = *i;
1449             bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1450             ext->set_param_optiongroup("link", "embed");
1451             ext->set_gui(false);
1453             gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1454             g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1455             file_import(doc, filename, ext);
1456             g_free(filename);
1458             ext->set_param_optiongroup("link", save ? "embed" : "link");
1459             ext->set_gui(true);
1460             sp_document_done( doc , SP_VERB_NONE,
1461                               _("Drop bitmap image"));
1462             break;
1463         }
1464     }
1467 #include "gradient-context.h"
1469 void sp_ui_drag_motion( GtkWidget */*widget*/,
1470                         GdkDragContext */*drag_context*/,
1471                         gint /*x*/, gint /*y*/,
1472                         GtkSelectionData */*data*/,
1473                         guint /*info*/,
1474                         guint /*event_time*/,
1475                         gpointer /*user_data*/)
1477 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1478 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1481 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1484 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1485                               GdkDragContext */*drag_context*/,
1486                               guint /*event_time*/,
1487                               gpointer /*user_data*/ )
1489 //     g_message("drag-n-drop leave                at %d", event_time);
1492 static void
1493 sp_ui_import_files(gchar *buffer)
1495     GList *list = gnome_uri_list_extract_filenames(buffer);
1496     if (!list)
1497         return;
1498     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1499     g_list_foreach(list, (GFunc) g_free, NULL);
1500     g_list_free(list);
1503 static void
1504 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1506     if (filename) {
1507         if (strlen((char const *)filename) > 2)
1508             sp_ui_import_one_file((char const *)filename);
1509     }
1512 static void
1513 sp_ui_import_one_file(char const *filename)
1515     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1516     if (!doc) return;
1518     if (filename == NULL) return;
1520     // Pass off to common implementation
1521     // TODO might need to get the proper type of Inkscape::Extension::Extension
1522     file_import( doc, filename, NULL );
1525 void
1526 sp_ui_error_dialog(gchar const *message)
1528     GtkWidget *dlg;
1529     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1531     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1532                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1533     sp_transientize(dlg);
1534     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1535     gtk_dialog_run(GTK_DIALOG(dlg));
1536     gtk_widget_destroy(dlg);
1537     g_free(safeMsg);
1540 bool
1541 sp_ui_overwrite_file(gchar const *filename)
1543     bool return_value = FALSE;
1545     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1546         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1547         gchar* baseName = g_path_get_basename( filename );
1548         gchar* dirName = g_path_get_dirname( filename );
1549         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1550                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1551                                                                 GTK_MESSAGE_QUESTION,
1552                                                                 GTK_BUTTONS_NONE,
1553                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1554                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1555                                                                 baseName,
1556                                                                 dirName
1557             );
1558         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1559                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1560                                 _("Replace"), GTK_RESPONSE_YES,
1561                                 NULL );
1562         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1564         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1565             return_value = TRUE;
1566         } else {
1567             return_value = FALSE;
1568         }
1569         gtk_widget_destroy(dialog);
1570         g_free( baseName );
1571         g_free( dirName );
1572     } else {
1573         return_value = TRUE;
1574     }
1576     return return_value;
1579 static void
1580 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1582     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1585 static void
1586 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1588     void *child = GTK_BIN (data)->child;
1589     //child is either
1590     //- a GtkHBox, whose first child is a label displaying name if the menu
1591     //item has an accel key
1592     //- a GtkLabel if the menu has no accel key
1593     if (GTK_IS_LABEL(child)) {
1594         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1595     } else if (GTK_IS_HBOX(child)) {
1596         gtk_label_set_markup_with_mnemonic(
1597         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1598         name.c_str());
1599     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1600     //a menu item in yet another way...
1603 void injectRenamedIcons()
1605     Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
1607     std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed;
1608     renamed.push_back(std::make_pair("gtk-file", "document-x-generic"));
1609     renamed.push_back(std::make_pair("gtk-directory", "folder"));
1611     for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) {
1612         bool hasIcon = iconTheme->has_icon(it->first);
1613         bool hasSecondIcon = iconTheme->has_icon(it->second);
1615         if ( !hasIcon && hasSecondIcon ) {
1616             Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second);
1617             for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
1618                 Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
1619                 if ( pb ) {
1620                     // install a private copy of the pixbuf to avoid pinning a theme
1621                     Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
1622                     Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
1623                 }
1624             }
1625         }
1626     }
1630 /*
1631   Local Variables:
1632   mode:c++
1633   c-file-style:"stroustrup"
1634   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1635   indent-tabs-mode:nil
1636   fill-column:99
1637   End:
1638 */
1639 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :