Code

implement dropping color onto stroke
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /**
4  * 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 "inkscape-private.h"
24 #include "extension/effect.h"
25 #include "widgets/icon.h"
26 #include "prefs-utils.h"
27 #include "path-prefix.h"
29 #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-flowtext.h"
44 #include "sp-namedview.h"
45 #include "ui/view/view.h"
47 #include "helper/action.h"
48 #include "helper/gnome-utils.h"
49 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "io/stringstream.h"
53 #include "io/base64stream.h"
55 #include "dialogs/dialog-events.h"
57 #include "message-context.h"
59 // Added for color drag-n-drop
60 #if ENABLE_LCMS
61 #include "lcms.h"
62 #endif // ENABLE_LCMS
63 #include "display/sp-canvas.h"
64 #include "color.h"
65 #include "svg/svg-color.h"
66 #include "desktop-style.h"
67 #include "style.h"
69 using Inkscape::IO::StringOutputStream;
70 using Inkscape::IO::Base64OutputStream;
72 /* Drag and Drop */
73 typedef enum {
74     URI_LIST,
75     SVG_XML_DATA,
76     SVG_DATA,
77     PNG_DATA,
78     JPEG_DATA,
79     IMAGE_DATA,
80     APP_X_INKY_COLOR,
81     APP_X_COLOR
82 } ui_drop_target_info;
84 static GtkTargetEntry ui_drop_target_entries [] = {
85     {"text/uri-list", 0, URI_LIST},
86     {"image/svg+xml", 0, SVG_XML_DATA},
87     {"image/svg",     0, SVG_DATA},
88     {"image/png",     0, PNG_DATA},
89     {"image/jpeg",    0, JPEG_DATA},
90 #if ENABLE_MAGIC_COLORS
91     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
92 #endif // ENABLE_MAGIC_COLORS
93     {"application/x-color", 0, APP_X_COLOR}
94 };
96 static GtkTargetEntry *completeDropTargets = 0;
97 static int completeDropTargetsCount = 0;
99 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
100 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
101 static void sp_ui_import_files(gchar *buffer);
102 static void sp_ui_import_one_file(char const *filename);
103 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
104 static void sp_ui_drag_data_received(GtkWidget *widget,
105                                      GdkDragContext *drag_context,
106                                      gint x, gint y,
107                                      GtkSelectionData *data,
108                                      guint info,
109                                      guint event_time,
110                                      gpointer user_data);
111 static void sp_ui_menu_item_set_sensitive(SPAction *action,
112                                           unsigned int sensitive,
113                                           void *data);
114 static void sp_ui_menu_item_set_name(SPAction *action, 
115                                      Glib::ustring name,
116                                      void *data);
118 SPActionEventVector menu_item_event_vector = {
119     {NULL},
120     NULL,
121     NULL, /* set_active */
122     sp_ui_menu_item_set_sensitive, /* set_sensitive */
123     NULL, /* set_shortcut */
124     sp_ui_menu_item_set_name /* set_name */
125 };
127 static const int MIN_ONSCREEN_DISTANCE = 50;
129 void
130 sp_create_window(SPViewWidget *vw, gboolean editable)
132     g_return_if_fail(vw != NULL);
133     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
135     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
137     if (editable) {
138                 g_object_set_data(G_OBJECT(vw), "window", win);
139                 
140                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
141                 SPDesktop* desktop = desktop_widget->desktop;
142                 
143                 desktop_widget->window = win;
145         /* fixme: doesn't allow making window any smaller than this */
146         win->set_default_size(640, 480);
147                 
148         win->set_data("desktop", desktop);
149         win->set_data("desktopwidget", desktop_widget);
150                 
151         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
152                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
153                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
154                 
155         gint prefs_geometry = 
156             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
157         if (prefs_geometry) {
158             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
159             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
160             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
161             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
162             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
163             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
164             if (pw>0 && ph>0) {
165                 gint w = MIN(gdk_screen_width(), pw);
166                 gint h = MIN(gdk_screen_height(), ph);
167                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
168                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
169                 if (w>0 && h>0 && x>0 && y>0) {
170                     x = MIN(gdk_screen_width() - w, x);
171                     y = MIN(gdk_screen_height() - h, y);
172                 }
173                 if (w>0 && h>0) {
174                     desktop->setWindowSize(w, h);
175                 }
177                 // Only restore xy for the first window so subsequent windows don't overlap exactly
178                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
179                 // other desktops?)
181                 // Empirically it seems that active_desktop==this desktop only the first time a
182                 // desktop is created.
183                 if (x>0 && y>0) {
184                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
185                     if (active_desktop == desktop || active_desktop==NULL) {
186                         desktop->setWindowPosition(NR::Point(x, y));
187                     }
188                 }
189             }
190             if (maxed) {
191                 win->maximize();
192             }
193             if (full) {
194                 win->fullscreen();
195             }
196         }
198     } else {
199         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
200     }
202     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
203     gtk_widget_show(GTK_WIDGET(vw));
205     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
206     {
207         std::vector<gchar*> types;
209         GSList *list = gdk_pixbuf_get_formats();
210         while ( list ) {
211             int i = 0;
212             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
213             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
214             for ( i = 0; typesXX[i]; i++ ) {
215                 types.push_back(g_strdup(typesXX[i]));
216             }
217             g_strfreev(typesXX);
219             list = g_slist_next(list);
220         }
221         completeDropTargetsCount = nui_drop_target_entries + types.size();
222         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
223         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
224             completeDropTargets[i] = ui_drop_target_entries[i];
225         }
226         int pos = nui_drop_target_entries;
228         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
229             completeDropTargets[pos].target = *it;
230             completeDropTargets[pos].flags = 0;
231             completeDropTargets[pos].info = IMAGE_DATA;
232             pos++;
233         }
234     }
236     gtk_drag_dest_set((GtkWidget*)win->gobj(),
237                       GTK_DEST_DEFAULT_ALL,
238                       completeDropTargets,
239                       completeDropTargetsCount,
240                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
241     g_signal_connect(G_OBJECT(win->gobj()),
242                      "drag_data_received",
243                      G_CALLBACK(sp_ui_drag_data_received),
244                      NULL);
245     win->show();
247     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
248     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
251 void
252 sp_ui_new_view()
254     SPDocument *document;
255     SPViewWidget *dtw;
257     document = SP_ACTIVE_DOCUMENT;
258     if (!document) return;
260     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
261     g_return_if_fail(dtw != NULL);
263     sp_create_window(dtw, TRUE);
264     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
265     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
268 /* TODO: not yet working */
269 /* To be re-enabled (by adding to menu) once it works. */
270 void
271 sp_ui_new_view_preview()
273     SPDocument *document;
274     SPViewWidget *dtw;
276     document = SP_ACTIVE_DOCUMENT;
277     if (!document) return;
279     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
280     g_return_if_fail(dtw != NULL);
281     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
283     sp_create_window(dtw, FALSE);
286 /**
287  * \param widget unused
288  */
289 void
290 sp_ui_close_view(GtkWidget *widget)
292     if (SP_ACTIVE_DESKTOP == NULL) {
293         return;
294     }
295     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
296         return;
297     }
298     SP_ACTIVE_DESKTOP->destroyWidget();
302 /**
303  *  sp_ui_close_all
304  *
305  *  This function is called to exit the program, and iterates through all
306  *  open document view windows, attempting to close each in turn.  If the
307  *  view has unsaved information, the user will be prompted to save,
308  *  discard, or cancel.
309  *
310  *  Returns FALSE if the user cancels the close_all operation, TRUE
311  *  otherwise.
312  */
313 unsigned int
314 sp_ui_close_all(void)
316     /* Iterate through all the windows, destroying each in the order they
317        become active */
318     while (SP_ACTIVE_DESKTOP) {
319         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
320             /* The user cancelled the operation, so end doing the close */
321             return FALSE;
322         }
323         SP_ACTIVE_DESKTOP->destroyWidget();
324     }
326     return TRUE;
329 /*
330  * Some day when the right-click menus are ready to start working
331  * smarter with the verbs, we'll need to change this NULL being
332  * sent to sp_action_perform to something useful, or set some kind
333  * of global "right-clicked position" variable for actions to
334  * investigate when they're called.
335  */
336 static void
337 sp_ui_menu_activate(void *object, SPAction *action)
339     sp_action_perform(action, NULL);
342 static void
343 sp_ui_menu_select_action(void *object, SPAction *action)
345     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
348 static void
349 sp_ui_menu_deselect_action(void *object, SPAction *action)
351     action->view->tipsMessageContext()->clear();
354 static void
355 sp_ui_menu_select(gpointer object, gpointer tip)
357     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
358     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
361 static void
362 sp_ui_menu_deselect(gpointer object)
364     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
365     view->tipsMessageContext()->clear();
368 /**
369  * sp_ui_menuitem_add_icon
370  *
371  * Creates and attaches a scaled icon to the given menu item.
372  *
373  */
374 void
375 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
377     GtkWidget *icon;
379     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
380     gtk_widget_show(icon);
381     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
382 } // end of sp_ui_menu_add_icon
384 /**
385  * sp_ui_menu_append_item
386  *
387  * Appends a UI item with specific info for Inkscape/Sodipodi.
388  *
389  */
390 static GtkWidget *
391 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
392                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
393                         gpointer data, gboolean with_mnemonic = TRUE )
395     GtkWidget *item;
397     if (stock) {
398         item = gtk_image_menu_item_new_from_stock(stock, NULL);
399     } else if (label) {
400         item = (with_mnemonic)
401             ? gtk_image_menu_item_new_with_mnemonic(label) :
402             gtk_image_menu_item_new_with_label(label);
403     } else {
404         item = gtk_separator_menu_item_new();
405     }
407     gtk_widget_show(item);
409     if (callback) {
410         g_signal_connect(G_OBJECT(item), "activate", callback, data);
411     }
413     if (tip && view) {
414         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
415         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
416         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
417     }
419     gtk_menu_append(GTK_MENU(menu), item);
421     return item;
423 } // end of sp_ui_menu_append_item()
425 /**
426 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
427  */
428 static gchar const *
429 sp_key_name(guint keyval)
431     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
432        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
433     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
435     if      (!strcmp(n, "asciicircum"))  return "^";
436     else if (!strcmp(n, "parenleft"  ))  return "(";
437     else if (!strcmp(n, "parenright" ))  return ")";
438     else if (!strcmp(n, "plus"       ))  return "+";
439     else if (!strcmp(n, "minus"      ))  return "-";
440     else if (!strcmp(n, "asterisk"   ))  return "*";
441     else if (!strcmp(n, "KP_Multiply"))  return "*";
442     else if (!strcmp(n, "Delete"     ))  return "Del";
443     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
444     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
445     else if (!strcmp(n, "grave"      ))  return "`";
446     else if (!strcmp(n, "numbersign" ))  return "#";
447     else if (!strcmp(n, "bar" ))  return "|";
448     else if (!strcmp(n, "slash" ))  return "/";
449     else if (!strcmp(n, "exclam" ))  return "!";
450     else return n;
454 /**
455  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
456  * \param c Points to a buffer at least 256 bytes long.
457  */
458 void
459 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
461     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
462      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
463      * Will probably need to change sp_shortcut_invoke callers.
464      *
465      * The existing gtk_label_new_with_mnemonic call can be replaced with
466      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
467      * gtk_label_set_text_with_mnemonic(lbl, str).
468      */
469     static GtkAccelLabelClass const &accel_lbl_cls
470         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
472     struct { unsigned test; char const *name; } const modifier_tbl[] = {
473         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
474         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
475         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
476     };
478     gchar *p = c;
479     gchar *end = p + 256;
481     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
482         if ((shortcut & modifier_tbl[i].test)
483             && (p < end))
484         {
485             p += g_snprintf(p, end - p, "%s%s",
486                             modifier_tbl[i].name,
487                             accel_lbl_cls.mod_separator);
488         }
489     }
490     if (p < end) {
491         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
492     }
493     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
496 void
497 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
499     SPAction     *action;
500     unsigned int shortcut;
501     gchar        *s;
502     gchar        key[256];
503     gchar        *atitle;
505     action = verb->get_action(NULL);
506     if (!action)
507         return;
509     atitle = sp_action_get_title(action);
511     s = g_stpcpy(c, atitle);
513     g_free(atitle);
515     shortcut = sp_shortcut_get_primary(verb);
516     if (shortcut) {
517         s = g_stpcpy(s, " (");
518         sp_ui_shortcut_string(shortcut, key);
519         s = g_stpcpy(s, key);
520         s = g_stpcpy(s, ")");
521     }
525 /**
526  * sp_ui_menu_append_item_from_verb
527  *
528  * Appends a custom menu UI from a verb.
529  *
530  */
532 static GtkWidget *
533 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
535     SPAction *action;
536     GtkWidget *item;
538     if (verb->get_code() == SP_VERB_NONE) {
540         item = gtk_separator_menu_item_new();
542     } else {
543         unsigned int shortcut;
545         action = verb->get_action(view);
547         if (!action) return NULL;
549         shortcut = sp_shortcut_get_primary(verb);
550         if (shortcut) {
551             gchar c[256];
552             sp_ui_shortcut_string(shortcut, c);
553             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
554             GtkWidget *const name_lbl = gtk_label_new("");
555             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
556             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
557             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
558             GtkWidget *const accel_lbl = gtk_label_new(c);
559             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
560             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
561             gtk_widget_show_all(hb);
562             if (radio) {
563                 item = gtk_radio_menu_item_new (group);
564             } else {
565                 item = gtk_image_menu_item_new();
566             }
567             gtk_container_add((GtkContainer *) item, hb);
568         } else {
569             if (radio) {
570                 item = gtk_radio_menu_item_new (group);
571             } else {
572                 item = gtk_image_menu_item_new ();
573             }
574             GtkWidget *const name_lbl = gtk_label_new("");
575             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
576             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
577             gtk_container_add((GtkContainer *) item, name_lbl);
578         }
580         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
581         if (!action->sensitive) {
582             gtk_widget_set_sensitive(item, FALSE);
583         }
585         if (action->image) {
586             sp_ui_menuitem_add_icon(item, action->image);
587         }
588         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
589         g_signal_connect( G_OBJECT(item), "activate",
590                           G_CALLBACK(sp_ui_menu_activate), action );
592         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
593         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
594     }
596     gtk_widget_show(item);
597     gtk_menu_append(GTK_MENU(menu), item);
599     return item;
601 } // end of sp_ui_menu_append_item_from_verb
604 static void
605 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
607     gchar const *pref = (gchar const *) user_data;
608     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
610     gchar const *pref_path;
611     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
612         pref_path = g_strconcat("fullscreen.", pref, NULL);
613     else
614         pref_path = g_strconcat("window.", pref, NULL);
616     gboolean checked = gtk_check_menu_item_get_active(menuitem);
617     prefs_set_int_attribute(pref_path, "state", checked);
619     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
622 static gboolean
623 checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
625     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
627     gchar const *pref = (gchar const *) user_data;
628     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
630     gchar const *pref_path;
631     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
632         pref_path = g_strconcat("fullscreen.", pref, NULL);
633     else
634         pref_path = g_strconcat("window.", pref, NULL);
636     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
638     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
639     gtk_check_menu_item_set_active(menuitem, ison);
640     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
642     return FALSE;
646 void
647 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
648                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
649                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
650                                        Inkscape::Verb *verb)
652     GtkWidget *item;
654     unsigned int shortcut = 0;
655     SPAction *action = NULL;
657     if (verb) {
658         shortcut = sp_shortcut_get_primary(verb);
659         action = verb->get_action(view);
660     }
662     if (verb && shortcut) {
663         gchar c[256];
664         sp_ui_shortcut_string(shortcut, c);
666         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
668         {
669             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
670             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
671             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
672         }
674         {
675             GtkWidget *l = gtk_label_new(c);
676             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
677             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
678         }
680         gtk_widget_show_all(hb);
682         item = gtk_check_menu_item_new();
683         gtk_container_add((GtkContainer *) item, hb);
684     } else {
685         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
686         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
687         item = gtk_check_menu_item_new();
688         gtk_container_add((GtkContainer *) item, l);
689     }
690 #if 0
691     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
692     if (!action->sensitive) {
693         gtk_widget_set_sensitive(item, FALSE);
694     }
695 #endif
696     gtk_widget_show(item);
698     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
700     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
702     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
703     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
705     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
706     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
709 static void
710 sp_recent_open(GtkWidget *widget, gchar const *uri)
712     sp_file_open(uri, NULL);
715 static void
716 sp_file_new_from_template(GtkWidget *widget, gchar const *uri)
718     sp_file_new(uri);
721 void
722 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
724     std::list<gchar *> sources;
725     sources.push_back( profile_path("templates") ); // first try user's local dir
726     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
728     // Use this loop to iterate through a list of possible document locations.
729     while (!sources.empty()) {
730         gchar *dirname = sources.front();
732         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
733             GError *err = 0;
734             GDir *dir = g_dir_open(dirname, 0, &err);
736             if (dir) {
737                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
738                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
739                         continue; // skip non-svg files
741                     gchar *basename = g_path_get_basename(file);
742                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
743                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
745                     gchar const *filepath = g_build_filename(dirname, file, NULL);
746                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
747                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
748                     g_free(dupfile);
749                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
750                     g_free(filename);
752                     gtk_widget_show(item);
753                     // how does "filepath" ever get freed?
754                     g_signal_connect(G_OBJECT(item),
755                                      "activate",
756                                      G_CALLBACK(sp_file_new_from_template),
757                                      (gpointer) filepath);
759                     if (view) {
760                         // set null tip for now; later use a description from the template file
761                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
762                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
763                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
764                     }
766                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
767                 }
768                 g_dir_close(dir);
769             }
770         }
772         // toss the dirname
773         g_free(dirname);
774         sources.pop_front();
775     }
778 void
779 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
781     gchar const **recent = prefs_get_recent_files();
782     if (recent) {
783         int i;
785         for (i = 0; recent[i] != NULL; i += 2) {
786             gchar const *uri = recent[i];
787             gchar const *name = recent[i + 1];
789             GtkWidget *item = gtk_menu_item_new_with_label(name);
790             gtk_widget_show(item);
791             g_signal_connect(G_OBJECT(item),
792                              "activate",
793                              G_CALLBACK(sp_recent_open),
794                              (gpointer)uri);
795             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
796         }
798         g_free(recent);
799     } else {
800         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
801         gtk_widget_show(item);
802         gtk_widget_set_sensitive(item, FALSE);
803         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
804     }
807 void
808 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
810     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
811     //                                       checkitem_toggled, checkitem_update, 0);
812     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
813                                            checkitem_toggled, checkitem_update, 0);
814     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
815                                            checkitem_toggled, checkitem_update, 0);
816     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
817                                            checkitem_toggled, checkitem_update, 0);
818     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
819                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
820     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
821                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
822     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
823                                            checkitem_toggled, checkitem_update, 0);
824     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
825                                            checkitem_toggled, checkitem_update, 0);
828 /** \brief  This function turns XML into a menu
829     \param  menus  This is the XML that defines the menu
830     \param  menu   Menu to be added to
831     \param  view   The View that this menu is being built for
833     This function is realitively simple as it just goes through the XML
834     and parses the individual elements.  In the case of a submenu, it
835     just calls itself recursively.  Because it is only reasonable to have
836     a couple of submenus, it is unlikely this will go more than two or
837     three times.
839     In the case of an unreconginzed verb, a menu item is made to identify
840     the verb that is missing, and display that.  The menu item is also made
841     insensitive.
842 */
843 void
844 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
846     if (menus == NULL) return;
847     if (menu == NULL)  return;
848     GSList *group = NULL;
850     for (Inkscape::XML::Node *menu_pntr = menus;
851          menu_pntr != NULL;
852          menu_pntr = menu_pntr->next()) {
853         if (!strcmp(menu_pntr->name(), "submenu")) {
854             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
855             GtkWidget *submenu = gtk_menu_new();
856             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
857             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
858             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
859             continue;
860         }
861         if (!strcmp(menu_pntr->name(), "verb")) {
862             gchar const *verb_name = menu_pntr->attribute("verb-id");
863             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
865             if (verb != NULL) {
866                 if (menu_pntr->attribute("radio") != NULL) {
867                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
868                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
869                     if (menu_pntr->attribute("default") != NULL) {
870                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
871                     }
872                 } else {
873                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
874                     group = NULL;
875                 }
876             } else {
877                 gchar string[120];
878                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
879                 string[119] = '\0'; /* may not be terminated */
880                 GtkWidget *item = gtk_menu_item_new_with_label(string);
881                 gtk_widget_set_sensitive(item, false);
882                 gtk_widget_show(item);
883                 gtk_menu_append(GTK_MENU(menu), item);
884             }
885             continue;
886         }
887         if (!strcmp(menu_pntr->name(), "separator")
888                 // This was spelt wrong in the original version
889                 // and so this is for backward compatibility.  It can
890                 // probably be dropped after the 0.44 release.
891              || !strcmp(menu_pntr->name(), "seperator")) {
892             GtkWidget *item = gtk_separator_menu_item_new();
893             gtk_widget_show(item);
894             gtk_menu_append(GTK_MENU(menu), item);
895             continue;
896         }
897         if (!strcmp(menu_pntr->name(), "template-list")) {
898             sp_menu_append_new_templates(menu, view);
899             continue;
900         }
901         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
902             sp_menu_append_recent_documents(menu, view);
903             continue;
904         }
905         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
906             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
907             continue;
908         }
909     }
912 /** \brief  Build the main tool bar
913     \param  view  View to build the bar for
915     Currently the main tool bar is built as a dynamic XML menu using
916     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
917     pass it to get items attached to it.
918 */
919 GtkWidget *
920 sp_ui_main_menubar(Inkscape::UI::View::View *view)
922     GtkWidget *mbar = gtk_menu_bar_new();
924     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
926     return mbar;
929 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
930     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
933 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
934     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
935     sp_desktop_selection(desktop)->clear();
938 GtkWidget *
939 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
941     GtkWidget *m;
942     SPDesktop *dt;
944     dt = static_cast<SPDesktop*>(view);
946     m = gtk_menu_new();
948     /* Undo and Redo */
949     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
950     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
952     /* Separator */
953     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
955     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
956     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
957     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
959     /* Separator */
960     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
962     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
963     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
965     /* Item menu */
966     if (item) {
967         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
968         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
969     }
971     /* layer menu */
972     SPGroup *group=NULL;
973     if (item) {
974         if (SP_IS_GROUP(item)) {
975             group = SP_GROUP(item);
976         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
977             group = SP_GROUP(SP_OBJECT_PARENT(item));
978         }
979     }
981     if (( group && group != dt->currentLayer() ) ||
982         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
983         /* Separator */
984         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
985     }
987     if ( group && group != dt->currentLayer() ) {
988         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
989         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
990         GtkWidget *w = gtk_menu_item_new_with_label(label);
991         g_free(label);
992         g_object_set_data(G_OBJECT(w), "group", group);
993         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
994         gtk_widget_show(w);
995         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
996     }
998     if ( dt->currentLayer() != dt->currentRoot() ) {
999         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1000             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1001             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1002             gtk_widget_show(w);
1003             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1005         }
1006     }
1008     return m;
1011 /* Drag and Drop */
1012 void
1013 sp_ui_drag_data_received(GtkWidget *widget,
1014                          GdkDragContext *drag_context,
1015                          gint x, gint y,
1016                          GtkSelectionData *data,
1017                          guint info,
1018                          guint event_time,
1019                          gpointer user_data)
1021     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1022     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1024     switch (info) {
1025 #if ENABLE_MAGIC_COLORS
1026         case APP_X_INKY_COLOR:
1027         {
1028             int destX = 0;
1029             int destY = 0;
1030             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1031             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1033             SPItem *item = desktop->item_at_point( where, true );
1034             if ( item )
1035             {
1036                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1038                 if ( data->length >= 8 ) {
1039                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1041                     gchar c[64] = {0};
1042                     // Careful about endian issues.
1043                     guint16* dataVals = (guint16*)data->data;
1044                     sp_svg_write_color( c, sizeof(c),
1045                                         SP_RGBA32_U_COMPOSE(
1046                                             0x0ff & (dataVals[0] >> 8),
1047                                             0x0ff & (dataVals[1] >> 8),
1048                                             0x0ff & (dataVals[2] >> 8),
1049                                             0xff // can't have transparency in the color itself
1050                                             //0x0ff & (data->data[3] >> 8),
1051                                             ));
1052                     SPCSSAttr *css = sp_repr_css_attr_new();
1053                     bool updatePerformed = false;
1055                     if ( data->length > 14 ) {
1056                         int flags = dataVals[4];
1058                         // piggie-backed palette entry info
1059                         int index = dataVals[5];
1060                         Glib::ustring palName;
1061                         for ( int i = 0; i < dataVals[6]; i++ ) {
1062                             palName += (gunichar)dataVals[7+i];
1063                         }
1065                         // Now hook in a magic tag of some sort.
1066                         if ( !palName.empty() && (flags & 1) ) {
1067                             gchar* str = g_strdup_printf("%d|", index);
1068                             palName.insert( 0, str );
1069                             g_free(str);
1070                             str = 0;
1072                             sp_object_setAttribute( SP_OBJECT(item),
1073                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1074                                                     palName.c_str(),
1075                                                     false );
1076                             item->updateRepr();
1078                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1079                             updatePerformed = true;
1080                         }
1081                     }
1083                     if ( !updatePerformed ) {
1084                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1085                     }
1087                     sp_desktop_apply_css_recursive( item, css, true );
1088                     item->updateRepr();
1090                     sp_document_done( doc , SP_VERB_NONE, 
1091                                       _("Drop color"));
1093                     if ( srgbProf ) {
1094                         cmsCloseProfile( srgbProf );
1095                     }
1096                 }
1097             }
1098         }
1099         break;
1100 #endif // ENABLE_MAGIC_COLORS
1102         case APP_X_COLOR:
1103         {
1104             int destX = 0;
1105             int destY = 0;
1106             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1107             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1108             NR::Point const button_dt(desktop->dt2doc(desktop->w2d(where)));
1110             SPItem *item = desktop->item_at_point( where, true );
1111             if ( item )
1112             {
1113                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1114                 if (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
1115                     Path *livarot_path = Path_for_item(item, true, true);
1116                     livarot_path->ConvertWithBackData(0.04);
1118                     NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_dt);
1119                     if (position) {
1120                         NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1121                         NR::Point delta = nearest - button_dt;
1122                         delta = desktop->d2w(delta);
1123                         double stroke_tolerance =
1124                             ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1125                               desktop->current_zoom() *
1126                               SP_OBJECT_STYLE (item)->stroke_width.computed *
1127                               sp_item_i2d_affine (item).expansion() * 0.5
1128                               : 0.0)
1129                             + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1131                         if (NR::L2 (delta) < stroke_tolerance) {
1132                             fillnotstroke = false;
1133                         }
1134                     }
1135                     delete livarot_path;
1136                 }
1138                 if ( data->length == 8 ) {
1139                     gchar c[64] = {0};
1140                     // Careful about endian issues.
1141                     guint16* dataVals = (guint16*)data->data;
1142                     sp_svg_write_color( c, 64,
1143                                         SP_RGBA32_U_COMPOSE(
1144                                             0x0ff & (dataVals[0] >> 8),
1145                                             0x0ff & (dataVals[1] >> 8),
1146                                             0x0ff & (dataVals[2] >> 8),
1147                                             0xff // can't have transparency in the color itself
1148                                             //0x0ff & (data->data[3] >> 8),
1149                                             ));
1150                     SPCSSAttr *css = sp_repr_css_attr_new();
1151                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1153                     sp_desktop_apply_css_recursive( item, css, true );
1154                     item->updateRepr();
1156                     sp_document_done( doc , SP_VERB_NONE, 
1157                                       _("Drop color"));
1158                 }
1159             }
1160         }
1161         break;
1163         case SVG_DATA:
1164         case SVG_XML_DATA: {
1165             gchar *svgdata = (gchar *)data->data;
1167             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1169             if (rnewdoc == NULL) {
1170                 sp_ui_error_dialog(_("Could not parse SVG data"));
1171                 return;
1172             }
1174             Inkscape::XML::Node *repr = rnewdoc->root();
1175             gchar const *style = repr->attribute("style");
1177             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1178             newgroup->setAttribute("style", style);
1180             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1181             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1182                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1183                 newgroup->appendChild(newchild);
1184             }
1186             Inkscape::GC::release(rnewdoc);
1188             // Add it to the current layer
1190             // Greg's edits to add intelligent positioning of svg drops
1191             SPObject *new_obj = NULL;
1192             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1194             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1195             selection->set(SP_ITEM(new_obj));
1196             // To move the imported object, we must temporarily set the "transform pattern with
1197             // object" option.
1198             {
1199                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1200                 prefs_set_int_attribute("options.transform", "pattern", 1);
1201                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1202                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1203                 if (sel_bbox) {
1204                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1205                     sp_selection_move_relative(selection, m);
1206                 }
1207                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1208             }
1210             Inkscape::GC::release(newgroup);
1211             sp_document_done(doc, SP_VERB_NONE, 
1212                              _("Drop SVG"));
1213             break;
1214         }
1216         case URI_LIST: {
1217             gchar *uri = (gchar *)data->data;
1218             sp_ui_import_files(uri);
1219             break;
1220         }
1222         case PNG_DATA:
1223         case JPEG_DATA:
1224         case IMAGE_DATA: {
1225             char tmp[1024];
1227             StringOutputStream outs;
1228             Base64OutputStream b64out(outs);
1229             b64out.setColumnWidth(0);
1231             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1233             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1235             for ( int i = 0; i < data->length; i++ ) {
1236                 b64out.put( data->data[i] );
1237             }
1238             b64out.close();
1241             Glib::ustring str = outs.getString();
1243             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1244             str.insert( 0, tmp );
1245             newImage->setAttribute("xlink:href", str.c_str());
1247             GError *error = NULL;
1248             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1249             if ( loader ) {
1250                 error = NULL;
1251                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1252                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1253                     if ( pbuf ) {
1254                         int width = gdk_pixbuf_get_width(pbuf);
1255                         int height = gdk_pixbuf_get_height(pbuf);
1256                         snprintf( tmp, sizeof(tmp), "%d", width );
1257                         newImage->setAttribute("width", tmp);
1259                         snprintf( tmp, sizeof(tmp), "%d", height );
1260                         newImage->setAttribute("height", tmp);
1261                     }
1262                 }
1263             }
1265             // Add it to the current layer
1266             desktop->currentLayer()->appendChildRepr(newImage);
1268             Inkscape::GC::release(newImage);
1269             sp_document_done( doc , SP_VERB_NONE, 
1270                               _("Drop bitmap image"));
1271             break;
1272         }
1273     }
1276 static void
1277 sp_ui_import_files(gchar *buffer)
1279     GList *list = gnome_uri_list_extract_filenames(buffer);
1280     if (!list)
1281         return;
1282     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1283     g_list_foreach(list, (GFunc) g_free, NULL);
1284     g_list_free(list);
1287 static void
1288 sp_ui_import_one_file_with_check(gpointer filename, gpointer unused)
1290     if (filename) {
1291         if (strlen((char const *)filename) > 2)
1292             sp_ui_import_one_file((char const *)filename);
1293     }
1296 static void
1297 sp_ui_import_one_file(char const *filename)
1299     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1300     if (!doc) return;
1302     if (filename == NULL) return;
1304     // Pass off to common implementation
1305     // TODO might need to get the proper type of Inkscape::Extension::Extension
1306     file_import( doc, filename, NULL );
1309 void
1310 sp_ui_error_dialog(gchar const *message)
1312     GtkWidget *dlg;
1313     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1315     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1316                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1317     sp_transientize(dlg);
1318     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1319     gtk_dialog_run(GTK_DIALOG(dlg));
1320     gtk_widget_destroy(dlg);
1321     g_free(safeMsg);
1324 bool
1325 sp_ui_overwrite_file(gchar const *filename)
1327     bool return_value = FALSE;
1329     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1330         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1331         gchar* baseName = g_path_get_basename( filename );
1332         gchar* dirName = g_path_get_dirname( filename );
1333         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1334                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1335                                                                 GTK_MESSAGE_QUESTION,
1336                                                                 GTK_BUTTONS_NONE,
1337                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1338                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1339                                                                 baseName,
1340                                                                 dirName
1341             );
1342         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1343                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1344                                 _("Replace"), GTK_RESPONSE_YES,
1345                                 NULL );
1346         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1348         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1349             return_value = TRUE;
1350         } else {
1351             return_value = FALSE;
1352         }
1353         gtk_widget_destroy(dialog);
1354         g_free( baseName );
1355         g_free( dirName );
1356     } else {
1357         return_value = TRUE;
1358     }
1360     return return_value;
1363 static void
1364 sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data)
1366     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1369 static void
1370 sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data)
1372     void *child = GTK_BIN (data)->child;
1373     //child is either
1374     //- a GtkHBox, whose first child is a label displaying name if the menu
1375     //item has an accel key
1376     //- a GtkLabel if the menu has no accel key
1377     if (GTK_IS_LABEL(child)) {
1378         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1379     } else if (GTK_IS_HBOX(child)) {
1380         gtk_label_set_markup_with_mnemonic(
1381         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1382         name.c_str());
1383     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1384     //a menu item in yet another way...
1388 /*
1389   Local Variables:
1390   mode:c++
1391   c-file-style:"stroustrup"
1392   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1393   indent-tabs-mode:nil
1394   fill-column:99
1395   End:
1396 */
1397 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :