Code

6806c69574fbb96df2571ba36eaac20592db0253
[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"
68 #include "event-context.h"
69 #include "gradient-drag.h"
71 using Inkscape::IO::StringOutputStream;
72 using Inkscape::IO::Base64OutputStream;
74 /* Drag and Drop */
75 typedef enum {
76     URI_LIST,
77     SVG_XML_DATA,
78     SVG_DATA,
79     PNG_DATA,
80     JPEG_DATA,
81     IMAGE_DATA,
82     APP_X_INKY_COLOR,
83     APP_X_COLOR
84 } ui_drop_target_info;
86 static GtkTargetEntry ui_drop_target_entries [] = {
87     {"text/uri-list", 0, URI_LIST},
88     {"image/svg+xml", 0, SVG_XML_DATA},
89     {"image/svg",     0, SVG_DATA},
90     {"image/png",     0, PNG_DATA},
91     {"image/jpeg",    0, JPEG_DATA},
92 #if ENABLE_MAGIC_COLORS
93     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
94 #endif // ENABLE_MAGIC_COLORS
95     {"application/x-color", 0, APP_X_COLOR}
96 };
98 static GtkTargetEntry *completeDropTargets = 0;
99 static int completeDropTargetsCount = 0;
101 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
102 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
103 static void sp_ui_import_files(gchar *buffer);
104 static void sp_ui_import_one_file(char const *filename);
105 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
106 static void sp_ui_drag_data_received(GtkWidget *widget,
107                                      GdkDragContext *drag_context,
108                                      gint x, gint y,
109                                      GtkSelectionData *data,
110                                      guint info,
111                                      guint event_time,
112                                      gpointer user_data);
113 static void sp_ui_menu_item_set_sensitive(SPAction *action,
114                                           unsigned int sensitive,
115                                           void *data);
116 static void sp_ui_menu_item_set_name(SPAction *action, 
117                                      Glib::ustring name,
118                                      void *data);
120 SPActionEventVector menu_item_event_vector = {
121     {NULL},
122     NULL,
123     NULL, /* set_active */
124     sp_ui_menu_item_set_sensitive, /* set_sensitive */
125     NULL, /* set_shortcut */
126     sp_ui_menu_item_set_name /* set_name */
127 };
129 static const int MIN_ONSCREEN_DISTANCE = 50;
131 void
132 sp_create_window(SPViewWidget *vw, gboolean editable)
134     g_return_if_fail(vw != NULL);
135     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
137     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
139     if (editable) {
140                 g_object_set_data(G_OBJECT(vw), "window", win);
141                 
142                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
143                 SPDesktop* desktop = desktop_widget->desktop;
144                 
145                 desktop_widget->window = win;
147         /* fixme: doesn't allow making window any smaller than this */
148         win->set_default_size(640, 480);
149                 
150         win->set_data("desktop", desktop);
151         win->set_data("desktopwidget", desktop_widget);
152                 
153         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
154                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
155                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
156                 
157         gint prefs_geometry = 
158             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
159         if (prefs_geometry) {
160             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
161             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
162             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
163             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
164             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
165             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
166             if (pw>0 && ph>0) {
167                 gint w = MIN(gdk_screen_width(), pw);
168                 gint h = MIN(gdk_screen_height(), ph);
169                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
170                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
171                 if (w>0 && h>0 && x>0 && y>0) {
172                     x = MIN(gdk_screen_width() - w, x);
173                     y = MIN(gdk_screen_height() - h, y);
174                 }
175                 if (w>0 && h>0) {
176                     desktop->setWindowSize(w, h);
177                 }
179                 // Only restore xy for the first window so subsequent windows don't overlap exactly
180                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
181                 // other desktops?)
183                 // Empirically it seems that active_desktop==this desktop only the first time a
184                 // desktop is created.
185                 if (x>0 && y>0) {
186                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
187                     if (active_desktop == desktop || active_desktop==NULL) {
188                         desktop->setWindowPosition(NR::Point(x, y));
189                     }
190                 }
191             }
192             if (maxed) {
193                 win->maximize();
194             }
195             if (full) {
196                 win->fullscreen();
197             }
198         }
200     } else {
201         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
202     }
204     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
205     gtk_widget_show(GTK_WIDGET(vw));
207     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
208     {
209         std::vector<gchar*> types;
211         GSList *list = gdk_pixbuf_get_formats();
212         while ( list ) {
213             int i = 0;
214             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
215             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
216             for ( i = 0; typesXX[i]; i++ ) {
217                 types.push_back(g_strdup(typesXX[i]));
218             }
219             g_strfreev(typesXX);
221             list = g_slist_next(list);
222         }
223         completeDropTargetsCount = nui_drop_target_entries + types.size();
224         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
225         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
226             completeDropTargets[i] = ui_drop_target_entries[i];
227         }
228         int pos = nui_drop_target_entries;
230         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
231             completeDropTargets[pos].target = *it;
232             completeDropTargets[pos].flags = 0;
233             completeDropTargets[pos].info = IMAGE_DATA;
234             pos++;
235         }
236     }
238     gtk_drag_dest_set((GtkWidget*)win->gobj(),
239                       GTK_DEST_DEFAULT_ALL,
240                       completeDropTargets,
241                       completeDropTargetsCount,
242                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
243     g_signal_connect(G_OBJECT(win->gobj()),
244                      "drag_data_received",
245                      G_CALLBACK(sp_ui_drag_data_received),
246                      NULL);
247     win->show();
249     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
250     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
253 void
254 sp_ui_new_view()
256     SPDocument *document;
257     SPViewWidget *dtw;
259     document = SP_ACTIVE_DOCUMENT;
260     if (!document) return;
262     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
263     g_return_if_fail(dtw != NULL);
265     sp_create_window(dtw, TRUE);
266     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
267     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
270 /* TODO: not yet working */
271 /* To be re-enabled (by adding to menu) once it works. */
272 void
273 sp_ui_new_view_preview()
275     SPDocument *document;
276     SPViewWidget *dtw;
278     document = SP_ACTIVE_DOCUMENT;
279     if (!document) return;
281     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
282     g_return_if_fail(dtw != NULL);
283     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
285     sp_create_window(dtw, FALSE);
288 /**
289  * \param widget unused
290  */
291 void
292 sp_ui_close_view(GtkWidget *widget)
294     if (SP_ACTIVE_DESKTOP == NULL) {
295         return;
296     }
297     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
298         return;
299     }
300     SP_ACTIVE_DESKTOP->destroyWidget();
304 /**
305  *  sp_ui_close_all
306  *
307  *  This function is called to exit the program, and iterates through all
308  *  open document view windows, attempting to close each in turn.  If the
309  *  view has unsaved information, the user will be prompted to save,
310  *  discard, or cancel.
311  *
312  *  Returns FALSE if the user cancels the close_all operation, TRUE
313  *  otherwise.
314  */
315 unsigned int
316 sp_ui_close_all(void)
318     /* Iterate through all the windows, destroying each in the order they
319        become active */
320     while (SP_ACTIVE_DESKTOP) {
321         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
322             /* The user cancelled the operation, so end doing the close */
323             return FALSE;
324         }
325         SP_ACTIVE_DESKTOP->destroyWidget();
326     }
328     return TRUE;
331 /*
332  * Some day when the right-click menus are ready to start working
333  * smarter with the verbs, we'll need to change this NULL being
334  * sent to sp_action_perform to something useful, or set some kind
335  * of global "right-clicked position" variable for actions to
336  * investigate when they're called.
337  */
338 static void
339 sp_ui_menu_activate(void *object, SPAction *action)
341     sp_action_perform(action, NULL);
344 static void
345 sp_ui_menu_select_action(void *object, SPAction *action)
347     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
350 static void
351 sp_ui_menu_deselect_action(void *object, SPAction *action)
353     action->view->tipsMessageContext()->clear();
356 static void
357 sp_ui_menu_select(gpointer object, gpointer tip)
359     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
360     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
363 static void
364 sp_ui_menu_deselect(gpointer object)
366     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
367     view->tipsMessageContext()->clear();
370 /**
371  * sp_ui_menuitem_add_icon
372  *
373  * Creates and attaches a scaled icon to the given menu item.
374  *
375  */
376 void
377 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
379     GtkWidget *icon;
381     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
382     gtk_widget_show(icon);
383     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
384 } // end of sp_ui_menu_add_icon
386 /**
387  * sp_ui_menu_append_item
388  *
389  * Appends a UI item with specific info for Inkscape/Sodipodi.
390  *
391  */
392 static GtkWidget *
393 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
394                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
395                         gpointer data, gboolean with_mnemonic = TRUE )
397     GtkWidget *item;
399     if (stock) {
400         item = gtk_image_menu_item_new_from_stock(stock, NULL);
401     } else if (label) {
402         item = (with_mnemonic)
403             ? gtk_image_menu_item_new_with_mnemonic(label) :
404             gtk_image_menu_item_new_with_label(label);
405     } else {
406         item = gtk_separator_menu_item_new();
407     }
409     gtk_widget_show(item);
411     if (callback) {
412         g_signal_connect(G_OBJECT(item), "activate", callback, data);
413     }
415     if (tip && view) {
416         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
417         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
418         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
419     }
421     gtk_menu_append(GTK_MENU(menu), item);
423     return item;
425 } // end of sp_ui_menu_append_item()
427 /**
428 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
429  */
430 static gchar const *
431 sp_key_name(guint keyval)
433     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
434        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
435     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
437     if      (!strcmp(n, "asciicircum"))  return "^";
438     else if (!strcmp(n, "parenleft"  ))  return "(";
439     else if (!strcmp(n, "parenright" ))  return ")";
440     else if (!strcmp(n, "plus"       ))  return "+";
441     else if (!strcmp(n, "minus"      ))  return "-";
442     else if (!strcmp(n, "asterisk"   ))  return "*";
443     else if (!strcmp(n, "KP_Multiply"))  return "*";
444     else if (!strcmp(n, "Delete"     ))  return "Del";
445     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
446     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
447     else if (!strcmp(n, "grave"      ))  return "`";
448     else if (!strcmp(n, "numbersign" ))  return "#";
449     else if (!strcmp(n, "bar" ))  return "|";
450     else if (!strcmp(n, "slash" ))  return "/";
451     else if (!strcmp(n, "exclam" ))  return "!";
452     else return n;
456 /**
457  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
458  * \param c Points to a buffer at least 256 bytes long.
459  */
460 void
461 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
463     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
464      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
465      * Will probably need to change sp_shortcut_invoke callers.
466      *
467      * The existing gtk_label_new_with_mnemonic call can be replaced with
468      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
469      * gtk_label_set_text_with_mnemonic(lbl, str).
470      */
471     static GtkAccelLabelClass const &accel_lbl_cls
472         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
474     struct { unsigned test; char const *name; } const modifier_tbl[] = {
475         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
476         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
477         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
478     };
480     gchar *p = c;
481     gchar *end = p + 256;
483     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
484         if ((shortcut & modifier_tbl[i].test)
485             && (p < end))
486         {
487             p += g_snprintf(p, end - p, "%s%s",
488                             modifier_tbl[i].name,
489                             accel_lbl_cls.mod_separator);
490         }
491     }
492     if (p < end) {
493         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
494     }
495     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
498 void
499 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
501     SPAction     *action;
502     unsigned int shortcut;
503     gchar        *s;
504     gchar        key[256];
505     gchar        *atitle;
507     action = verb->get_action(NULL);
508     if (!action)
509         return;
511     atitle = sp_action_get_title(action);
513     s = g_stpcpy(c, atitle);
515     g_free(atitle);
517     shortcut = sp_shortcut_get_primary(verb);
518     if (shortcut) {
519         s = g_stpcpy(s, " (");
520         sp_ui_shortcut_string(shortcut, key);
521         s = g_stpcpy(s, key);
522         s = g_stpcpy(s, ")");
523     }
527 /**
528  * sp_ui_menu_append_item_from_verb
529  *
530  * Appends a custom menu UI from a verb.
531  *
532  */
534 static GtkWidget *
535 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
537     SPAction *action;
538     GtkWidget *item;
540     if (verb->get_code() == SP_VERB_NONE) {
542         item = gtk_separator_menu_item_new();
544     } else {
545         unsigned int shortcut;
547         action = verb->get_action(view);
549         if (!action) return NULL;
551         shortcut = sp_shortcut_get_primary(verb);
552         if (shortcut) {
553             gchar c[256];
554             sp_ui_shortcut_string(shortcut, c);
555             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
556             GtkWidget *const name_lbl = gtk_label_new("");
557             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
558             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
559             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
560             GtkWidget *const accel_lbl = gtk_label_new(c);
561             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
562             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
563             gtk_widget_show_all(hb);
564             if (radio) {
565                 item = gtk_radio_menu_item_new (group);
566             } else {
567                 item = gtk_image_menu_item_new();
568             }
569             gtk_container_add((GtkContainer *) item, hb);
570         } else {
571             if (radio) {
572                 item = gtk_radio_menu_item_new (group);
573             } else {
574                 item = gtk_image_menu_item_new ();
575             }
576             GtkWidget *const name_lbl = gtk_label_new("");
577             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
578             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
579             gtk_container_add((GtkContainer *) item, name_lbl);
580         }
582         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
583         if (!action->sensitive) {
584             gtk_widget_set_sensitive(item, FALSE);
585         }
587         if (action->image) {
588             sp_ui_menuitem_add_icon(item, action->image);
589         }
590         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
591         g_signal_connect( G_OBJECT(item), "activate",
592                           G_CALLBACK(sp_ui_menu_activate), action );
594         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
595         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
596     }
598     gtk_widget_show(item);
599     gtk_menu_append(GTK_MENU(menu), item);
601     return item;
603 } // end of sp_ui_menu_append_item_from_verb
606 static void
607 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
609     gchar const *pref = (gchar const *) user_data;
610     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
612     gchar const *pref_path;
613     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
614         pref_path = g_strconcat("fullscreen.", pref, NULL);
615     else
616         pref_path = g_strconcat("window.", pref, NULL);
618     gboolean checked = gtk_check_menu_item_get_active(menuitem);
619     prefs_set_int_attribute(pref_path, "state", checked);
621     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
624 static gboolean
625 checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
627     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
629     gchar const *pref = (gchar const *) user_data;
630     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
632     gchar const *pref_path;
633     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
634         pref_path = g_strconcat("fullscreen.", pref, NULL);
635     else
636         pref_path = g_strconcat("window.", pref, NULL);
638     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
640     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
641     gtk_check_menu_item_set_active(menuitem, ison);
642     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
644     return FALSE;
648 void
649 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
650                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
651                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
652                                        Inkscape::Verb *verb)
654     GtkWidget *item;
656     unsigned int shortcut = 0;
657     SPAction *action = NULL;
659     if (verb) {
660         shortcut = sp_shortcut_get_primary(verb);
661         action = verb->get_action(view);
662     }
664     if (verb && shortcut) {
665         gchar c[256];
666         sp_ui_shortcut_string(shortcut, c);
668         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
670         {
671             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
672             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
673             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
674         }
676         {
677             GtkWidget *l = gtk_label_new(c);
678             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
679             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
680         }
682         gtk_widget_show_all(hb);
684         item = gtk_check_menu_item_new();
685         gtk_container_add((GtkContainer *) item, hb);
686     } else {
687         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
688         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
689         item = gtk_check_menu_item_new();
690         gtk_container_add((GtkContainer *) item, l);
691     }
692 #if 0
693     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
694     if (!action->sensitive) {
695         gtk_widget_set_sensitive(item, FALSE);
696     }
697 #endif
698     gtk_widget_show(item);
700     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
702     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
704     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
705     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
707     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
708     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
711 static void
712 sp_recent_open(GtkWidget *widget, gchar const *uri)
714     sp_file_open(uri, NULL);
717 static void
718 sp_file_new_from_template(GtkWidget *widget, gchar const *uri)
720     sp_file_new(uri);
723 void
724 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
726     std::list<gchar *> sources;
727     sources.push_back( profile_path("templates") ); // first try user's local dir
728     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
730     // Use this loop to iterate through a list of possible document locations.
731     while (!sources.empty()) {
732         gchar *dirname = sources.front();
734         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
735             GError *err = 0;
736             GDir *dir = g_dir_open(dirname, 0, &err);
738             if (dir) {
739                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
740                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
741                         continue; // skip non-svg files
743                     gchar *basename = g_path_get_basename(file);
744                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
745                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
747                     gchar const *filepath = g_build_filename(dirname, file, NULL);
748                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
749                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
750                     g_free(dupfile);
751                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
752                     g_free(filename);
754                     gtk_widget_show(item);
755                     // how does "filepath" ever get freed?
756                     g_signal_connect(G_OBJECT(item),
757                                      "activate",
758                                      G_CALLBACK(sp_file_new_from_template),
759                                      (gpointer) filepath);
761                     if (view) {
762                         // set null tip for now; later use a description from the template file
763                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
764                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
765                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
766                     }
768                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
769                 }
770                 g_dir_close(dir);
771             }
772         }
774         // toss the dirname
775         g_free(dirname);
776         sources.pop_front();
777     }
780 void
781 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
783     gchar const **recent = prefs_get_recent_files();
784     if (recent) {
785         int i;
787         for (i = 0; recent[i] != NULL; i += 2) {
788             gchar const *uri = recent[i];
789             gchar const *name = recent[i + 1];
791             GtkWidget *item = gtk_menu_item_new_with_label(name);
792             gtk_widget_show(item);
793             g_signal_connect(G_OBJECT(item),
794                              "activate",
795                              G_CALLBACK(sp_recent_open),
796                              (gpointer)uri);
797             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
798         }
800         g_free(recent);
801     } else {
802         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
803         gtk_widget_show(item);
804         gtk_widget_set_sensitive(item, FALSE);
805         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
806     }
809 void
810 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
812     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
813     //                                       checkitem_toggled, checkitem_update, 0);
814     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
815                                            checkitem_toggled, checkitem_update, 0);
816     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
817                                            checkitem_toggled, checkitem_update, 0);
818     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
819                                            checkitem_toggled, checkitem_update, 0);
820     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
821                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
822     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
823                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
824     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
825                                            checkitem_toggled, checkitem_update, 0);
826     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
827                                            checkitem_toggled, checkitem_update, 0);
830 /** \brief  This function turns XML into a menu
831     \param  menus  This is the XML that defines the menu
832     \param  menu   Menu to be added to
833     \param  view   The View that this menu is being built for
835     This function is realitively simple as it just goes through the XML
836     and parses the individual elements.  In the case of a submenu, it
837     just calls itself recursively.  Because it is only reasonable to have
838     a couple of submenus, it is unlikely this will go more than two or
839     three times.
841     In the case of an unreconginzed verb, a menu item is made to identify
842     the verb that is missing, and display that.  The menu item is also made
843     insensitive.
844 */
845 void
846 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
848     if (menus == NULL) return;
849     if (menu == NULL)  return;
850     GSList *group = NULL;
852     for (Inkscape::XML::Node *menu_pntr = menus;
853          menu_pntr != NULL;
854          menu_pntr = menu_pntr->next()) {
855         if (!strcmp(menu_pntr->name(), "submenu")) {
856             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
857             GtkWidget *submenu = gtk_menu_new();
858             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
859             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
860             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
861             continue;
862         }
863         if (!strcmp(menu_pntr->name(), "verb")) {
864             gchar const *verb_name = menu_pntr->attribute("verb-id");
865             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
867             if (verb != NULL) {
868                 if (menu_pntr->attribute("radio") != NULL) {
869                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
870                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
871                     if (menu_pntr->attribute("default") != NULL) {
872                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
873                     }
874                 } else {
875                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
876                     group = NULL;
877                 }
878             } else {
879                 gchar string[120];
880                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
881                 string[119] = '\0'; /* may not be terminated */
882                 GtkWidget *item = gtk_menu_item_new_with_label(string);
883                 gtk_widget_set_sensitive(item, false);
884                 gtk_widget_show(item);
885                 gtk_menu_append(GTK_MENU(menu), item);
886             }
887             continue;
888         }
889         if (!strcmp(menu_pntr->name(), "separator")
890                 // This was spelt wrong in the original version
891                 // and so this is for backward compatibility.  It can
892                 // probably be dropped after the 0.44 release.
893              || !strcmp(menu_pntr->name(), "seperator")) {
894             GtkWidget *item = gtk_separator_menu_item_new();
895             gtk_widget_show(item);
896             gtk_menu_append(GTK_MENU(menu), item);
897             continue;
898         }
899         if (!strcmp(menu_pntr->name(), "template-list")) {
900             sp_menu_append_new_templates(menu, view);
901             continue;
902         }
903         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
904             sp_menu_append_recent_documents(menu, view);
905             continue;
906         }
907         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
908             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
909             continue;
910         }
911     }
914 /** \brief  Build the main tool bar
915     \param  view  View to build the bar for
917     Currently the main tool bar is built as a dynamic XML menu using
918     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
919     pass it to get items attached to it.
920 */
921 GtkWidget *
922 sp_ui_main_menubar(Inkscape::UI::View::View *view)
924     GtkWidget *mbar = gtk_menu_bar_new();
926     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
928     return mbar;
931 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
932     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
935 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
936     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
937     sp_desktop_selection(desktop)->clear();
940 GtkWidget *
941 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
943     GtkWidget *m;
944     SPDesktop *dt;
946     dt = static_cast<SPDesktop*>(view);
948     m = gtk_menu_new();
950     /* Undo and Redo */
951     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
952     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
954     /* Separator */
955     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
957     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
958     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
959     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
961     /* Separator */
962     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
964     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
965     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
967     /* Item menu */
968     if (item) {
969         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
970         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
971     }
973     /* layer menu */
974     SPGroup *group=NULL;
975     if (item) {
976         if (SP_IS_GROUP(item)) {
977             group = SP_GROUP(item);
978         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
979             group = SP_GROUP(SP_OBJECT_PARENT(item));
980         }
981     }
983     if (( group && group != dt->currentLayer() ) ||
984         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
985         /* Separator */
986         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
987     }
989     if ( group && group != dt->currentLayer() ) {
990         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
991         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
992         GtkWidget *w = gtk_menu_item_new_with_label(label);
993         g_free(label);
994         g_object_set_data(G_OBJECT(w), "group", group);
995         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
996         gtk_widget_show(w);
997         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
998     }
1000     if ( dt->currentLayer() != dt->currentRoot() ) {
1001         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1002             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1003             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1004             gtk_widget_show(w);
1005             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1007         }
1008     }
1010     return m;
1013 /* Drag and Drop */
1014 void
1015 sp_ui_drag_data_received(GtkWidget *widget,
1016                          GdkDragContext *drag_context,
1017                          gint x, gint y,
1018                          GtkSelectionData *data,
1019                          guint info,
1020                          guint event_time,
1021                          gpointer user_data)
1023     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1024     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1026     switch (info) {
1027 #if ENABLE_MAGIC_COLORS
1028         case APP_X_INKY_COLOR:
1029         {
1030             int destX = 0;
1031             int destY = 0;
1032             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1033             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1035             SPItem *item = desktop->item_at_point( where, true );
1036             if ( item )
1037             {
1038                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1040                 if ( data->length >= 8 ) {
1041                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1043                     gchar c[64] = {0};
1044                     // Careful about endian issues.
1045                     guint16* dataVals = (guint16*)data->data;
1046                     sp_svg_write_color( c, sizeof(c),
1047                                         SP_RGBA32_U_COMPOSE(
1048                                             0x0ff & (dataVals[0] >> 8),
1049                                             0x0ff & (dataVals[1] >> 8),
1050                                             0x0ff & (dataVals[2] >> 8),
1051                                             0xff // can't have transparency in the color itself
1052                                             //0x0ff & (data->data[3] >> 8),
1053                                             ));
1054                     SPCSSAttr *css = sp_repr_css_attr_new();
1055                     bool updatePerformed = false;
1057                     if ( data->length > 14 ) {
1058                         int flags = dataVals[4];
1060                         // piggie-backed palette entry info
1061                         int index = dataVals[5];
1062                         Glib::ustring palName;
1063                         for ( int i = 0; i < dataVals[6]; i++ ) {
1064                             palName += (gunichar)dataVals[7+i];
1065                         }
1067                         // Now hook in a magic tag of some sort.
1068                         if ( !palName.empty() && (flags & 1) ) {
1069                             gchar* str = g_strdup_printf("%d|", index);
1070                             palName.insert( 0, str );
1071                             g_free(str);
1072                             str = 0;
1074                             sp_object_setAttribute( SP_OBJECT(item),
1075                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1076                                                     palName.c_str(),
1077                                                     false );
1078                             item->updateRepr();
1080                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1081                             updatePerformed = true;
1082                         }
1083                     }
1085                     if ( !updatePerformed ) {
1086                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1087                     }
1089                     sp_desktop_apply_css_recursive( item, css, true );
1090                     item->updateRepr();
1092                     sp_document_done( doc , SP_VERB_NONE, 
1093                                       _("Drop color"));
1095                     if ( srgbProf ) {
1096                         cmsCloseProfile( srgbProf );
1097                     }
1098                 }
1099             }
1100         }
1101         break;
1102 #endif // ENABLE_MAGIC_COLORS
1104         case APP_X_COLOR:
1105         {
1106             int destX = 0;
1107             int destY = 0;
1108             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1109             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1110             NR::Point const button_dt(desktop->w2d(where));
1111             NR::Point const button_doc(desktop->dt2doc(button_dt));
1113             if ( data->length == 8 ) {
1114                 gchar c[64] = {0};
1115                 // Careful about endian issues.
1116                 guint16* dataVals = (guint16*)data->data;
1117                 sp_svg_write_color( c, 64,
1118                                     SP_RGBA32_U_COMPOSE(
1119                                         0x0ff & (dataVals[0] >> 8),
1120                                         0x0ff & (dataVals[1] >> 8),
1121                                         0x0ff & (dataVals[2] >> 8),
1122                                         0xff // can't have transparency in the color itself
1123                                         //0x0ff & (data->data[3] >> 8),
1124                                         ));
1126                 SPItem *item = desktop->item_at_point( where, true );
1128                 bool consumed = false;
1129                 if (desktop->event_context && desktop->event_context->get_drag()) {
1130                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1131                     if (consumed) {
1132                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1133                         desktop->event_context->get_drag()->updateDraggers();
1134                     }
1135                 }
1137                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1138                 //    consumed = sp_text_context_drop_color(c, button_doc);
1139                 //    if (consumed) {
1140                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1141                 //    }
1142                 //}
1144                 if (!consumed && item) {
1145                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1146                     if (fillnotstroke && 
1147                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1148                         Path *livarot_path = Path_for_item(item, true, true);
1149                         livarot_path->ConvertWithBackData(0.04);
1151                         NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1152                         if (position) {
1153                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1154                             NR::Point delta = nearest - button_doc;
1155                             delta = desktop->d2w(delta);
1156                             double stroke_tolerance =
1157                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1158                                   desktop->current_zoom() *
1159                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1160                                   sp_item_i2d_affine (item).expansion() * 0.5
1161                                   : 0.0)
1162                                 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1164                             if (NR::L2 (delta) < stroke_tolerance) {
1165                                 fillnotstroke = false;
1166                             }
1167                         }
1168                         delete livarot_path;
1169                     }
1171                     SPCSSAttr *css = sp_repr_css_attr_new();
1172                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1174                     sp_desktop_apply_css_recursive( item, css, true );
1175                     item->updateRepr();
1177                     sp_document_done( doc , SP_VERB_NONE, 
1178                                       _("Drop color"));
1179                 }
1180             }
1181         }
1182         break;
1184         case SVG_DATA:
1185         case SVG_XML_DATA: {
1186             gchar *svgdata = (gchar *)data->data;
1188             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1190             if (rnewdoc == NULL) {
1191                 sp_ui_error_dialog(_("Could not parse SVG data"));
1192                 return;
1193             }
1195             Inkscape::XML::Node *repr = rnewdoc->root();
1196             gchar const *style = repr->attribute("style");
1198             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1199             newgroup->setAttribute("style", style);
1201             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1202             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1203                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1204                 newgroup->appendChild(newchild);
1205             }
1207             Inkscape::GC::release(rnewdoc);
1209             // Add it to the current layer
1211             // Greg's edits to add intelligent positioning of svg drops
1212             SPObject *new_obj = NULL;
1213             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1215             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1216             selection->set(SP_ITEM(new_obj));
1217             // To move the imported object, we must temporarily set the "transform pattern with
1218             // object" option.
1219             {
1220                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1221                 prefs_set_int_attribute("options.transform", "pattern", 1);
1222                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1223                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1224                 if (sel_bbox) {
1225                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1226                     sp_selection_move_relative(selection, m);
1227                 }
1228                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1229             }
1231             Inkscape::GC::release(newgroup);
1232             sp_document_done(doc, SP_VERB_NONE, 
1233                              _("Drop SVG"));
1234             break;
1235         }
1237         case URI_LIST: {
1238             gchar *uri = (gchar *)data->data;
1239             sp_ui_import_files(uri);
1240             break;
1241         }
1243         case PNG_DATA:
1244         case JPEG_DATA:
1245         case IMAGE_DATA: {
1246             char tmp[1024];
1248             StringOutputStream outs;
1249             Base64OutputStream b64out(outs);
1250             b64out.setColumnWidth(0);
1252             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1254             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1256             for ( int i = 0; i < data->length; i++ ) {
1257                 b64out.put( data->data[i] );
1258             }
1259             b64out.close();
1262             Glib::ustring str = outs.getString();
1264             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1265             str.insert( 0, tmp );
1266             newImage->setAttribute("xlink:href", str.c_str());
1268             GError *error = NULL;
1269             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1270             if ( loader ) {
1271                 error = NULL;
1272                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1273                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1274                     if ( pbuf ) {
1275                         int width = gdk_pixbuf_get_width(pbuf);
1276                         int height = gdk_pixbuf_get_height(pbuf);
1277                         snprintf( tmp, sizeof(tmp), "%d", width );
1278                         newImage->setAttribute("width", tmp);
1280                         snprintf( tmp, sizeof(tmp), "%d", height );
1281                         newImage->setAttribute("height", tmp);
1282                     }
1283                 }
1284             }
1286             // Add it to the current layer
1287             desktop->currentLayer()->appendChildRepr(newImage);
1289             Inkscape::GC::release(newImage);
1290             sp_document_done( doc , SP_VERB_NONE, 
1291                               _("Drop bitmap image"));
1292             break;
1293         }
1294     }
1297 static void
1298 sp_ui_import_files(gchar *buffer)
1300     GList *list = gnome_uri_list_extract_filenames(buffer);
1301     if (!list)
1302         return;
1303     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1304     g_list_foreach(list, (GFunc) g_free, NULL);
1305     g_list_free(list);
1308 static void
1309 sp_ui_import_one_file_with_check(gpointer filename, gpointer unused)
1311     if (filename) {
1312         if (strlen((char const *)filename) > 2)
1313             sp_ui_import_one_file((char const *)filename);
1314     }
1317 static void
1318 sp_ui_import_one_file(char const *filename)
1320     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1321     if (!doc) return;
1323     if (filename == NULL) return;
1325     // Pass off to common implementation
1326     // TODO might need to get the proper type of Inkscape::Extension::Extension
1327     file_import( doc, filename, NULL );
1330 void
1331 sp_ui_error_dialog(gchar const *message)
1333     GtkWidget *dlg;
1334     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1336     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1337                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1338     sp_transientize(dlg);
1339     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1340     gtk_dialog_run(GTK_DIALOG(dlg));
1341     gtk_widget_destroy(dlg);
1342     g_free(safeMsg);
1345 bool
1346 sp_ui_overwrite_file(gchar const *filename)
1348     bool return_value = FALSE;
1350     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1351         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1352         gchar* baseName = g_path_get_basename( filename );
1353         gchar* dirName = g_path_get_dirname( filename );
1354         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1355                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1356                                                                 GTK_MESSAGE_QUESTION,
1357                                                                 GTK_BUTTONS_NONE,
1358                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1359                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1360                                                                 baseName,
1361                                                                 dirName
1362             );
1363         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1364                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1365                                 _("Replace"), GTK_RESPONSE_YES,
1366                                 NULL );
1367         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1369         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1370             return_value = TRUE;
1371         } else {
1372             return_value = FALSE;
1373         }
1374         gtk_widget_destroy(dialog);
1375         g_free( baseName );
1376         g_free( dirName );
1377     } else {
1378         return_value = TRUE;
1379     }
1381     return return_value;
1384 static void
1385 sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data)
1387     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1390 static void
1391 sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data)
1393     void *child = GTK_BIN (data)->child;
1394     //child is either
1395     //- a GtkHBox, whose first child is a label displaying name if the menu
1396     //item has an accel key
1397     //- a GtkLabel if the menu has no accel key
1398     if (GTK_IS_LABEL(child)) {
1399         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1400     } else if (GTK_IS_HBOX(child)) {
1401         gtk_label_set_markup_with_mnemonic(
1402         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1403         name.c_str());
1404     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1405     //a menu item in yet another way...
1409 /*
1410   Local Variables:
1411   mode:c++
1412   c-file-style:"stroustrup"
1413   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1414   indent-tabs-mode:nil
1415   fill-column:99
1416   End:
1417 */
1418 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :