Code

warning cleanup
[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_drag_motion( GtkWidget *widget,
114                                GdkDragContext *drag_context,
115                                gint x, gint y,
116                                GtkSelectionData *data,
117                                guint info,
118                                guint event_time,
119                                gpointer user_data );
120 static void sp_ui_drag_leave( GtkWidget *widget,
121                               GdkDragContext *drag_context,
122                               guint event_time,
123                               gpointer user_data );
124 static void sp_ui_menu_item_set_sensitive(SPAction *action,
125                                           unsigned int sensitive,
126                                           void *data);
127 static void sp_ui_menu_item_set_name(SPAction *action, 
128                                      Glib::ustring name,
129                                      void *data);
131 SPActionEventVector menu_item_event_vector = {
132     {NULL},
133     NULL,
134     NULL, /* set_active */
135     sp_ui_menu_item_set_sensitive, /* set_sensitive */
136     NULL, /* set_shortcut */
137     sp_ui_menu_item_set_name /* set_name */
138 };
140 static const int MIN_ONSCREEN_DISTANCE = 50;
142 void
143 sp_create_window(SPViewWidget *vw, gboolean editable)
145     g_return_if_fail(vw != NULL);
146     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
148     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
150     if (editable) {
151                 g_object_set_data(G_OBJECT(vw), "window", win);
152                 
153                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
154                 SPDesktop* desktop = desktop_widget->desktop;
155                 
156                 desktop_widget->window = win;
158         /* fixme: doesn't allow making window any smaller than this */
159         win->set_default_size(640, 480);
160                 
161         win->set_data("desktop", desktop);
162         win->set_data("desktopwidget", desktop_widget);
163                 
164         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
165                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
166                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
167                 
168         gint prefs_geometry = 
169             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
170         if (prefs_geometry) {
171             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
172             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
173             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
174             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
175             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
176             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
177             if (pw>0 && ph>0) {
178                 gint w = MIN(gdk_screen_width(), pw);
179                 gint h = MIN(gdk_screen_height(), ph);
180                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
181                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
182                 if (w>0 && h>0 && x>0 && y>0) {
183                     x = MIN(gdk_screen_width() - w, x);
184                     y = MIN(gdk_screen_height() - h, y);
185                 }
186                 if (w>0 && h>0) {
187                     desktop->setWindowSize(w, h);
188                 }
190                 // Only restore xy for the first window so subsequent windows don't overlap exactly
191                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
192                 // other desktops?)
194                 // Empirically it seems that active_desktop==this desktop only the first time a
195                 // desktop is created.
196                 if (x>0 && y>0) {
197                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
198                     if (active_desktop == desktop || active_desktop==NULL) {
199                         desktop->setWindowPosition(NR::Point(x, y));
200                     }
201                 }
202             }
203             if (maxed) {
204                 win->maximize();
205             }
206             if (full) {
207                 win->fullscreen();
208             }
209         }
211     } else {
212         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
213     }
215     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
216     gtk_widget_show(GTK_WIDGET(vw));
218     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
219     {
220         std::vector<gchar*> types;
222         GSList *list = gdk_pixbuf_get_formats();
223         while ( list ) {
224             int i = 0;
225             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
226             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
227             for ( i = 0; typesXX[i]; i++ ) {
228                 types.push_back(g_strdup(typesXX[i]));
229             }
230             g_strfreev(typesXX);
232             list = g_slist_next(list);
233         }
234         completeDropTargetsCount = nui_drop_target_entries + types.size();
235         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
236         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
237             completeDropTargets[i] = ui_drop_target_entries[i];
238         }
239         int pos = nui_drop_target_entries;
241         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
242             completeDropTargets[pos].target = *it;
243             completeDropTargets[pos].flags = 0;
244             completeDropTargets[pos].info = IMAGE_DATA;
245             pos++;
246         }
247     }
249     gtk_drag_dest_set((GtkWidget*)win->gobj(),
250                       GTK_DEST_DEFAULT_ALL,
251                       completeDropTargets,
252                       completeDropTargetsCount,
253                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
256     g_signal_connect(G_OBJECT(win->gobj()),
257                      "drag_data_received",
258                      G_CALLBACK(sp_ui_drag_data_received),
259                      NULL);
260     g_signal_connect(G_OBJECT(win->gobj()),
261                      "drag_motion",
262                      G_CALLBACK(sp_ui_drag_motion),
263                      NULL);
264     g_signal_connect(G_OBJECT(win->gobj()),
265                      "drag_leave",
266                      G_CALLBACK(sp_ui_drag_leave),
267                      NULL);
268     win->show();
270     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
271     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
274 void
275 sp_ui_new_view()
277     SPDocument *document;
278     SPViewWidget *dtw;
280     document = SP_ACTIVE_DOCUMENT;
281     if (!document) return;
283     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
284     g_return_if_fail(dtw != NULL);
286     sp_create_window(dtw, TRUE);
287     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
288     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
291 /* TODO: not yet working */
292 /* To be re-enabled (by adding to menu) once it works. */
293 void
294 sp_ui_new_view_preview()
296     SPDocument *document;
297     SPViewWidget *dtw;
299     document = SP_ACTIVE_DOCUMENT;
300     if (!document) return;
302     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
303     g_return_if_fail(dtw != NULL);
304     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
306     sp_create_window(dtw, FALSE);
309 /**
310  * \param widget unused
311  */
312 void
313 sp_ui_close_view(GtkWidget */*widget*/)
315     if (SP_ACTIVE_DESKTOP == NULL) {
316         return;
317     }
318     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
319         return;
320     }
321     SP_ACTIVE_DESKTOP->destroyWidget();
325 /**
326  *  sp_ui_close_all
327  *
328  *  This function is called to exit the program, and iterates through all
329  *  open document view windows, attempting to close each in turn.  If the
330  *  view has unsaved information, the user will be prompted to save,
331  *  discard, or cancel.
332  *
333  *  Returns FALSE if the user cancels the close_all operation, TRUE
334  *  otherwise.
335  */
336 unsigned int
337 sp_ui_close_all(void)
339     /* Iterate through all the windows, destroying each in the order they
340        become active */
341     while (SP_ACTIVE_DESKTOP) {
342         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
343             /* The user cancelled the operation, so end doing the close */
344             return FALSE;
345         }
346         SP_ACTIVE_DESKTOP->destroyWidget();
347     }
349     return TRUE;
352 /*
353  * Some day when the right-click menus are ready to start working
354  * smarter with the verbs, we'll need to change this NULL being
355  * sent to sp_action_perform to something useful, or set some kind
356  * of global "right-clicked position" variable for actions to
357  * investigate when they're called.
358  */
359 static void
360 sp_ui_menu_activate(void */*object*/, SPAction *action)
362     sp_action_perform(action, NULL);
365 static void
366 sp_ui_menu_select_action(void */*object*/, SPAction *action)
368     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
371 static void
372 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
374     action->view->tipsMessageContext()->clear();
377 static void
378 sp_ui_menu_select(gpointer object, gpointer tip)
380     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
381     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
384 static void
385 sp_ui_menu_deselect(gpointer object)
387     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
388     view->tipsMessageContext()->clear();
391 /**
392  * sp_ui_menuitem_add_icon
393  *
394  * Creates and attaches a scaled icon to the given menu item.
395  *
396  */
397 void
398 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
400     GtkWidget *icon;
402     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
403     gtk_widget_show(icon);
404     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
405 } // end of sp_ui_menu_add_icon
407 /**
408  * sp_ui_menu_append_item
409  *
410  * Appends a UI item with specific info for Inkscape/Sodipodi.
411  *
412  */
413 static GtkWidget *
414 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
415                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
416                         gpointer data, gboolean with_mnemonic = TRUE )
418     GtkWidget *item;
420     if (stock) {
421         item = gtk_image_menu_item_new_from_stock(stock, NULL);
422     } else if (label) {
423         item = (with_mnemonic)
424             ? gtk_image_menu_item_new_with_mnemonic(label) :
425             gtk_image_menu_item_new_with_label(label);
426     } else {
427         item = gtk_separator_menu_item_new();
428     }
430     gtk_widget_show(item);
432     if (callback) {
433         g_signal_connect(G_OBJECT(item), "activate", callback, data);
434     }
436     if (tip && view) {
437         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
438         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
439         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
440     }
442     gtk_menu_append(GTK_MENU(menu), item);
444     return item;
446 } // end of sp_ui_menu_append_item()
448 /**
449 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
450  */
451 static gchar const *
452 sp_key_name(guint keyval)
454     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
455        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
456     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
458     if      (!strcmp(n, "asciicircum"))  return "^";
459     else if (!strcmp(n, "parenleft"  ))  return "(";
460     else if (!strcmp(n, "parenright" ))  return ")";
461     else if (!strcmp(n, "plus"       ))  return "+";
462     else if (!strcmp(n, "minus"      ))  return "-";
463     else if (!strcmp(n, "asterisk"   ))  return "*";
464     else if (!strcmp(n, "KP_Multiply"))  return "*";
465     else if (!strcmp(n, "Delete"     ))  return "Del";
466     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
467     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
468     else if (!strcmp(n, "grave"      ))  return "`";
469     else if (!strcmp(n, "numbersign" ))  return "#";
470     else if (!strcmp(n, "bar" ))  return "|";
471     else if (!strcmp(n, "slash" ))  return "/";
472     else if (!strcmp(n, "exclam" ))  return "!";
473     else return n;
477 /**
478  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
479  * \param c Points to a buffer at least 256 bytes long.
480  */
481 void
482 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
484     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
485      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
486      * Will probably need to change sp_shortcut_invoke callers.
487      *
488      * The existing gtk_label_new_with_mnemonic call can be replaced with
489      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
490      * gtk_label_set_text_with_mnemonic(lbl, str).
491      */
492     static GtkAccelLabelClass const &accel_lbl_cls
493         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
495     struct { unsigned test; char const *name; } const modifier_tbl[] = {
496         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
497         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
498         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
499     };
501     gchar *p = c;
502     gchar *end = p + 256;
504     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
505         if ((shortcut & modifier_tbl[i].test)
506             && (p < end))
507         {
508             p += g_snprintf(p, end - p, "%s%s",
509                             modifier_tbl[i].name,
510                             accel_lbl_cls.mod_separator);
511         }
512     }
513     if (p < end) {
514         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
515     }
516     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
519 void
520 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
522     SPAction     *action;
523     unsigned int shortcut;
524     gchar        *s;
525     gchar        key[256];
526     gchar        *atitle;
528     action = verb->get_action(NULL);
529     if (!action)
530         return;
532     atitle = sp_action_get_title(action);
534     s = g_stpcpy(c, atitle);
536     g_free(atitle);
538     shortcut = sp_shortcut_get_primary(verb);
539     if (shortcut) {
540         s = g_stpcpy(s, " (");
541         sp_ui_shortcut_string(shortcut, key);
542         s = g_stpcpy(s, key);
543         s = g_stpcpy(s, ")");
544     }
548 /**
549  * sp_ui_menu_append_item_from_verb
550  *
551  * Appends a custom menu UI from a verb.
552  *
553  */
555 static GtkWidget *
556 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
558     SPAction *action;
559     GtkWidget *item;
561     if (verb->get_code() == SP_VERB_NONE) {
563         item = gtk_separator_menu_item_new();
565     } else {
566         unsigned int shortcut;
568         action = verb->get_action(view);
570         if (!action) return NULL;
572         shortcut = sp_shortcut_get_primary(verb);
573         if (shortcut) {
574             gchar c[256];
575             sp_ui_shortcut_string(shortcut, c);
576             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
577             GtkWidget *const name_lbl = gtk_label_new("");
578             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
579             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
580             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
581             GtkWidget *const accel_lbl = gtk_label_new(c);
582             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
583             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
584             gtk_widget_show_all(hb);
585             if (radio) {
586                 item = gtk_radio_menu_item_new (group);
587             } else {
588                 item = gtk_image_menu_item_new();
589             }
590             gtk_container_add((GtkContainer *) item, hb);
591         } else {
592             if (radio) {
593                 item = gtk_radio_menu_item_new (group);
594             } else {
595                 item = gtk_image_menu_item_new ();
596             }
597             GtkWidget *const name_lbl = gtk_label_new("");
598             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
599             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
600             gtk_container_add((GtkContainer *) item, name_lbl);
601         }
603         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
604         if (!action->sensitive) {
605             gtk_widget_set_sensitive(item, FALSE);
606         }
608         if (action->image) {
609             sp_ui_menuitem_add_icon(item, action->image);
610         }
611         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
612         g_signal_connect( G_OBJECT(item), "activate",
613                           G_CALLBACK(sp_ui_menu_activate), action );
615         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
616         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
617     }
619     gtk_widget_show(item);
620     gtk_menu_append(GTK_MENU(menu), item);
622     return item;
624 } // end of sp_ui_menu_append_item_from_verb
627 static void
628 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
630     gchar const *pref = (gchar const *) user_data;
631     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
633     gchar const *pref_path;
634     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
635         pref_path = g_strconcat("fullscreen.", pref, NULL);
636     else
637         pref_path = g_strconcat("window.", pref, NULL);
639     gboolean checked = gtk_check_menu_item_get_active(menuitem);
640     prefs_set_int_attribute(pref_path, "state", checked);
642     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
645 static gboolean
646 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
648     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
650     gchar const *pref = (gchar const *) user_data;
651     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
653     gchar const *pref_path;
654     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
655         pref_path = g_strconcat("fullscreen.", pref, NULL);
656     else
657         pref_path = g_strconcat("window.", pref, NULL);
659     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
661     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
662     gtk_check_menu_item_set_active(menuitem, ison);
663     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
665     return FALSE;
669 void
670 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
671                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
672                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
673                                        Inkscape::Verb *verb)
675     GtkWidget *item;
677     unsigned int shortcut = 0;
678     SPAction *action = NULL;
680     if (verb) {
681         shortcut = sp_shortcut_get_primary(verb);
682         action = verb->get_action(view);
683     }
685     if (verb && shortcut) {
686         gchar c[256];
687         sp_ui_shortcut_string(shortcut, c);
689         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
691         {
692             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
693             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
694             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
695         }
697         {
698             GtkWidget *l = gtk_label_new(c);
699             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
700             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
701         }
703         gtk_widget_show_all(hb);
705         item = gtk_check_menu_item_new();
706         gtk_container_add((GtkContainer *) item, hb);
707     } else {
708         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
709         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
710         item = gtk_check_menu_item_new();
711         gtk_container_add((GtkContainer *) item, l);
712     }
713 #if 0
714     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
715     if (!action->sensitive) {
716         gtk_widget_set_sensitive(item, FALSE);
717     }
718 #endif
719     gtk_widget_show(item);
721     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
723     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
725     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
726     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
728     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
729     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
732 static void
733 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
735     sp_file_open(uri, NULL);
738 static void
739 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
741     sp_file_new(uri);
744 void
745 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
747     std::list<gchar *> sources;
748     sources.push_back( profile_path("templates") ); // first try user's local dir
749     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
751     // Use this loop to iterate through a list of possible document locations.
752     while (!sources.empty()) {
753         gchar *dirname = sources.front();
755         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
756             GError *err = 0;
757             GDir *dir = g_dir_open(dirname, 0, &err);
759             if (dir) {
760                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
761                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
762                         continue; // skip non-svg files
764                     gchar *basename = g_path_get_basename(file);
765                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
766                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
768                     gchar const *filepath = g_build_filename(dirname, file, NULL);
769                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
770                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
771                     g_free(dupfile);
772                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
773                     g_free(filename);
775                     gtk_widget_show(item);
776                     // how does "filepath" ever get freed?
777                     g_signal_connect(G_OBJECT(item),
778                                      "activate",
779                                      G_CALLBACK(sp_file_new_from_template),
780                                      (gpointer) filepath);
782                     if (view) {
783                         // set null tip for now; later use a description from the template file
784                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
785                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
786                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
787                     }
789                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
790                 }
791                 g_dir_close(dir);
792             }
793         }
795         // toss the dirname
796         g_free(dirname);
797         sources.pop_front();
798     }
801 void
802 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
804     gchar const **recent = prefs_get_recent_files();
805     if (recent) {
806         int i;
808         for (i = 0; recent[i] != NULL; i += 2) {
809             gchar const *uri = recent[i];
810             gchar const *name = recent[i + 1];
812             GtkWidget *item = gtk_menu_item_new_with_label(name);
813             gtk_widget_show(item);
814             g_signal_connect(G_OBJECT(item),
815                              "activate",
816                              G_CALLBACK(sp_recent_open),
817                              (gpointer)uri);
818             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
819         }
821         g_free(recent);
822     } else {
823         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
824         gtk_widget_show(item);
825         gtk_widget_set_sensitive(item, FALSE);
826         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
827     }
830 void
831 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
833     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
834     //                                       checkitem_toggled, checkitem_update, 0);
835     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
836                                            checkitem_toggled, checkitem_update, 0);
837     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
838                                            checkitem_toggled, checkitem_update, 0);
839     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
840                                            checkitem_toggled, checkitem_update, 0);
841     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
842                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
843     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
844                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
845     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
846                                            checkitem_toggled, checkitem_update, 0);
847     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
848                                            checkitem_toggled, checkitem_update, 0);
851 /** \brief  This function turns XML into a menu
852     \param  menus  This is the XML that defines the menu
853     \param  menu   Menu to be added to
854     \param  view   The View that this menu is being built for
856     This function is realitively simple as it just goes through the XML
857     and parses the individual elements.  In the case of a submenu, it
858     just calls itself recursively.  Because it is only reasonable to have
859     a couple of submenus, it is unlikely this will go more than two or
860     three times.
862     In the case of an unreconginzed verb, a menu item is made to identify
863     the verb that is missing, and display that.  The menu item is also made
864     insensitive.
865 */
866 void
867 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
869     if (menus == NULL) return;
870     if (menu == NULL)  return;
871     GSList *group = NULL;
873     for (Inkscape::XML::Node *menu_pntr = menus;
874          menu_pntr != NULL;
875          menu_pntr = menu_pntr->next()) {
876         if (!strcmp(menu_pntr->name(), "submenu")) {
877             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
878             GtkWidget *submenu = gtk_menu_new();
879             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
880             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
881             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
882             continue;
883         }
884         if (!strcmp(menu_pntr->name(), "verb")) {
885             gchar const *verb_name = menu_pntr->attribute("verb-id");
886             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
888             if (verb != NULL) {
889                 if (menu_pntr->attribute("radio") != NULL) {
890                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
891                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
892                     if (menu_pntr->attribute("default") != NULL) {
893                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
894                     }
895                 } else {
896                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
897                     group = NULL;
898                 }
899             } else {
900                 gchar string[120];
901                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
902                 string[119] = '\0'; /* may not be terminated */
903                 GtkWidget *item = gtk_menu_item_new_with_label(string);
904                 gtk_widget_set_sensitive(item, false);
905                 gtk_widget_show(item);
906                 gtk_menu_append(GTK_MENU(menu), item);
907             }
908             continue;
909         }
910         if (!strcmp(menu_pntr->name(), "separator")
911                 // This was spelt wrong in the original version
912                 // and so this is for backward compatibility.  It can
913                 // probably be dropped after the 0.44 release.
914              || !strcmp(menu_pntr->name(), "seperator")) {
915             GtkWidget *item = gtk_separator_menu_item_new();
916             gtk_widget_show(item);
917             gtk_menu_append(GTK_MENU(menu), item);
918             continue;
919         }
920         if (!strcmp(menu_pntr->name(), "template-list")) {
921             sp_menu_append_new_templates(menu, view);
922             continue;
923         }
924         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
925             sp_menu_append_recent_documents(menu, view);
926             continue;
927         }
928         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
929             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
930             continue;
931         }
932     }
935 /** \brief  Build the main tool bar
936     \param  view  View to build the bar for
938     Currently the main tool bar is built as a dynamic XML menu using
939     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
940     pass it to get items attached to it.
941 */
942 GtkWidget *
943 sp_ui_main_menubar(Inkscape::UI::View::View *view)
945     GtkWidget *mbar = gtk_menu_bar_new();
947     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
949     return mbar;
952 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
953     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
956 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
957     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
958     sp_desktop_selection(desktop)->clear();
961 GtkWidget *
962 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
964     GtkWidget *m;
965     SPDesktop *dt;
967     dt = static_cast<SPDesktop*>(view);
969     m = gtk_menu_new();
971     /* Undo and Redo */
972     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
973     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
975     /* Separator */
976     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
978     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
979     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
980     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
982     /* Separator */
983     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
985     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
986     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
988     /* Item menu */
989     if (item) {
990         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
991         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
992     }
994     /* layer menu */
995     SPGroup *group=NULL;
996     if (item) {
997         if (SP_IS_GROUP(item)) {
998             group = SP_GROUP(item);
999         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1000             group = SP_GROUP(SP_OBJECT_PARENT(item));
1001         }
1002     }
1004     if (( group && group != dt->currentLayer() ) ||
1005         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1006         /* Separator */
1007         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1008     }
1010     if ( group && group != dt->currentLayer() ) {
1011         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1012         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1013         GtkWidget *w = gtk_menu_item_new_with_label(label);
1014         g_free(label);
1015         g_object_set_data(G_OBJECT(w), "group", group);
1016         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1017         gtk_widget_show(w);
1018         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1019     }
1021     if ( dt->currentLayer() != dt->currentRoot() ) {
1022         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1023             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1024             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1025             gtk_widget_show(w);
1026             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1028         }
1029     }
1031     return m;
1034 /* Drag and Drop */
1035 void
1036 sp_ui_drag_data_received(GtkWidget *widget,
1037                          GdkDragContext *drag_context,
1038                          gint x, gint y,
1039                          GtkSelectionData *data,
1040                          guint info,
1041                          guint /*event_time*/,
1042                          gpointer /*user_data*/)
1044     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1045     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1047     switch (info) {
1048 #if ENABLE_MAGIC_COLORS
1049         case APP_X_INKY_COLOR:
1050         {
1051             int destX = 0;
1052             int destY = 0;
1053             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1054             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1056             SPItem *item = desktop->item_at_point( where, true );
1057             if ( item )
1058             {
1059                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1061                 if ( data->length >= 8 ) {
1062                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1064                     gchar c[64] = {0};
1065                     // Careful about endian issues.
1066                     guint16* dataVals = (guint16*)data->data;
1067                     sp_svg_write_color( c, sizeof(c),
1068                                         SP_RGBA32_U_COMPOSE(
1069                                             0x0ff & (dataVals[0] >> 8),
1070                                             0x0ff & (dataVals[1] >> 8),
1071                                             0x0ff & (dataVals[2] >> 8),
1072                                             0xff // can't have transparency in the color itself
1073                                             //0x0ff & (data->data[3] >> 8),
1074                                             ));
1075                     SPCSSAttr *css = sp_repr_css_attr_new();
1076                     bool updatePerformed = false;
1078                     if ( data->length > 14 ) {
1079                         int flags = dataVals[4];
1081                         // piggie-backed palette entry info
1082                         int index = dataVals[5];
1083                         Glib::ustring palName;
1084                         for ( int i = 0; i < dataVals[6]; i++ ) {
1085                             palName += (gunichar)dataVals[7+i];
1086                         }
1088                         // Now hook in a magic tag of some sort.
1089                         if ( !palName.empty() && (flags & 1) ) {
1090                             gchar* str = g_strdup_printf("%d|", index);
1091                             palName.insert( 0, str );
1092                             g_free(str);
1093                             str = 0;
1095                             sp_object_setAttribute( SP_OBJECT(item),
1096                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1097                                                     palName.c_str(),
1098                                                     false );
1099                             item->updateRepr();
1101                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1102                             updatePerformed = true;
1103                         }
1104                     }
1106                     if ( !updatePerformed ) {
1107                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1108                     }
1110                     sp_desktop_apply_css_recursive( item, css, true );
1111                     item->updateRepr();
1113                     sp_document_done( doc , SP_VERB_NONE, 
1114                                       _("Drop color"));
1116                     if ( srgbProf ) {
1117                         cmsCloseProfile( srgbProf );
1118                     }
1119                 }
1120             }
1121         }
1122         break;
1123 #endif // ENABLE_MAGIC_COLORS
1125         case APP_X_COLOR:
1126         {
1127             int destX = 0;
1128             int destY = 0;
1129             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1130             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1131             NR::Point const button_dt(desktop->w2d(where));
1132             NR::Point const button_doc(desktop->dt2doc(button_dt));
1134             if ( data->length == 8 ) {
1135                 gchar c[64] = {0};
1136                 // Careful about endian issues.
1137                 guint16* dataVals = (guint16*)data->data;
1138                 sp_svg_write_color( c, 64,
1139                                     SP_RGBA32_U_COMPOSE(
1140                                         0x0ff & (dataVals[0] >> 8),
1141                                         0x0ff & (dataVals[1] >> 8),
1142                                         0x0ff & (dataVals[2] >> 8),
1143                                         0xff // can't have transparency in the color itself
1144                                         //0x0ff & (data->data[3] >> 8),
1145                                         ));
1147                 SPItem *item = desktop->item_at_point( where, true );
1149                 bool consumed = false;
1150                 if (desktop->event_context && desktop->event_context->get_drag()) {
1151                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1152                     if (consumed) {
1153                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1154                         desktop->event_context->get_drag()->updateDraggers();
1155                     }
1156                 }
1158                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1159                 //    consumed = sp_text_context_drop_color(c, button_doc);
1160                 //    if (consumed) {
1161                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1162                 //    }
1163                 //}
1165                 if (!consumed && item) {
1166                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1167                     if (fillnotstroke && 
1168                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1169                         Path *livarot_path = Path_for_item(item, true, true);
1170                         livarot_path->ConvertWithBackData(0.04);
1172                         NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1173                         if (position) {
1174                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1175                             NR::Point delta = nearest - button_doc;
1176                             delta = desktop->d2w(delta);
1177                             double stroke_tolerance =
1178                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1179                                   desktop->current_zoom() *
1180                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1181                                   sp_item_i2d_affine (item).expansion() * 0.5
1182                                   : 0.0)
1183                                 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1185                             if (NR::L2 (delta) < stroke_tolerance) {
1186                                 fillnotstroke = false;
1187                             }
1188                         }
1189                         delete livarot_path;
1190                     }
1192                     SPCSSAttr *css = sp_repr_css_attr_new();
1193                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1195                     sp_desktop_apply_css_recursive( item, css, true );
1196                     item->updateRepr();
1198                     sp_document_done( doc , SP_VERB_NONE, 
1199                                       _("Drop color"));
1200                 }
1201             }
1202         }
1203         break;
1205         case SVG_DATA:
1206         case SVG_XML_DATA: {
1207             gchar *svgdata = (gchar *)data->data;
1209             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1211             if (rnewdoc == NULL) {
1212                 sp_ui_error_dialog(_("Could not parse SVG data"));
1213                 return;
1214             }
1216             Inkscape::XML::Node *repr = rnewdoc->root();
1217             gchar const *style = repr->attribute("style");
1219             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1220             newgroup->setAttribute("style", style);
1222             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1223             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1224                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1225                 newgroup->appendChild(newchild);
1226             }
1228             Inkscape::GC::release(rnewdoc);
1230             // Add it to the current layer
1232             // Greg's edits to add intelligent positioning of svg drops
1233             SPObject *new_obj = NULL;
1234             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1236             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1237             selection->set(SP_ITEM(new_obj));
1238             // To move the imported object, we must temporarily set the "transform pattern with
1239             // object" option.
1240             {
1241                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1242                 prefs_set_int_attribute("options.transform", "pattern", 1);
1243                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1244                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1245                 if (sel_bbox) {
1246                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1247                     sp_selection_move_relative(selection, m);
1248                 }
1249                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1250             }
1252             Inkscape::GC::release(newgroup);
1253             sp_document_done(doc, SP_VERB_NONE, 
1254                              _("Drop SVG"));
1255             break;
1256         }
1258         case URI_LIST: {
1259             gchar *uri = (gchar *)data->data;
1260             sp_ui_import_files(uri);
1261             break;
1262         }
1264         case PNG_DATA:
1265         case JPEG_DATA:
1266         case IMAGE_DATA: {
1267             char tmp[1024];
1269             StringOutputStream outs;
1270             Base64OutputStream b64out(outs);
1271             b64out.setColumnWidth(0);
1273             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1275             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1277             for ( int i = 0; i < data->length; i++ ) {
1278                 b64out.put( data->data[i] );
1279             }
1280             b64out.close();
1283             Glib::ustring str = outs.getString();
1285             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1286             str.insert( 0, tmp );
1287             newImage->setAttribute("xlink:href", str.c_str());
1289             GError *error = NULL;
1290             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1291             if ( loader ) {
1292                 error = NULL;
1293                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1294                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1295                     if ( pbuf ) {
1296                         int width = gdk_pixbuf_get_width(pbuf);
1297                         int height = gdk_pixbuf_get_height(pbuf);
1298                         snprintf( tmp, sizeof(tmp), "%d", width );
1299                         newImage->setAttribute("width", tmp);
1301                         snprintf( tmp, sizeof(tmp), "%d", height );
1302                         newImage->setAttribute("height", tmp);
1303                     }
1304                 }
1305             }
1307             // Add it to the current layer
1308             desktop->currentLayer()->appendChildRepr(newImage);
1310             Inkscape::GC::release(newImage);
1311             sp_document_done( doc , SP_VERB_NONE, 
1312                               _("Drop bitmap image"));
1313             break;
1314         }
1315     }
1318 #include "gradient-context.h"
1320 void sp_ui_drag_motion( GtkWidget */*widget*/,
1321                         GdkDragContext */*drag_context*/,
1322                         gint /*x*/, gint /*y*/,
1323                         GtkSelectionData */*data*/,
1324                         guint /*info*/,
1325                         guint /*event_time*/,
1326                         gpointer /*user_data*/)
1328 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1329 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1332 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1335 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1336                               GdkDragContext */*drag_context*/,
1337                               guint /*event_time*/,
1338                               gpointer /*user_data*/ )
1340 //     g_message("drag-n-drop leave                at %d", event_time);
1343 static void
1344 sp_ui_import_files(gchar *buffer)
1346     GList *list = gnome_uri_list_extract_filenames(buffer);
1347     if (!list)
1348         return;
1349     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1350     g_list_foreach(list, (GFunc) g_free, NULL);
1351     g_list_free(list);
1354 static void
1355 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1357     if (filename) {
1358         if (strlen((char const *)filename) > 2)
1359             sp_ui_import_one_file((char const *)filename);
1360     }
1363 static void
1364 sp_ui_import_one_file(char const *filename)
1366     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1367     if (!doc) return;
1369     if (filename == NULL) return;
1371     // Pass off to common implementation
1372     // TODO might need to get the proper type of Inkscape::Extension::Extension
1373     file_import( doc, filename, NULL );
1376 void
1377 sp_ui_error_dialog(gchar const *message)
1379     GtkWidget *dlg;
1380     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1382     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1383                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1384     sp_transientize(dlg);
1385     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1386     gtk_dialog_run(GTK_DIALOG(dlg));
1387     gtk_widget_destroy(dlg);
1388     g_free(safeMsg);
1391 bool
1392 sp_ui_overwrite_file(gchar const *filename)
1394     bool return_value = FALSE;
1396     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1397         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1398         gchar* baseName = g_path_get_basename( filename );
1399         gchar* dirName = g_path_get_dirname( filename );
1400         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1401                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1402                                                                 GTK_MESSAGE_QUESTION,
1403                                                                 GTK_BUTTONS_NONE,
1404                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1405                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1406                                                                 baseName,
1407                                                                 dirName
1408             );
1409         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1410                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1411                                 _("Replace"), GTK_RESPONSE_YES,
1412                                 NULL );
1413         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1415         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1416             return_value = TRUE;
1417         } else {
1418             return_value = FALSE;
1419         }
1420         gtk_widget_destroy(dialog);
1421         g_free( baseName );
1422         g_free( dirName );
1423     } else {
1424         return_value = TRUE;
1425     }
1427     return return_value;
1430 static void
1431 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1433     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1436 static void
1437 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1439     void *child = GTK_BIN (data)->child;
1440     //child is either
1441     //- a GtkHBox, whose first child is a label displaying name if the menu
1442     //item has an accel key
1443     //- a GtkLabel if the menu has no accel key
1444     if (GTK_IS_LABEL(child)) {
1445         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1446     } else if (GTK_IS_HBOX(child)) {
1447         gtk_label_set_markup_with_mnemonic(
1448         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1449         name.c_str());
1450     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1451     //a menu item in yet another way...
1455 /*
1456   Local Variables:
1457   mode:c++
1458   c-file-style:"stroustrup"
1459   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1460   indent-tabs-mode:nil
1461   fill-column:99
1462   End:
1463 */
1464 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :