Code

Added a new toolbar with snapping controls
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /** @file
4  * @brief Main UI stuff
5  */
6 /* Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 1999-2005 authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  * Copyright (C) 2004 David Turner
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include <glib.h>
24 #include "inkscape-private.h"
25 #include "extension/effect.h"
26 #include "widgets/icon.h"
27 #include "preferences.h"
28 #include "path-prefix.h"
30 #include "shortcuts.h"
32 #include "document.h"
33 #include "desktop-handles.h"
34 #include "file.h"
35 #include "interface.h"
36 #include "desktop.h"
37 #include "ui/context-menu.h"
38 #include "selection.h"
39 #include "selection-chemistry.h"
40 #include "svg-view-widget.h"
41 #include "widgets/desktop-widget.h"
42 #include "sp-item-group.h"
43 #include "sp-text.h"
44 #include "sp-flowtext.h"
45 #include "sp-namedview.h"
46 #include "ui/view/view.h"
48 #include "helper/action.h"
49 #include "helper/gnome-utils.h"
50 #include "helper/window.h"
52 #include "io/sys.h"
53 #include "io/stringstream.h"
54 #include "io/base64stream.h"
56 #include "dialogs/dialog-events.h"
58 #include "message-context.h"
60 // Added for color drag-n-drop
61 #if ENABLE_LCMS
62 #include "lcms.h"
63 #endif // ENABLE_LCMS
64 #include "display/sp-canvas.h"
65 #include "color.h"
66 #include "svg/svg-color.h"
67 #include "desktop-style.h"
68 #include "style.h"
69 #include "event-context.h"
70 #include "gradient-drag.h"
72 // Include Mac OS X menu synchronization on native OSX build
73 #ifdef GDK_WINDOWING_QUARTZ
74 #include "ige-mac-menu.h"
75 #endif
77 using Inkscape::IO::StringOutputStream;
78 using Inkscape::IO::Base64OutputStream;
80 /* Drag and Drop */
81 typedef enum {
82     URI_LIST,
83     SVG_XML_DATA,
84     SVG_DATA,
85     PNG_DATA,
86     JPEG_DATA,
87     IMAGE_DATA,
88     APP_X_INKY_COLOR,
89     APP_X_COLOR
90 } ui_drop_target_info;
92 static GtkTargetEntry ui_drop_target_entries [] = {
93     {(gchar *)"text/uri-list",                0, URI_LIST        },
94     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
95     {(gchar *)"image/svg",                    0, SVG_DATA        },
96     {(gchar *)"image/png",                    0, PNG_DATA        },
97     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
98 #if ENABLE_MAGIC_COLORS
99     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
100 #endif // ENABLE_MAGIC_COLORS
101     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
102 };
104 static GtkTargetEntry *completeDropTargets = 0;
105 static int completeDropTargetsCount = 0;
107 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
108 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
109 static void sp_ui_import_files(gchar *buffer);
110 static void sp_ui_import_one_file(char const *filename);
111 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
112 static void sp_ui_drag_data_received(GtkWidget *widget,
113                                      GdkDragContext *drag_context,
114                                      gint x, gint y,
115                                      GtkSelectionData *data,
116                                      guint info,
117                                      guint event_time,
118                                      gpointer user_data);
119 static void sp_ui_drag_motion( GtkWidget *widget,
120                                GdkDragContext *drag_context,
121                                gint x, gint y,
122                                GtkSelectionData *data,
123                                guint info,
124                                guint event_time,
125                                gpointer user_data );
126 static void sp_ui_drag_leave( GtkWidget *widget,
127                               GdkDragContext *drag_context,
128                               guint event_time,
129                               gpointer user_data );
130 static void sp_ui_menu_item_set_sensitive(SPAction *action,
131                                           unsigned int sensitive,
132                                           void *data);
133 static void sp_ui_menu_item_set_name(SPAction *action,
134                                      Glib::ustring name,
135                                      void *data);
136 static void sp_recent_open(GtkRecentChooser *, gpointer);
138 SPActionEventVector menu_item_event_vector = {
139     {NULL},
140     NULL,
141     NULL, /* set_active */
142     sp_ui_menu_item_set_sensitive, /* set_sensitive */
143     NULL, /* set_shortcut */
144     sp_ui_menu_item_set_name /* set_name */
145 };
147 static const int MIN_ONSCREEN_DISTANCE = 50;
149 void
150 sp_create_window(SPViewWidget *vw, gboolean editable)
152     g_return_if_fail(vw != NULL);
153     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
155     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
157     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
158     gtk_widget_show(GTK_WIDGET(vw));
160     if (editable) {
161                 g_object_set_data(G_OBJECT(vw), "window", win);
163                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
164                 SPDesktop* desktop = desktop_widget->desktop;
166                 desktop_widget->window = win;
168         win->set_data("desktop", desktop);
169         win->set_data("desktopwidget", desktop_widget);
171         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
172                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
173                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
175         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
176         gint prefs_geometry =
177             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
178         if (prefs_geometry) {
179             gint pw = prefs->getInt("/desktop/geometry/width", -1);
180             gint ph = prefs->getInt("/desktop/geometry/height", -1);
181             gint px = prefs->getInt("/desktop/geometry/x", -1);
182             gint py = prefs->getInt("/desktop/geometry/y", -1);
183             gint full = prefs->getBool("/desktop/geometry/fullscreen");
184             gint maxed = prefs->getBool("/desktop/geometry/maximized");
185             if (pw>0 && ph>0) {
186                 gint w = MIN(gdk_screen_width(), pw);
187                 gint h = MIN(gdk_screen_height(), ph);
188                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
189                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
190                 if (w>0 && h>0 && x>0 && y>0) {
191                     x = MIN(gdk_screen_width() - w, x);
192                     y = MIN(gdk_screen_height() - h, y);
193                 }
194                 if (w>0 && h>0) {
195                     desktop->setWindowSize(w, h);
196                 }
198                 // Only restore xy for the first window so subsequent windows don't overlap exactly
199                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
200                 // other desktops?)
202                 // Empirically it seems that active_desktop==this desktop only the first time a
203                 // desktop is created.
204                 if (x>0 && y>0) {
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             }
211             if (maxed) {
212                 win->maximize();
213             }
214             if (full) {
215                 win->fullscreen();
216             }
217         }
219     } else {
220         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
221     }
223     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
224     {
225         std::vector<gchar*> types;
227         GSList *list = gdk_pixbuf_get_formats();
228         while ( list ) {
229             int i = 0;
230             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
231             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
232             for ( i = 0; typesXX[i]; i++ ) {
233                 types.push_back(g_strdup(typesXX[i]));
234             }
235             g_strfreev(typesXX);
237             list = g_slist_next(list);
238         }
239         completeDropTargetsCount = nui_drop_target_entries + types.size();
240         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
241         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
242             completeDropTargets[i] = ui_drop_target_entries[i];
243         }
244         int pos = nui_drop_target_entries;
246         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
247             completeDropTargets[pos].target = *it;
248             completeDropTargets[pos].flags = 0;
249             completeDropTargets[pos].info = IMAGE_DATA;
250             pos++;
251         }
252     }
254     gtk_drag_dest_set((GtkWidget*)win->gobj(),
255                       GTK_DEST_DEFAULT_ALL,
256                       completeDropTargets,
257                       completeDropTargetsCount,
258                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
261     g_signal_connect(G_OBJECT(win->gobj()),
262                      "drag_data_received",
263                      G_CALLBACK(sp_ui_drag_data_received),
264                      NULL);
265     g_signal_connect(G_OBJECT(win->gobj()),
266                      "drag_motion",
267                      G_CALLBACK(sp_ui_drag_motion),
268                      NULL);
269     g_signal_connect(G_OBJECT(win->gobj()),
270                      "drag_leave",
271                      G_CALLBACK(sp_ui_drag_leave),
272                      NULL);
273     win->show();
275     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
276     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
279 void
280 sp_ui_new_view()
282     SPDocument *document;
283     SPViewWidget *dtw;
285     document = SP_ACTIVE_DOCUMENT;
286     if (!document) return;
288     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
289     g_return_if_fail(dtw != NULL);
291     sp_create_window(dtw, TRUE);
292     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
293     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
296 /* TODO: not yet working */
297 /* To be re-enabled (by adding to menu) once it works. */
298 void
299 sp_ui_new_view_preview()
301     SPDocument *document;
302     SPViewWidget *dtw;
304     document = SP_ACTIVE_DOCUMENT;
305     if (!document) return;
307     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
308     g_return_if_fail(dtw != NULL);
309     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
311     sp_create_window(dtw, FALSE);
314 /**
315  * \param widget unused
316  */
317 void
318 sp_ui_close_view(GtkWidget */*widget*/)
320     if (SP_ACTIVE_DESKTOP == NULL) {
321         return;
322     }
323     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
324         return;
325     }
326     SP_ACTIVE_DESKTOP->destroyWidget();
330 /**
331  *  sp_ui_close_all
332  *
333  *  This function is called to exit the program, and iterates through all
334  *  open document view windows, attempting to close each in turn.  If the
335  *  view has unsaved information, the user will be prompted to save,
336  *  discard, or cancel.
337  *
338  *  Returns FALSE if the user cancels the close_all operation, TRUE
339  *  otherwise.
340  */
341 unsigned int
342 sp_ui_close_all(void)
344     /* Iterate through all the windows, destroying each in the order they
345        become active */
346     while (SP_ACTIVE_DESKTOP) {
347         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
348             /* The user cancelled the operation, so end doing the close */
349             return FALSE;
350         }
351         SP_ACTIVE_DESKTOP->destroyWidget();
352     }
354     return TRUE;
357 /*
358  * Some day when the right-click menus are ready to start working
359  * smarter with the verbs, we'll need to change this NULL being
360  * sent to sp_action_perform to something useful, or set some kind
361  * of global "right-clicked position" variable for actions to
362  * investigate when they're called.
363  */
364 static void
365 sp_ui_menu_activate(void */*object*/, SPAction *action)
367     sp_action_perform(action, NULL);
370 static void
371 sp_ui_menu_select_action(void */*object*/, SPAction *action)
373     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
376 static void
377 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
379     action->view->tipsMessageContext()->clear();
382 static void
383 sp_ui_menu_select(gpointer object, gpointer tip)
385     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
386     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
389 static void
390 sp_ui_menu_deselect(gpointer object)
392     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
393     view->tipsMessageContext()->clear();
396 /**
397  * sp_ui_menuitem_add_icon
398  *
399  * Creates and attaches a scaled icon to the given menu item.
400  *
401  */
402 void
403 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
405     GtkWidget *icon;
407     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
408     gtk_widget_show(icon);
409     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
410 } // end of sp_ui_menu_add_icon
412 /**
413  * sp_ui_menu_append_item
414  *
415  * Appends a UI item with specific info for Inkscape/Sodipodi.
416  *
417  */
418 static GtkWidget *
419 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
420                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
421                         gpointer data, gboolean with_mnemonic = TRUE )
423     GtkWidget *item;
425     if (stock) {
426         item = gtk_image_menu_item_new_from_stock(stock, NULL);
427     } else if (label) {
428         item = (with_mnemonic)
429             ? gtk_image_menu_item_new_with_mnemonic(label) :
430             gtk_image_menu_item_new_with_label(label);
431     } else {
432         item = gtk_separator_menu_item_new();
433     }
435     gtk_widget_show(item);
437     if (callback) {
438         g_signal_connect(G_OBJECT(item), "activate", callback, data);
439     }
441     if (tip && view) {
442         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
443         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
444         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
445     }
447     gtk_menu_append(GTK_MENU(menu), item);
449     return item;
451 } // end of sp_ui_menu_append_item()
453 /**
454 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
455  */
456 static gchar const *
457 sp_key_name(guint keyval)
459     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
460        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
461     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
463     if      (!strcmp(n, "asciicircum"))  return "^";
464     else if (!strcmp(n, "parenleft"  ))  return "(";
465     else if (!strcmp(n, "parenright" ))  return ")";
466     else if (!strcmp(n, "plus"       ))  return "+";
467     else if (!strcmp(n, "minus"      ))  return "-";
468     else if (!strcmp(n, "asterisk"   ))  return "*";
469     else if (!strcmp(n, "KP_Multiply"))  return "*";
470     else if (!strcmp(n, "Delete"     ))  return "Del";
471     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
472     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
473     else if (!strcmp(n, "grave"      ))  return "`";
474     else if (!strcmp(n, "numbersign" ))  return "#";
475     else if (!strcmp(n, "bar"        ))  return "|";
476     else if (!strcmp(n, "slash"      ))  return "/";
477     else if (!strcmp(n, "exclam"     ))  return "!";
478     else if (!strcmp(n, "percent"    ))  return "%";
479     else return n;
483 /**
484  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
485  * \param c Points to a buffer at least 256 bytes long.
486  */
487 void
488 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
490     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
491      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
492      * Will probably need to change sp_shortcut_invoke callers.
493      *
494      * The existing gtk_label_new_with_mnemonic call can be replaced with
495      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
496      * gtk_label_set_text_with_mnemonic(lbl, str).
497      */
498     static GtkAccelLabelClass const &accel_lbl_cls
499         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
501     struct { unsigned test; char const *name; } const modifier_tbl[] = {
502         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
503         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
504         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
505     };
507     gchar *p = c;
508     gchar *end = p + 256;
510     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
511         if ((shortcut & modifier_tbl[i].test)
512             && (p < end))
513         {
514             p += g_snprintf(p, end - p, "%s%s",
515                             modifier_tbl[i].name,
516                             accel_lbl_cls.mod_separator);
517         }
518     }
519     if (p < end) {
520         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
521     }
522     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
525 void
526 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
528     SPAction     *action;
529     unsigned int shortcut;
530     gchar        *s;
531     gchar        key[256];
532     gchar        *atitle;
534     action = verb->get_action(NULL);
535     if (!action)
536         return;
538     atitle = sp_action_get_title(action);
540     s = g_stpcpy(c, atitle);
542     g_free(atitle);
544     shortcut = sp_shortcut_get_primary(verb);
545     if (shortcut) {
546         s = g_stpcpy(s, " (");
547         sp_ui_shortcut_string(shortcut, key);
548         s = g_stpcpy(s, key);
549         s = g_stpcpy(s, ")");
550     }
554 /**
555  * sp_ui_menu_append_item_from_verb
556  *
557  * Appends a custom menu UI from a verb.
558  *
559  */
561 static GtkWidget *
562 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
564     SPAction *action;
565     GtkWidget *item;
567     if (verb->get_code() == SP_VERB_NONE) {
569         item = gtk_separator_menu_item_new();
571     } else {
572         unsigned int shortcut;
574         action = verb->get_action(view);
576         if (!action) return NULL;
578         shortcut = sp_shortcut_get_primary(verb);
579         if (shortcut) {
580             gchar c[256];
581             sp_ui_shortcut_string(shortcut, c);
582             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
583             GtkWidget *const name_lbl = gtk_label_new("");
584             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
585             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
586             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
587             GtkWidget *const accel_lbl = gtk_label_new(c);
588             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
589             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
590             gtk_widget_show_all(hb);
591             if (radio) {
592                 item = gtk_radio_menu_item_new (group);
593             } else {
594                 item = gtk_image_menu_item_new();
595             }
596             gtk_container_add((GtkContainer *) item, hb);
597         } else {
598             if (radio) {
599                 item = gtk_radio_menu_item_new (group);
600             } else {
601                 item = gtk_image_menu_item_new ();
602             }
603             GtkWidget *const name_lbl = gtk_label_new("");
604             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
605             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
606             gtk_container_add((GtkContainer *) item, name_lbl);
607         }
609         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
610         if (!action->sensitive) {
611             gtk_widget_set_sensitive(item, FALSE);
612         }
614         if (action->image) {
615             sp_ui_menuitem_add_icon(item, action->image);
616         }
617         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
618         g_signal_connect( G_OBJECT(item), "activate",
619                           G_CALLBACK(sp_ui_menu_activate), action );
621         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
622         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
623     }
625     gtk_widget_show(item);
626     gtk_menu_append(GTK_MENU(menu), item);
628     return item;
630 } // end of sp_ui_menu_append_item_from_verb
633 static void
634 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
636     gchar const *pref = (gchar const *) user_data;
637     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
639     Glib::ustring pref_path;
640     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
641         pref_path = "/focus/";
642     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
643         pref_path = "/fullscreen/";
644     } else {
645         pref_path = "/window/";
646     }
647     pref_path += pref;
648     pref_path += "/state";
650     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
651     gboolean checked = gtk_check_menu_item_get_active(menuitem);
652     prefs->setBool(pref_path, checked);
654     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
657 static gboolean
658 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
660     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
662     gchar const *pref = (gchar const *) user_data;
663     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
665     Glib::ustring pref_path;
666     if ((static_cast<SPDesktop*>(view))->is_fullscreen()) {
667         pref_path = "/fullscreen/";
668     } else {
669         pref_path = "/window/";
670     }
671     pref_path += pref;
673     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
674     bool ison = prefs->getBool(pref_path + "/state", true);
676     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
677     gtk_check_menu_item_set_active(menuitem, ison);
678     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
680     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     GtkWidget *item;
692     unsigned int shortcut = 0;
693     SPAction *action = NULL;
695     if (verb) {
696         shortcut = sp_shortcut_get_primary(verb);
697         action = verb->get_action(view);
698     }
700     if (verb && shortcut) {
701         gchar c[256];
702         sp_ui_shortcut_string(shortcut, c);
704         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
706         {
707             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
708             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
709             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
710         }
712         {
713             GtkWidget *l = gtk_label_new(c);
714             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
715             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
716         }
718         gtk_widget_show_all(hb);
720         item = gtk_check_menu_item_new();
721         gtk_container_add((GtkContainer *) item, hb);
722     } else {
723         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
724         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
725         item = gtk_check_menu_item_new();
726         gtk_container_add((GtkContainer *) item, l);
727     }
728 #if 0
729     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
730     if (!action->sensitive) {
731         gtk_widget_set_sensitive(item, FALSE);
732     }
733 #endif
734     gtk_widget_show(item);
736     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
738     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
740     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
741     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
743     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
744     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
747 static void
748 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
750     // dealing with the bizarre filename convention in Inkscape for now
751     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
752     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
753     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
754     sp_file_open(utf8_fn, NULL);
755     g_free(utf8_fn);
756     g_free(local_fn);
757     g_free(uri);
760 static void
761 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
763     sp_file_new(uri);
766 void
767 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
769     std::list<gchar *> sources;
770     sources.push_back( profile_path("templates") ); // first try user's local dir
771     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
773     // Use this loop to iterate through a list of possible document locations.
774     while (!sources.empty()) {
775         gchar *dirname = sources.front();
777         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
778             GError *err = 0;
779             GDir *dir = g_dir_open(dirname, 0, &err);
781             if (dir) {
782                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
783                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
784                         continue; // skip non-svg files
786                     gchar *basename = g_path_get_basename(file);
787                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
788                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
790                     gchar const *filepath = g_build_filename(dirname, file, NULL);
791                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
792                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
793                     g_free(dupfile);
794                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
795                     g_free(filename);
797                     gtk_widget_show(item);
798                     // how does "filepath" ever get freed?
799                     g_signal_connect(G_OBJECT(item),
800                                      "activate",
801                                      G_CALLBACK(sp_file_new_from_template),
802                                      (gpointer) filepath);
804                     if (view) {
805                         // set null tip for now; later use a description from the template file
806                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
807                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
808                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
809                     }
811                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
812                 }
813                 g_dir_close(dir);
814             }
815         }
817         // toss the dirname
818         g_free(dirname);
819         sources.pop_front();
820     }
823 void
824 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
826     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
827     //                                       checkitem_toggled, checkitem_update, 0);
828     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
829                                            checkitem_toggled, checkitem_update, 0);
830     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
831                                                                                    checkitem_toggled, checkitem_update, 0);
832     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
833                                            checkitem_toggled, checkitem_update, 0);
834     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
835                                            checkitem_toggled, checkitem_update, 0);
836     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
837                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
838     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
839                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
840     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
841                                            checkitem_toggled, checkitem_update, 0);
842     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
843                                            checkitem_toggled, checkitem_update, 0);
846 /** \brief  This function turns XML into a menu
847     \param  menus  This is the XML that defines the menu
848     \param  menu   Menu to be added to
849     \param  view   The View that this menu is being built for
851     This function is realitively simple as it just goes through the XML
852     and parses the individual elements.  In the case of a submenu, it
853     just calls itself recursively.  Because it is only reasonable to have
854     a couple of submenus, it is unlikely this will go more than two or
855     three times.
857     In the case of an unreconginzed verb, a menu item is made to identify
858     the verb that is missing, and display that.  The menu item is also made
859     insensitive.
860 */
861 void
862 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
864     if (menus == NULL) return;
865     if (menu == NULL)  return;
866     GSList *group = NULL;
868     for (Inkscape::XML::Node *menu_pntr = menus;
869          menu_pntr != NULL;
870          menu_pntr = menu_pntr->next()) {
871         if (!strcmp(menu_pntr->name(), "submenu")) {
872             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
873             GtkWidget *submenu = gtk_menu_new();
874             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
875             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
876             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
877             continue;
878         }
879         if (!strcmp(menu_pntr->name(), "verb")) {
880             gchar const *verb_name = menu_pntr->attribute("verb-id");
881             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
883             if (verb != NULL) {
884                 if (menu_pntr->attribute("radio") != NULL) {
885                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
886                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
887                     if (menu_pntr->attribute("default") != NULL) {
888                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
889                     }
890                 } else {
891                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
892                     group = NULL;
893                 }
894             } else {
895                 gchar string[120];
896                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
897                 string[119] = '\0'; /* may not be terminated */
898                 GtkWidget *item = gtk_menu_item_new_with_label(string);
899                 gtk_widget_set_sensitive(item, false);
900                 gtk_widget_show(item);
901                 gtk_menu_append(GTK_MENU(menu), item);
902             }
903             continue;
904         }
905         if (!strcmp(menu_pntr->name(), "separator")
906                 // This was spelt wrong in the original version
907                 // and so this is for backward compatibility.  It can
908                 // probably be dropped after the 0.44 release.
909              || !strcmp(menu_pntr->name(), "seperator")) {
910             GtkWidget *item = gtk_separator_menu_item_new();
911             gtk_widget_show(item);
912             gtk_menu_append(GTK_MENU(menu), item);
913             continue;
914         }
915         if (!strcmp(menu_pntr->name(), "template-list")) {
916             sp_menu_append_new_templates(menu, view);
917             continue;
918         }
919         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
920             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
922             // create recent files menu
923             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
924             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
925             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
926             // sort most recently used documents first to preserve previous behavior
927             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
928             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
930             // add filter to only open files added by Inkscape
931             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
932             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
933             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
935             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
936             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
938             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
939             continue;
940         }
941         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
942             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
943             continue;
944         }
945     }
948 /** \brief  Build the main tool bar
949     \param  view  View to build the bar for
951     Currently the main tool bar is built as a dynamic XML menu using
952     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
953     pass it to get items attached to it.
954 */
955 GtkWidget *
956 sp_ui_main_menubar(Inkscape::UI::View::View *view)
958     GtkWidget *mbar = gtk_menu_bar_new();
960 #ifdef GDK_WINDOWING_QUARTZ
961         ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
962 #endif
964     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
966 #ifdef GDK_WINDOWING_QUARTZ
967         return NULL;
968 #else
969     return mbar;
970 #endif
973 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
974     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
977 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
978     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
979     sp_desktop_selection(desktop)->clear();
982 GtkWidget *
983 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
985     GtkWidget *m;
986     SPDesktop *dt;
988     dt = static_cast<SPDesktop*>(view);
990     m = gtk_menu_new();
992     /* Undo and Redo */
993     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
994     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
996     /* Separator */
997     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
999     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1000     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1001     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1003     /* Separator */
1004     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1006     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1007     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1009     /* Item menu */
1010     if (item) {
1011         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1012         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1013     }
1015     /* layer menu */
1016     SPGroup *group=NULL;
1017     if (item) {
1018         if (SP_IS_GROUP(item)) {
1019             group = SP_GROUP(item);
1020         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1021             group = SP_GROUP(SP_OBJECT_PARENT(item));
1022         }
1023     }
1025     if (( group && group != dt->currentLayer() ) ||
1026         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1027         /* Separator */
1028         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1029     }
1031     if ( group && group != dt->currentLayer() ) {
1032         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1033         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1034         GtkWidget *w = gtk_menu_item_new_with_label(label);
1035         g_free(label);
1036         g_object_set_data(G_OBJECT(w), "group", group);
1037         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1038         gtk_widget_show(w);
1039         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1040     }
1042     if ( dt->currentLayer() != dt->currentRoot() ) {
1043         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1044             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1045             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1046             gtk_widget_show(w);
1047             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1049         }
1050     }
1052     return m;
1055 /* Drag and Drop */
1056 void
1057 sp_ui_drag_data_received(GtkWidget *widget,
1058                          GdkDragContext *drag_context,
1059                          gint x, gint y,
1060                          GtkSelectionData *data,
1061                          guint info,
1062                          guint /*event_time*/,
1063                          gpointer /*user_data*/)
1065     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1066     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1068     switch (info) {
1069 #if ENABLE_MAGIC_COLORS
1070         case APP_X_INKY_COLOR:
1071         {
1072             int destX = 0;
1073             int destY = 0;
1074             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1075             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1077             SPItem *item = desktop->item_at_point( where, true );
1078             if ( item )
1079             {
1080                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1082                 if ( data->length >= 8 ) {
1083                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1085                     gchar c[64] = {0};
1086                     // Careful about endian issues.
1087                     guint16* dataVals = (guint16*)data->data;
1088                     sp_svg_write_color( c, sizeof(c),
1089                                         SP_RGBA32_U_COMPOSE(
1090                                             0x0ff & (dataVals[0] >> 8),
1091                                             0x0ff & (dataVals[1] >> 8),
1092                                             0x0ff & (dataVals[2] >> 8),
1093                                             0xff // can't have transparency in the color itself
1094                                             //0x0ff & (data->data[3] >> 8),
1095                                             ));
1096                     SPCSSAttr *css = sp_repr_css_attr_new();
1097                     bool updatePerformed = false;
1099                     if ( data->length > 14 ) {
1100                         int flags = dataVals[4];
1102                         // piggie-backed palette entry info
1103                         int index = dataVals[5];
1104                         Glib::ustring palName;
1105                         for ( int i = 0; i < dataVals[6]; i++ ) {
1106                             palName += (gunichar)dataVals[7+i];
1107                         }
1109                         // Now hook in a magic tag of some sort.
1110                         if ( !palName.empty() && (flags & 1) ) {
1111                             gchar* str = g_strdup_printf("%d|", index);
1112                             palName.insert( 0, str );
1113                             g_free(str);
1114                             str = 0;
1116                             sp_object_setAttribute( SP_OBJECT(item),
1117                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1118                                                     palName.c_str(),
1119                                                     false );
1120                             item->updateRepr();
1122                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1123                             updatePerformed = true;
1124                         }
1125                     }
1127                     if ( !updatePerformed ) {
1128                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1129                     }
1131                     sp_desktop_apply_css_recursive( item, css, true );
1132                     item->updateRepr();
1134                     sp_document_done( doc , SP_VERB_NONE,
1135                                       _("Drop color"));
1137                     if ( srgbProf ) {
1138                         cmsCloseProfile( srgbProf );
1139                     }
1140                 }
1141             }
1142         }
1143         break;
1144 #endif // ENABLE_MAGIC_COLORS
1146         case APP_X_COLOR:
1147         {
1148             int destX = 0;
1149             int destY = 0;
1150             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1151             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1152             Geom::Point const button_dt(desktop->w2d(where));
1153             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1155             if ( data->length == 8 ) {
1156                 gchar c[64] = {0};
1157                 // Careful about endian issues.
1158                 guint16* dataVals = (guint16*)data->data;
1159                 sp_svg_write_color( c, 64,
1160                                     SP_RGBA32_U_COMPOSE(
1161                                         0x0ff & (dataVals[0] >> 8),
1162                                         0x0ff & (dataVals[1] >> 8),
1163                                         0x0ff & (dataVals[2] >> 8),
1164                                         0xff // can't have transparency in the color itself
1165                                         //0x0ff & (data->data[3] >> 8),
1166                                         ));
1168                 SPItem *item = desktop->item_at_point( where, true );
1170                 bool consumed = false;
1171                 if (desktop->event_context && desktop->event_context->get_drag()) {
1172                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1173                     if (consumed) {
1174                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1175                         desktop->event_context->get_drag()->updateDraggers();
1176                     }
1177                 }
1179                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1180                 //    consumed = sp_text_context_drop_color(c, button_doc);
1181                 //    if (consumed) {
1182                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1183                 //    }
1184                 //}
1186                 if (!consumed && item) {
1187                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1188                     if (fillnotstroke &&
1189                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1190                         Path *livarot_path = Path_for_item(item, true, true);
1191                         livarot_path->ConvertWithBackData(0.04);
1193                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1194                         if (position) {
1195                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1196                             Geom::Point delta = nearest - button_doc;
1197                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1198                             delta = desktop->d2w(delta);
1199                             double stroke_tolerance =
1200                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1201                                   desktop->current_zoom() *
1202                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1203                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1204                                   : 0.0)
1205                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1207                             if (Geom::L2 (delta) < stroke_tolerance) {
1208                                 fillnotstroke = false;
1209                             }
1210                         }
1211                         delete livarot_path;
1212                     }
1214                     SPCSSAttr *css = sp_repr_css_attr_new();
1215                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1217                     sp_desktop_apply_css_recursive( item, css, true );
1218                     item->updateRepr();
1220                     sp_document_done( doc , SP_VERB_NONE,
1221                                       _("Drop color"));
1222                 }
1223             }
1224         }
1225         break;
1227         case SVG_DATA:
1228         case SVG_XML_DATA: {
1229             gchar *svgdata = (gchar *)data->data;
1231             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1233             if (rnewdoc == NULL) {
1234                 sp_ui_error_dialog(_("Could not parse SVG data"));
1235                 return;
1236             }
1238             Inkscape::XML::Node *repr = rnewdoc->root();
1239             gchar const *style = repr->attribute("style");
1241             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1242             newgroup->setAttribute("style", style);
1244             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1245             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1246                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1247                 newgroup->appendChild(newchild);
1248             }
1250             Inkscape::GC::release(rnewdoc);
1252             // Add it to the current layer
1254             // Greg's edits to add intelligent positioning of svg drops
1255             SPObject *new_obj = NULL;
1256             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1258             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1259             selection->set(SP_ITEM(new_obj));
1260             // To move the imported object, we must temporarily set the "transform pattern with
1261             // object" option.
1262             {
1263                 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1264                 bool const saved_pref = prefs->getBool("/options/transform/pattern", true);
1265                 prefs->setBool("/options/transform/pattern", true);
1266                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1267                 Geom::OptRect sel_bbox = selection->bounds();
1268                 if (sel_bbox) {
1269                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1270                     sp_selection_move_relative(selection, m);
1271                 }
1272                 prefs->setBool("/options/transform/pattern", saved_pref);
1273             }
1275             Inkscape::GC::release(newgroup);
1276             sp_document_done(doc, SP_VERB_NONE,
1277                              _("Drop SVG"));
1278             break;
1279         }
1281         case URI_LIST: {
1282             gchar *uri = (gchar *)data->data;
1283             sp_ui_import_files(uri);
1284             break;
1285         }
1287         case PNG_DATA:
1288         case JPEG_DATA:
1289         case IMAGE_DATA: {
1290             char tmp[1024];
1292             StringOutputStream outs;
1293             Base64OutputStream b64out(outs);
1294             b64out.setColumnWidth(0);
1296             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1298             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1300             for ( int i = 0; i < data->length; i++ ) {
1301                 b64out.put( data->data[i] );
1302             }
1303             b64out.close();
1306             Glib::ustring str = outs.getString();
1308             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1309             str.insert( 0, tmp );
1310             newImage->setAttribute("xlink:href", str.c_str());
1312             GError *error = NULL;
1313             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1314             if ( loader ) {
1315                 error = NULL;
1316                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1317                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1318                     if ( pbuf ) {
1319                         int width = gdk_pixbuf_get_width(pbuf);
1320                         int height = gdk_pixbuf_get_height(pbuf);
1321                         snprintf( tmp, sizeof(tmp), "%d", width );
1322                         newImage->setAttribute("width", tmp);
1324                         snprintf( tmp, sizeof(tmp), "%d", height );
1325                         newImage->setAttribute("height", tmp);
1326                     }
1327                 }
1328             }
1330             // Add it to the current layer
1331             desktop->currentLayer()->appendChildRepr(newImage);
1333             Inkscape::GC::release(newImage);
1334             sp_document_done( doc , SP_VERB_NONE,
1335                               _("Drop bitmap image"));
1336             break;
1337         }
1338     }
1341 #include "gradient-context.h"
1343 void sp_ui_drag_motion( GtkWidget */*widget*/,
1344                         GdkDragContext */*drag_context*/,
1345                         gint /*x*/, gint /*y*/,
1346                         GtkSelectionData */*data*/,
1347                         guint /*info*/,
1348                         guint /*event_time*/,
1349                         gpointer /*user_data*/)
1351 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1352 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1355 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1358 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1359                               GdkDragContext */*drag_context*/,
1360                               guint /*event_time*/,
1361                               gpointer /*user_data*/ )
1363 //     g_message("drag-n-drop leave                at %d", event_time);
1366 static void
1367 sp_ui_import_files(gchar *buffer)
1369     GList *list = gnome_uri_list_extract_filenames(buffer);
1370     if (!list)
1371         return;
1372     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1373     g_list_foreach(list, (GFunc) g_free, NULL);
1374     g_list_free(list);
1377 static void
1378 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1380     if (filename) {
1381         if (strlen((char const *)filename) > 2)
1382             sp_ui_import_one_file((char const *)filename);
1383     }
1386 static void
1387 sp_ui_import_one_file(char const *filename)
1389     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1390     if (!doc) return;
1392     if (filename == NULL) return;
1394     // Pass off to common implementation
1395     // TODO might need to get the proper type of Inkscape::Extension::Extension
1396     file_import( doc, filename, NULL );
1399 void
1400 sp_ui_error_dialog(gchar const *message)
1402     GtkWidget *dlg;
1403     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1405     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1406                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1407     sp_transientize(dlg);
1408     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1409     gtk_dialog_run(GTK_DIALOG(dlg));
1410     gtk_widget_destroy(dlg);
1411     g_free(safeMsg);
1414 bool
1415 sp_ui_overwrite_file(gchar const *filename)
1417     bool return_value = FALSE;
1419     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1420         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1421         gchar* baseName = g_path_get_basename( filename );
1422         gchar* dirName = g_path_get_dirname( filename );
1423         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1424                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1425                                                                 GTK_MESSAGE_QUESTION,
1426                                                                 GTK_BUTTONS_NONE,
1427                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1428                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1429                                                                 baseName,
1430                                                                 dirName
1431             );
1432         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1433                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1434                                 _("Replace"), GTK_RESPONSE_YES,
1435                                 NULL );
1436         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1438         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1439             return_value = TRUE;
1440         } else {
1441             return_value = FALSE;
1442         }
1443         gtk_widget_destroy(dialog);
1444         g_free( baseName );
1445         g_free( dirName );
1446     } else {
1447         return_value = TRUE;
1448     }
1450     return return_value;
1453 static void
1454 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1456     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1459 static void
1460 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1462     void *child = GTK_BIN (data)->child;
1463     //child is either
1464     //- a GtkHBox, whose first child is a label displaying name if the menu
1465     //item has an accel key
1466     //- a GtkLabel if the menu has no accel key
1467     if (GTK_IS_LABEL(child)) {
1468         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1469     } else if (GTK_IS_HBOX(child)) {
1470         gtk_label_set_markup_with_mnemonic(
1471         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1472         name.c_str());
1473     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1474     //a menu item in yet another way...
1478 /*
1479   Local Variables:
1480   mode:c++
1481   c-file-style:"stroustrup"
1482   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1483   indent-tabs-mode:nil
1484   fill-column:99
1485   End:
1486 */
1487 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :