Code

patch 1734633: option to save window geometry in prefs
[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-namedview.h"
44 #include "helper/action.h"
45 #include "helper/gnome-utils.h"
46 #include "helper/window.h"
48 #include "io/sys.h"
49 #include "io/stringstream.h"
50 #include "io/base64stream.h"
52 #include "dialogs/dialog-events.h"
54 #include "message-context.h"
56 // Added for color drag-n-drop
57 #if ENABLE_LCMS
58 #include "lcms.h"
59 #endif // ENABLE_LCMS
60 #include "display/sp-canvas.h"
61 #include "color.h"
62 #include "svg/svg-color.h"
63 #include "desktop-style.h"
64 #include "style.h"
67 using Inkscape::IO::StringOutputStream;
68 using Inkscape::IO::Base64OutputStream;
70 /* forward declaration */
71 static gint sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view);
72 static void sp_ui_state_event(GtkWidget *widget, GdkEventWindowState *event, SPDesktop*desktop);
75 /* Drag and Drop */
76 typedef enum {
77     URI_LIST,
78     SVG_XML_DATA,
79     SVG_DATA,
80     PNG_DATA,
81     JPEG_DATA,
82     IMAGE_DATA,
83     APP_X_INKY_COLOR,
84     APP_X_COLOR
85 } ui_drop_target_info;
87 static GtkTargetEntry ui_drop_target_entries [] = {
88     {"text/uri-list", 0, URI_LIST},
89     {"image/svg+xml", 0, SVG_XML_DATA},
90     {"image/svg",     0, SVG_DATA},
91     {"image/png",     0, PNG_DATA},
92     {"image/jpeg",    0, JPEG_DATA},
93 #if ENABLE_MAGIC_COLORS
94     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
95 #endif // ENABLE_MAGIC_COLORS
96     {"application/x-color", 0, APP_X_COLOR}
97 };
99 static GtkTargetEntry *completeDropTargets = 0;
100 static int completeDropTargetsCount = 0;
102 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
103 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
104 static void sp_ui_import_files(gchar *buffer);
105 static void sp_ui_import_one_file(char const *filename);
106 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
107 static void sp_ui_drag_data_received(GtkWidget *widget,
108                                      GdkDragContext *drag_context,
109                                      gint x, gint y,
110                                      GtkSelectionData *data,
111                                      guint info,
112                                      guint event_time,
113                                      gpointer user_data);
114 static void sp_ui_menu_item_set_sensitive(SPAction *action,
115                                           unsigned int sensitive,
116                                           void *data);
117 static void sp_ui_menu_item_set_name(SPAction *action, 
118                                      Glib::ustring name,
119                                      void *data);
121 SPActionEventVector menu_item_event_vector = {
122     {NULL},
123     NULL,
124     NULL, /* set_active */
125     sp_ui_menu_item_set_sensitive, /* set_sensitive */
126     NULL, /* set_shortcut */
127     sp_ui_menu_item_set_name /* set_name */
128 };
130 static const int MIN_ONSCREEN_DISTANCE = 50;
132 void
133 sp_create_window(SPViewWidget *vw, gboolean editable)
135     g_return_if_fail(vw != NULL);
136     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
138     GtkWidget *win = sp_window_new("", TRUE);
140     if (editable) {
141       g_object_set_data(G_OBJECT(vw), "window", win);
142       reinterpret_cast<SPDesktopWidget*>(vw)->window =
143         static_cast<GtkWindow*>((void*)win);
144     }
146     if (editable) {
147         SPDesktop* desktop = SP_DESKTOP_WIDGET(vw)->desktop;
149         /* fixme: doesn't allow making window any smaller than this */
150         gtk_window_set_default_size((GtkWindow *) win, 640, 480);
151         g_object_set_data(G_OBJECT(win), "desktop", desktop);
152         g_object_set_data(G_OBJECT(win), "desktopwidget", vw);
153         g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(sp_ui_delete), vw->view);
155         g_signal_connect(G_OBJECT(win), "window_state_event", G_CALLBACK(sp_ui_state_event), static_cast<SPDesktop*>(vw->view));
156         g_signal_connect(G_OBJECT(win), "focus_in_event", G_CALLBACK(sp_desktop_widget_set_focus), vw);
158         gint prefs_geometry = 
159             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
160         if (prefs_geometry) {
161             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
162             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
163             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
164             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
165             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
166             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
167             if (pw>0 && ph>0) {
168                 gint w = MIN(gdk_screen_width(), pw);
169                 gint h = MIN(gdk_screen_height(), ph);
170                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
171                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
172                 if (w>0 && h>0 && x>0 && y>0) {
173                     x = MIN(gdk_screen_width() - w, x);
174                     y = MIN(gdk_screen_height() - h, y);
175                 }
176                 if (w>0 && h>0) {
177                     desktop->setWindowSize(w, h);
178                 }
180                 // Only restore xy for the first window so subsequent windows don't overlap exactly
181                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
182                 // other desktops?)
184                 // Empirically it seems that active_desktop==this desktop only the first time a
185                 // desktop is created.
186                 if (x>0 && y>0) {
187                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
188                     if (active_desktop == desktop || active_desktop==NULL) {
189                         desktop->setWindowPosition(NR::Point(x, y));
190                     }
191                 }
192             }
193             if (maxed) {
194                 gtk_window_maximize(GTK_WINDOW(win));
195             }
196             if (full) {
197                 gtk_window_fullscreen(GTK_WINDOW(win));
198             }
199         }
201     } else {
202         gtk_window_set_policy(GTK_WINDOW(win), TRUE, TRUE, TRUE);
203     }
205     gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(vw));
206     gtk_widget_show(GTK_WIDGET(vw));
208     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
209     {
210         std::vector<gchar*> types;
212         GSList *list = gdk_pixbuf_get_formats();
213         while ( list ) {
214             int i = 0;
215             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
216             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
217             for ( i = 0; typesXX[i]; i++ ) {
218                 types.push_back(g_strdup(typesXX[i]));
219             }
220             g_strfreev(typesXX);
222             list = g_slist_next(list);
223         }
224         completeDropTargetsCount = nui_drop_target_entries + types.size();
225         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
226         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
227             completeDropTargets[i] = ui_drop_target_entries[i];
228         }
229         int pos = nui_drop_target_entries;
231         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
232             completeDropTargets[pos].target = *it;
233             completeDropTargets[pos].flags = 0;
234             completeDropTargets[pos].info = IMAGE_DATA;
235             pos++;
236         }
237     }
239     gtk_drag_dest_set(win,
240                       GTK_DEST_DEFAULT_ALL,
241                       completeDropTargets,
242                       completeDropTargetsCount,
243                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
244     g_signal_connect(G_OBJECT(win),
245                      "drag_data_received",
246                      G_CALLBACK(sp_ui_drag_data_received),
247                      NULL);
248     gtk_widget_show(win);
250     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
251     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
254 void
255 sp_ui_new_view()
257     SPDocument *document;
258     SPViewWidget *dtw;
260     document = SP_ACTIVE_DOCUMENT;
261     if (!document) return;
263     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
264     g_return_if_fail(dtw != NULL);
266     sp_create_window(dtw, TRUE);
267     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
268     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
271 /* TODO: not yet working */
272 /* To be re-enabled (by adding to menu) once it works. */
273 void
274 sp_ui_new_view_preview()
276     SPDocument *document;
277     SPViewWidget *dtw;
279     document = SP_ACTIVE_DOCUMENT;
280     if (!document) return;
282     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
283     g_return_if_fail(dtw != NULL);
284     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
286     sp_create_window(dtw, FALSE);
289 /**
290  * \param widget unused
291  */
292 void
293 sp_ui_close_view(GtkWidget *widget)
295     if (SP_ACTIVE_DESKTOP == NULL) {
296         return;
297     }
298     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
299         return;
300     }
301     SP_ACTIVE_DESKTOP->destroyWidget();
305 /**
306  *  sp_ui_close_all
307  *
308  *  This function is called to exit the program, and iterates through all
309  *  open document view windows, attempting to close each in turn.  If the
310  *  view has unsaved information, the user will be prompted to save,
311  *  discard, or cancel.
312  *
313  *  Returns FALSE if the user cancels the close_all operation, TRUE
314  *  otherwise.
315  */
316 unsigned int
317 sp_ui_close_all(void)
319     /* Iterate through all the windows, destroying each in the order they
320        become active */
321     while (SP_ACTIVE_DESKTOP) {
322         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
323             /* The user cancelled the operation, so end doing the close */
324             return FALSE;
325         }
326         SP_ACTIVE_DESKTOP->destroyWidget();
327     }
329     return TRUE;
332 static gint
333 sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view)
335     return view->shutdown();
338 /**
339  *  sp_ui_state_event
340  *
341  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
342  *  Since GTK doesn't have a way to query this state information directly, we
343  *  record it for the desktop here, and also possibly trigger a layout.
344  */
345 static void
346 sp_ui_state_event(GtkWidget *widget, GdkEventWindowState *event, SPDesktop* desktop)
348     // Record the desktop window's state
349     desktop->window_state = event->new_window_state;
351     // Layout may differ depending on full-screen mode or not
352     GdkWindowState changed = event->changed_mask;
353     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
354         desktop->layoutWidget();
355     }
356 /*
357     // debug info
358     g_message("State event desktop=0x%p", desktop);
359     GdkWindowState state = event->new_window_state;
360     GdkWindowState changed = event->changed_mask;
361     if (changed & GDK_WINDOW_STATE_WITHDRAWN) {
362         g_message("-- WIDTHDRAWN = %d", 0!=(state&GDK_WINDOW_STATE_WITHDRAWN));
363     }
364     if (changed & GDK_WINDOW_STATE_ICONIFIED) {
365         g_message("-- ICONIFIED = %d", 0!=(state&GDK_WINDOW_STATE_ICONIFIED));
366     }
367     if (changed & GDK_WINDOW_STATE_MAXIMIZED) {
368         g_message("-- MAXIMIZED = %d", 0!=(state&GDK_WINDOW_STATE_MAXIMIZED));
369     }
370     if (changed & GDK_WINDOW_STATE_STICKY) {
371         g_message("-- STICKY = %d", 0!=(state&GDK_WINDOW_STATE_STICKY));
372     }
373     if (changed & GDK_WINDOW_STATE_FULLSCREEN) {
374         g_message("-- FULLSCREEN = %d", 0!=(state&GDK_WINDOW_STATE_FULLSCREEN));
375     }
376     if (changed & GDK_WINDOW_STATE_ABOVE) {
377         g_message("-- ABOVE = %d", 0!=(state&GDK_WINDOW_STATE_ABOVE));
378     }
379     if (changed & GDK_WINDOW_STATE_BELOW) {
380         g_message("-- BELOW = %d", 0!=(state&GDK_WINDOW_STATE_BELOW));
381     }
382 */
385 /*
386  * Some day when the right-click menus are ready to start working
387  * smarter with the verbs, we'll need to change this NULL being
388  * sent to sp_action_perform to something useful, or set some kind
389  * of global "right-clicked position" variable for actions to
390  * investigate when they're called.
391  */
392 static void
393 sp_ui_menu_activate(void *object, SPAction *action)
395     sp_action_perform(action, NULL);
398 static void
399 sp_ui_menu_select_action(void *object, SPAction *action)
401     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
404 static void
405 sp_ui_menu_deselect_action(void *object, SPAction *action)
407     action->view->tipsMessageContext()->clear();
410 static void
411 sp_ui_menu_select(gpointer object, gpointer tip)
413     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
414     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
417 static void
418 sp_ui_menu_deselect(gpointer object)
420     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
421     view->tipsMessageContext()->clear();
424 /**
425  * sp_ui_menuitem_add_icon
426  *
427  * Creates and attaches a scaled icon to the given menu item.
428  *
429  */
430 void
431 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
433     GtkWidget *icon;
435     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
436     gtk_widget_show(icon);
437     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
438 } // end of sp_ui_menu_add_icon
440 /**
441  * sp_ui_menu_append_item
442  *
443  * Appends a UI item with specific info for Inkscape/Sodipodi.
444  *
445  */
446 static GtkWidget *
447 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
448                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
449                         gpointer data, gboolean with_mnemonic = TRUE )
451     GtkWidget *item;
453     if (stock) {
454         item = gtk_image_menu_item_new_from_stock(stock, NULL);
455     } else if (label) {
456         item = (with_mnemonic)
457             ? gtk_image_menu_item_new_with_mnemonic(label) :
458             gtk_image_menu_item_new_with_label(label);
459     } else {
460         item = gtk_separator_menu_item_new();
461     }
463     gtk_widget_show(item);
465     if (callback) {
466         g_signal_connect(G_OBJECT(item), "activate", callback, data);
467     }
469     if (tip && view) {
470         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
471         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
472         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
473     }
475     gtk_menu_append(GTK_MENU(menu), item);
477     return item;
479 } // end of sp_ui_menu_append_item()
481 /**
482 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
483  */
484 static gchar const *
485 sp_key_name(guint keyval)
487     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
488        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
489     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
491     if      (!strcmp(n, "asciicircum"))  return "^";
492     else if (!strcmp(n, "parenleft"  ))  return "(";
493     else if (!strcmp(n, "parenright" ))  return ")";
494     else if (!strcmp(n, "plus"       ))  return "+";
495     else if (!strcmp(n, "minus"      ))  return "-";
496     else if (!strcmp(n, "asterisk"   ))  return "*";
497     else if (!strcmp(n, "KP_Multiply"))  return "*";
498     else if (!strcmp(n, "Delete"     ))  return "Del";
499     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
500     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
501     else if (!strcmp(n, "grave"      ))  return "`";
502     else if (!strcmp(n, "numbersign" ))  return "#";
503     else if (!strcmp(n, "bar" ))  return "|";
504     else if (!strcmp(n, "slash" ))  return "/";
505     else if (!strcmp(n, "exclam" ))  return "!";
506     else return n;
510 /**
511  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
512  * \param c Points to a buffer at least 256 bytes long.
513  */
514 void
515 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
517     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
518      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
519      * Will probably need to change sp_shortcut_invoke callers.
520      *
521      * The existing gtk_label_new_with_mnemonic call can be replaced with
522      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
523      * gtk_label_set_text_with_mnemonic(lbl, str).
524      */
525     static GtkAccelLabelClass const &accel_lbl_cls
526         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
528     struct { unsigned test; char const *name; } const modifier_tbl[] = {
529         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
530         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
531         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
532     };
534     gchar *p = c;
535     gchar *end = p + 256;
537     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
538         if ((shortcut & modifier_tbl[i].test)
539             && (p < end))
540         {
541             p += g_snprintf(p, end - p, "%s%s",
542                             modifier_tbl[i].name,
543                             accel_lbl_cls.mod_separator);
544         }
545     }
546     if (p < end) {
547         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
548     }
549     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
552 void
553 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
555     SPAction     *action;
556     unsigned int shortcut;
557     gchar        *s;
558     gchar        key[256];
559     gchar        *atitle;
561     action = verb->get_action(NULL);
562     if (!action)
563         return;
565     atitle = sp_action_get_title(action);
567     s = g_stpcpy(c, atitle);
569     g_free(atitle);
571     shortcut = sp_shortcut_get_primary(verb);
572     if (shortcut) {
573         s = g_stpcpy(s, " (");
574         sp_ui_shortcut_string(shortcut, key);
575         s = g_stpcpy(s, key);
576         s = g_stpcpy(s, ")");
577     }
581 /**
582  * sp_ui_menu_append_item_from_verb
583  *
584  * Appends a custom menu UI from a verb.
585  *
586  */
588 static GtkWidget *
589 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
591     SPAction *action;
592     GtkWidget *item;
594     if (verb->get_code() == SP_VERB_NONE) {
596         item = gtk_separator_menu_item_new();
598     } else {
599         unsigned int shortcut;
601         action = verb->get_action(view);
603         if (!action) return NULL;
605         shortcut = sp_shortcut_get_primary(verb);
606         if (shortcut) {
607             gchar c[256];
608             sp_ui_shortcut_string(shortcut, c);
609             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
610             GtkWidget *const name_lbl = gtk_label_new("");
611             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
612             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
613             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
614             GtkWidget *const accel_lbl = gtk_label_new(c);
615             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
616             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
617             gtk_widget_show_all(hb);
618             if (radio) {
619                 item = gtk_radio_menu_item_new (group);
620             } else {
621                 item = gtk_image_menu_item_new();
622             }
623             gtk_container_add((GtkContainer *) item, hb);
624         } else {
625             if (radio) {
626                 item = gtk_radio_menu_item_new (group);
627             } else {
628                 item = gtk_image_menu_item_new ();
629             }
630             GtkWidget *const name_lbl = gtk_label_new("");
631             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
632             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
633             gtk_container_add((GtkContainer *) item, name_lbl);
634         }
636         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
637         if (!action->sensitive) {
638             gtk_widget_set_sensitive(item, FALSE);
639         }
641         if (action->image) {
642             sp_ui_menuitem_add_icon(item, action->image);
643         }
644         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
645         g_signal_connect( G_OBJECT(item), "activate",
646                           G_CALLBACK(sp_ui_menu_activate), action );
648         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
649         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
650     }
652     gtk_widget_show(item);
653     gtk_menu_append(GTK_MENU(menu), item);
655     return item;
657 } // end of sp_ui_menu_append_item_from_verb
660 static void
661 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
663     gchar const *pref = (gchar const *) user_data;
664     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
666     gchar const *pref_path;
667     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
668         pref_path = g_strconcat("fullscreen.", pref, NULL);
669     else
670         pref_path = g_strconcat("window.", pref, NULL);
672     gboolean checked = gtk_check_menu_item_get_active(menuitem);
673     prefs_set_int_attribute(pref_path, "state", checked);
675     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
678 static gboolean
679 checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
681     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
683     gchar const *pref = (gchar const *) user_data;
684     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
686     gchar const *pref_path;
687     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
688         pref_path = g_strconcat("fullscreen.", pref, NULL);
689     else
690         pref_path = g_strconcat("window.", pref, NULL);
692     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
694     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
695     gtk_check_menu_item_set_active(menuitem, ison);
696     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
698     return FALSE;
702 void
703 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
704                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
705                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
706                                        Inkscape::Verb *verb)
708     GtkWidget *item;
710     unsigned int shortcut = 0;
711     SPAction *action = NULL;
713     if (verb) {
714         shortcut = sp_shortcut_get_primary(verb);
715         action = verb->get_action(view);
716     }
718     if (verb && shortcut) {
719         gchar c[256];
720         sp_ui_shortcut_string(shortcut, c);
722         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
724         {
725             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
726             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
727             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
728         }
730         {
731             GtkWidget *l = gtk_label_new(c);
732             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
733             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
734         }
736         gtk_widget_show_all(hb);
738         item = gtk_check_menu_item_new();
739         gtk_container_add((GtkContainer *) item, hb);
740     } else {
741         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
742         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
743         item = gtk_check_menu_item_new();
744         gtk_container_add((GtkContainer *) item, l);
745     }
746 #if 0
747     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
748     if (!action->sensitive) {
749         gtk_widget_set_sensitive(item, FALSE);
750     }
751 #endif
752     gtk_widget_show(item);
754     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
756     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
758     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
759     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
761     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
762     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
765 static void
766 sp_recent_open(GtkWidget *widget, gchar const *uri)
768     sp_file_open(uri, NULL);
771 static void
772 sp_file_new_from_template(GtkWidget *widget, gchar const *uri)
774     sp_file_new(uri);
777 void
778 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
780     std::list<gchar *> sources;
781     sources.push_back( profile_path("templates") ); // first try user's local dir
782     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
784     // Use this loop to iterate through a list of possible document locations.
785     while (!sources.empty()) {
786         gchar *dirname = sources.front();
788         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
789             GError *err = 0;
790             GDir *dir = g_dir_open(dirname, 0, &err);
792             if (dir) {
793                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
794                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
795                         continue; // skip non-svg files
797                     gchar *basename = g_path_get_basename(file);
798                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
799                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
801                     gchar const *filepath = g_build_filename(dirname, file, NULL);
802                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
803                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
804                     g_free(dupfile);
805                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
806                     g_free(filename);
808                     gtk_widget_show(item);
809                     // how does "filepath" ever get freed?
810                     g_signal_connect(G_OBJECT(item),
811                                      "activate",
812                                      G_CALLBACK(sp_file_new_from_template),
813                                      (gpointer) filepath);
815                     if (view) {
816                         // set null tip for now; later use a description from the template file
817                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
818                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
819                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
820                     }
822                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
823                 }
824                 g_dir_close(dir);
825             }
826         }
828         // toss the dirname
829         g_free(dirname);
830         sources.pop_front();
831     }
834 void
835 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
837     gchar const **recent = prefs_get_recent_files();
838     if (recent) {
839         int i;
841         for (i = 0; recent[i] != NULL; i += 2) {
842             gchar const *uri = recent[i];
843             gchar const *name = recent[i + 1];
845             GtkWidget *item = gtk_menu_item_new_with_label(name);
846             gtk_widget_show(item);
847             g_signal_connect(G_OBJECT(item),
848                              "activate",
849                              G_CALLBACK(sp_recent_open),
850                              (gpointer)uri);
851             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
852         }
854         g_free(recent);
855     } else {
856         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
857         gtk_widget_show(item);
858         gtk_widget_set_sensitive(item, FALSE);
859         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
860     }
863 void
864 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
866     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
867     //                                       checkitem_toggled, checkitem_update, 0);
868     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
869                                            checkitem_toggled, checkitem_update, 0);
870     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
871                                            checkitem_toggled, checkitem_update, 0);
872     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
873                                            checkitem_toggled, checkitem_update, 0);
874     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
875                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
876     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
877                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
878     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
879                                            checkitem_toggled, checkitem_update, 0);
880     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
881                                            checkitem_toggled, checkitem_update, 0);
884 /** \brief  This function turns XML into a menu
885     \param  menus  This is the XML that defines the menu
886     \param  menu   Menu to be added to
887     \param  view   The View that this menu is being built for
889     This function is realitively simple as it just goes through the XML
890     and parses the individual elements.  In the case of a submenu, it
891     just calls itself recursively.  Because it is only reasonable to have
892     a couple of submenus, it is unlikely this will go more than two or
893     three times.
895     In the case of an unreconginzed verb, a menu item is made to identify
896     the verb that is missing, and display that.  The menu item is also made
897     insensitive.
898 */
899 void
900 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
902     if (menus == NULL) return;
903     if (menu == NULL)  return;
904     GSList *group = NULL;
906     for (Inkscape::XML::Node *menu_pntr = menus;
907          menu_pntr != NULL;
908          menu_pntr = menu_pntr->next()) {
909         if (!strcmp(menu_pntr->name(), "submenu")) {
910             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
911             GtkWidget *submenu = gtk_menu_new();
912             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
913             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
914             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
915             continue;
916         }
917         if (!strcmp(menu_pntr->name(), "verb")) {
918             gchar const *verb_name = menu_pntr->attribute("verb-id");
919             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
921             if (verb != NULL) {
922                 if (menu_pntr->attribute("radio") != NULL) {
923                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
924                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
925                     if (menu_pntr->attribute("default") != NULL) {
926                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
927                     }
928                 } else {
929                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
930                     group = NULL;
931                 }
932             } else {
933                 gchar string[120];
934                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
935                 string[119] = '\0'; /* may not be terminated */
936                 GtkWidget *item = gtk_menu_item_new_with_label(string);
937                 gtk_widget_set_sensitive(item, false);
938                 gtk_widget_show(item);
939                 gtk_menu_append(GTK_MENU(menu), item);
940             }
941             continue;
942         }
943         if (!strcmp(menu_pntr->name(), "separator")
944                 // This was spelt wrong in the original version
945                 // and so this is for backward compatibility.  It can
946                 // probably be dropped after the 0.44 release.
947              || !strcmp(menu_pntr->name(), "seperator")) {
948             GtkWidget *item = gtk_separator_menu_item_new();
949             gtk_widget_show(item);
950             gtk_menu_append(GTK_MENU(menu), item);
951             continue;
952         }
953         if (!strcmp(menu_pntr->name(), "template-list")) {
954             sp_menu_append_new_templates(menu, view);
955             continue;
956         }
957         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
958             sp_menu_append_recent_documents(menu, view);
959             continue;
960         }
961         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
962             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
963             continue;
964         }
965     }
968 /** \brief  Build the main tool bar
969     \param  view  View to build the bar for
971     Currently the main tool bar is built as a dynamic XML menu using
972     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
973     pass it to get items attached to it.
974 */
975 GtkWidget *
976 sp_ui_main_menubar(Inkscape::UI::View::View *view)
978     GtkWidget *mbar = gtk_menu_bar_new();
980     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
982     return mbar;
985 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
986     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
989 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
990     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
991     sp_desktop_selection(desktop)->clear();
994 GtkWidget *
995 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
997     GtkWidget *m;
998     SPDesktop *dt;
1000     dt = static_cast<SPDesktop*>(view);
1002     m = gtk_menu_new();
1004     /* Undo and Redo */
1005     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1006     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1008     /* Separator */
1009     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1011     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1012     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1013     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1015     /* Separator */
1016     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1018     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1019     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1021     /* Item menu */
1022     if (item) {
1023         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1024         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1025     }
1027     /* layer menu */
1028     SPGroup *group=NULL;
1029     if (item) {
1030         if (SP_IS_GROUP(item)) {
1031             group = SP_GROUP(item);
1032         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1033             group = SP_GROUP(SP_OBJECT_PARENT(item));
1034         }
1035     }
1037     if (( group && group != dt->currentLayer() ) ||
1038         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1039         /* Separator */
1040         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1041     }
1043     if ( group && group != dt->currentLayer() ) {
1044         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1045         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1046         GtkWidget *w = gtk_menu_item_new_with_label(label);
1047         g_free(label);
1048         g_object_set_data(G_OBJECT(w), "group", group);
1049         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1050         gtk_widget_show(w);
1051         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1052     }
1054     if ( dt->currentLayer() != dt->currentRoot() ) {
1055         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1056             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1057             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1058             gtk_widget_show(w);
1059             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1061         }
1062     }
1064     return m;
1067 /* Drag and Drop */
1068 void
1069 sp_ui_drag_data_received(GtkWidget *widget,
1070                          GdkDragContext *drag_context,
1071                          gint x, gint y,
1072                          GtkSelectionData *data,
1073                          guint info,
1074                          guint event_time,
1075                          gpointer user_data)
1077     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1078     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1080     switch (info) {
1081 #if ENABLE_MAGIC_COLORS
1082         case APP_X_INKY_COLOR:
1083         {
1084             int destX = 0;
1085             int destY = 0;
1086             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1087             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1089             SPItem *item = desktop->item_at_point( where, true );
1090             if ( item )
1091             {
1092                 if ( data->length >= 8 ) {
1093                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1095                     gchar c[64] = {0};
1096                     // Careful about endian issues.
1097                     guint16* dataVals = (guint16*)data->data;
1098                     sp_svg_write_color( c, 64,
1099                                         SP_RGBA32_U_COMPOSE(
1100                                             0x0ff & (dataVals[0] >> 8),
1101                                             0x0ff & (dataVals[1] >> 8),
1102                                             0x0ff & (dataVals[2] >> 8),
1103                                             0xff // can't have transparency in the color itself
1104                                             //0x0ff & (data->data[3] >> 8),
1105                                             ));
1106                     SPCSSAttr *css = sp_repr_css_attr_new();
1107                     bool updatePerformed = false;
1109                     if ( data->length > 14 ) {
1110                         int flags = dataVals[4];
1112                         // piggie-backed palette entry info
1113                         int index = dataVals[5];
1114                         Glib::ustring palName;
1115                         for ( int i = 0; i < dataVals[6]; i++ ) {
1116                             palName += (gunichar)dataVals[7+i];
1117                         }
1119                         // Now hook in a magic tag of some sort.
1120                         if ( !palName.empty() && (flags & 1) ) {
1121                             gchar* str = g_strdup_printf("%d|", index);
1122                             palName.insert( 0, str );
1123                             g_free(str);
1124                             str = 0;
1126                             sp_object_setAttribute( SP_OBJECT(item),
1127                                                     (drag_context->action != GDK_ACTION_MOVE) ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1128                                                     palName.c_str(),
1129                                                     false );
1130                             item->updateRepr();
1132                             sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1133                             updatePerformed = true;
1134                         }
1135                     }
1137                     if ( !updatePerformed ) {
1138                         sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1139                     }
1141                     sp_desktop_apply_css_recursive( item, css, true );
1142                     item->updateRepr();
1144                     sp_document_done( doc , SP_VERB_NONE, 
1145                                       _("Drop color"));
1147                     if ( srgbProf ) {
1148                         cmsCloseProfile( srgbProf );
1149                     }
1150                 }
1151             }
1152         }
1153         break;
1154 #endif // ENABLE_MAGIC_COLORS
1156         case APP_X_COLOR:
1157         {
1158             int destX = 0;
1159             int destY = 0;
1160             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1161             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1163             SPItem *item = desktop->item_at_point( where, true );
1164             if ( item )
1165             {
1166                 if ( data->length == 8 ) {
1167                     gchar c[64] = {0};
1168                     // Careful about endian issues.
1169                     guint16* dataVals = (guint16*)data->data;
1170                     sp_svg_write_color( c, 64,
1171                                         SP_RGBA32_U_COMPOSE(
1172                                             0x0ff & (dataVals[0] >> 8),
1173                                             0x0ff & (dataVals[1] >> 8),
1174                                             0x0ff & (dataVals[2] >> 8),
1175                                             0xff // can't have transparency in the color itself
1176                                             //0x0ff & (data->data[3] >> 8),
1177                                             ));
1178                     SPCSSAttr *css = sp_repr_css_attr_new();
1179                     sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1181                     sp_desktop_apply_css_recursive( item, css, true );
1182                     item->updateRepr();
1184                     sp_document_done( doc , SP_VERB_NONE, 
1185                                       _("Drop color"));
1186                 }
1187             }
1188         }
1189         break;
1191         case SVG_DATA:
1192         case SVG_XML_DATA: {
1193             gchar *svgdata = (gchar *)data->data;
1195             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1197             if (rnewdoc == NULL) {
1198                 sp_ui_error_dialog(_("Could not parse SVG data"));
1199                 return;
1200             }
1202             Inkscape::XML::Node *repr = rnewdoc->root();
1203             gchar const *style = repr->attribute("style");
1205             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1206             newgroup->setAttribute("style", style);
1208             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1209             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1210                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1211                 newgroup->appendChild(newchild);
1212             }
1214             Inkscape::GC::release(rnewdoc);
1216             // Add it to the current layer
1218             // Greg's edits to add intelligent positioning of svg drops
1219             SPObject *new_obj = NULL;
1220             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1222             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1223             selection->set(SP_ITEM(new_obj));
1224             // To move the imported object, we must temporarily set the "transform pattern with
1225             // object" option.
1226             {
1227                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1228                 prefs_set_int_attribute("options.transform", "pattern", 1);
1229                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1230                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1231                 if (sel_bbox) {
1232                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1233                     sp_selection_move_relative(selection, m);
1234                 }
1235                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1236             }
1238             Inkscape::GC::release(newgroup);
1239             sp_document_done(doc, SP_VERB_NONE, 
1240                              _("Drop SVG"));
1241             break;
1242         }
1244         case URI_LIST: {
1245             gchar *uri = (gchar *)data->data;
1246             sp_ui_import_files(uri);
1247             break;
1248         }
1250         case PNG_DATA:
1251         case JPEG_DATA:
1252         case IMAGE_DATA: {
1253             char tmp[1024];
1255             StringOutputStream outs;
1256             Base64OutputStream b64out(outs);
1257             b64out.setColumnWidth(0);
1259             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1261             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1263             for ( int i = 0; i < data->length; i++ ) {
1264                 b64out.put( data->data[i] );
1265             }
1266             b64out.close();
1269             Glib::ustring str = outs.getString();
1271             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1272             str.insert( 0, tmp );
1273             newImage->setAttribute("xlink:href", str.c_str());
1275             GError *error = NULL;
1276             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1277             if ( loader ) {
1278                 error = NULL;
1279                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1280                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1281                     if ( pbuf ) {
1282                         int width = gdk_pixbuf_get_width(pbuf);
1283                         int height = gdk_pixbuf_get_height(pbuf);
1284                         snprintf( tmp, sizeof(tmp), "%d", width );
1285                         newImage->setAttribute("width", tmp);
1287                         snprintf( tmp, sizeof(tmp), "%d", height );
1288                         newImage->setAttribute("height", tmp);
1289                     }
1290                 }
1291             }
1293             // Add it to the current layer
1294             desktop->currentLayer()->appendChildRepr(newImage);
1296             Inkscape::GC::release(newImage);
1297             sp_document_done( doc , SP_VERB_NONE, 
1298                               _("Drop bitmap image"));
1299             break;
1300         }
1301     }
1304 static void
1305 sp_ui_import_files(gchar *buffer)
1307     GList *list = gnome_uri_list_extract_filenames(buffer);
1308     if (!list)
1309         return;
1310     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1311     g_list_foreach(list, (GFunc) g_free, NULL);
1312     g_list_free(list);
1315 static void
1316 sp_ui_import_one_file_with_check(gpointer filename, gpointer unused)
1318     if (filename) {
1319         if (strlen((char const *)filename) > 2)
1320             sp_ui_import_one_file((char const *)filename);
1321     }
1324 static void
1325 sp_ui_import_one_file(char const *filename)
1327     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1328     if (!doc) return;
1330     if (filename == NULL) return;
1332     // Pass off to common implementation
1333     // TODO might need to get the proper type of Inkscape::Extension::Extension
1334     file_import( doc, filename, NULL );
1337 void
1338 sp_ui_error_dialog(gchar const *message)
1340     GtkWidget *dlg;
1341     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1343     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1344                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1345     sp_transientize(dlg);
1346     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1347     gtk_dialog_run(GTK_DIALOG(dlg));
1348     gtk_widget_destroy(dlg);
1349     g_free(safeMsg);
1352 bool
1353 sp_ui_overwrite_file(gchar const *filename)
1355     bool return_value = FALSE;
1357     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1358         GtkWidget* ancestor = 0;
1359         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1360         if ( desktop ) {
1361             desktop->getToplevel( ancestor );
1362         }
1363         GtkWindow *window = GTK_WIDGET_TOPLEVEL(ancestor) ? GTK_WINDOW( ancestor ) : 0;
1364         gchar* baseName = g_path_get_basename( filename );
1365         gchar* dirName = g_path_get_dirname( filename );
1366         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window,
1367                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1368                                                                 GTK_MESSAGE_QUESTION,
1369                                                                 GTK_BUTTONS_NONE,
1370                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1371                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1372                                                                 baseName,
1373                                                                 dirName
1374             );
1375         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1376                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1377                                 _("Replace"), GTK_RESPONSE_YES,
1378                                 NULL );
1379         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1381         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1382             return_value = TRUE;
1383         } else {
1384             return_value = FALSE;
1385         }
1386         gtk_widget_destroy(dialog);
1387         g_free( baseName );
1388         g_free( dirName );
1389     } else {
1390         return_value = TRUE;
1391     }
1393     return return_value;
1396 static void
1397 sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data)
1399     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1402 static void
1403 sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data)
1405     gtk_label_set_markup_with_mnemonic(
1406         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (GTK_BIN (data)->child))->data), 
1407         name.c_str());
1411 /*
1412   Local Variables:
1413   mode:c++
1414   c-file-style:"stroustrup"
1415   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1416   indent-tabs-mode:nil
1417   fill-column:99
1418   End:
1419 */
1420 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :