Code

Sorry, I got off on a branch and ended up with a bunch of things. I'm just going...
[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 // Include Mac OS X menu synchronization on native OSX build
72 #ifdef GDK_WINDOWING_QUARTZ
73 #include "ige-mac-menu.h"
74 #endif
76 using Inkscape::IO::StringOutputStream;
77 using Inkscape::IO::Base64OutputStream;
79 /* Drag and Drop */
80 typedef enum {
81     URI_LIST,
82     SVG_XML_DATA,
83     SVG_DATA,
84     PNG_DATA,
85     JPEG_DATA,
86     IMAGE_DATA,
87     APP_X_INKY_COLOR,
88     APP_X_COLOR
89 } ui_drop_target_info;
91 static GtkTargetEntry ui_drop_target_entries [] = {
92     {(gchar *)"text/uri-list",                0, URI_LIST        },
93     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
94     {(gchar *)"image/svg",                    0, SVG_DATA        },
95     {(gchar *)"image/png",                    0, PNG_DATA        },
96     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
97 #if ENABLE_MAGIC_COLORS
98     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
99 #endif // ENABLE_MAGIC_COLORS
100     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
101 };
103 static GtkTargetEntry *completeDropTargets = 0;
104 static int completeDropTargetsCount = 0;
106 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
107 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
108 static void sp_ui_import_files(gchar *buffer);
109 static void sp_ui_import_one_file(char const *filename);
110 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
111 static void sp_ui_drag_data_received(GtkWidget *widget,
112                                      GdkDragContext *drag_context,
113                                      gint x, gint y,
114                                      GtkSelectionData *data,
115                                      guint info,
116                                      guint event_time,
117                                      gpointer user_data);
118 static void sp_ui_drag_motion( GtkWidget *widget,
119                                GdkDragContext *drag_context,
120                                gint x, gint y,
121                                GtkSelectionData *data,
122                                guint info,
123                                guint event_time,
124                                gpointer user_data );
125 static void sp_ui_drag_leave( GtkWidget *widget,
126                               GdkDragContext *drag_context,
127                               guint event_time,
128                               gpointer user_data );
129 static void sp_ui_menu_item_set_sensitive(SPAction *action,
130                                           unsigned int sensitive,
131                                           void *data);
132 static void sp_ui_menu_item_set_name(SPAction *action, 
133                                      Glib::ustring name,
134                                      void *data);
136 SPActionEventVector menu_item_event_vector = {
137     {NULL},
138     NULL,
139     NULL, /* set_active */
140     sp_ui_menu_item_set_sensitive, /* set_sensitive */
141     NULL, /* set_shortcut */
142     sp_ui_menu_item_set_name /* set_name */
143 };
145 static const int MIN_ONSCREEN_DISTANCE = 50;
147 void
148 sp_create_window(SPViewWidget *vw, gboolean editable)
150     g_return_if_fail(vw != NULL);
151     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
153     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
155     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
156     gtk_widget_show(GTK_WIDGET(vw));
158     if (editable) {
159                 g_object_set_data(G_OBJECT(vw), "window", win);
160                 
161                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
162                 SPDesktop* desktop = desktop_widget->desktop;
163                 
164                 desktop_widget->window = win;
166         win->set_data("desktop", desktop);
167         win->set_data("desktopwidget", desktop_widget);
168                 
169         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
170                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
171                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
172         
173         gint prefs_geometry = 
174             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
175         if (prefs_geometry) {
176             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
177             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
178             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
179             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
180             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
181             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
182             if (pw>0 && ph>0) {
183                 gint w = MIN(gdk_screen_width(), pw);
184                 gint h = MIN(gdk_screen_height(), ph);
185                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
186                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
187                 if (w>0 && h>0 && x>0 && y>0) {
188                     x = MIN(gdk_screen_width() - w, x);
189                     y = MIN(gdk_screen_height() - h, y);
190                 }
191                 if (w>0 && h>0) {
192                     desktop->setWindowSize(w, h);
193                 }
195                 // Only restore xy for the first window so subsequent windows don't overlap exactly
196                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
197                 // other desktops?)
199                 // Empirically it seems that active_desktop==this desktop only the first time a
200                 // desktop is created.
201                 if (x>0 && y>0) {
202                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
203                     if (active_desktop == desktop || active_desktop==NULL) {
204                         desktop->setWindowPosition(Geom::Point(x, y));
205                     }
206                 }
207             }
208             if (maxed) {
209                 win->maximize();
210             }
211             if (full) {
212                 win->fullscreen();
213             }
214         }
216     } else {
217         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
218     }
220     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
221     {
222         std::vector<gchar*> types;
224         GSList *list = gdk_pixbuf_get_formats();
225         while ( list ) {
226             int i = 0;
227             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
228             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
229             for ( i = 0; typesXX[i]; i++ ) {
230                 types.push_back(g_strdup(typesXX[i]));
231             }
232             g_strfreev(typesXX);
234             list = g_slist_next(list);
235         }
236         completeDropTargetsCount = nui_drop_target_entries + types.size();
237         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
238         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
239             completeDropTargets[i] = ui_drop_target_entries[i];
240         }
241         int pos = nui_drop_target_entries;
243         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
244             completeDropTargets[pos].target = *it;
245             completeDropTargets[pos].flags = 0;
246             completeDropTargets[pos].info = IMAGE_DATA;
247             pos++;
248         }
249     }
251     gtk_drag_dest_set((GtkWidget*)win->gobj(),
252                       GTK_DEST_DEFAULT_ALL,
253                       completeDropTargets,
254                       completeDropTargetsCount,
255                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
258     g_signal_connect(G_OBJECT(win->gobj()),
259                      "drag_data_received",
260                      G_CALLBACK(sp_ui_drag_data_received),
261                      NULL);
262     g_signal_connect(G_OBJECT(win->gobj()),
263                      "drag_motion",
264                      G_CALLBACK(sp_ui_drag_motion),
265                      NULL);
266     g_signal_connect(G_OBJECT(win->gobj()),
267                      "drag_leave",
268                      G_CALLBACK(sp_ui_drag_leave),
269                      NULL);
270     win->show();
272     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
273     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
276 void
277 sp_ui_new_view()
279     SPDocument *document;
280     SPViewWidget *dtw;
282     document = SP_ACTIVE_DOCUMENT;
283     if (!document) return;
285     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
286     g_return_if_fail(dtw != NULL);
288     sp_create_window(dtw, TRUE);
289     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
290     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
293 /* TODO: not yet working */
294 /* To be re-enabled (by adding to menu) once it works. */
295 void
296 sp_ui_new_view_preview()
298     SPDocument *document;
299     SPViewWidget *dtw;
301     document = SP_ACTIVE_DOCUMENT;
302     if (!document) return;
304     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
305     g_return_if_fail(dtw != NULL);
306     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
308     sp_create_window(dtw, FALSE);
311 /**
312  * \param widget unused
313  */
314 void
315 sp_ui_close_view(GtkWidget */*widget*/)
317     if (SP_ACTIVE_DESKTOP == NULL) {
318         return;
319     }
320     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
321         return;
322     }
323     SP_ACTIVE_DESKTOP->destroyWidget();
327 /**
328  *  sp_ui_close_all
329  *
330  *  This function is called to exit the program, and iterates through all
331  *  open document view windows, attempting to close each in turn.  If the
332  *  view has unsaved information, the user will be prompted to save,
333  *  discard, or cancel.
334  *
335  *  Returns FALSE if the user cancels the close_all operation, TRUE
336  *  otherwise.
337  */
338 unsigned int
339 sp_ui_close_all(void)
341     /* Iterate through all the windows, destroying each in the order they
342        become active */
343     while (SP_ACTIVE_DESKTOP) {
344         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
345             /* The user cancelled the operation, so end doing the close */
346             return FALSE;
347         }
348         SP_ACTIVE_DESKTOP->destroyWidget();
349     }
351     return TRUE;
354 /*
355  * Some day when the right-click menus are ready to start working
356  * smarter with the verbs, we'll need to change this NULL being
357  * sent to sp_action_perform to something useful, or set some kind
358  * of global "right-clicked position" variable for actions to
359  * investigate when they're called.
360  */
361 static void
362 sp_ui_menu_activate(void */*object*/, SPAction *action)
364     sp_action_perform(action, NULL);
367 static void
368 sp_ui_menu_select_action(void */*object*/, SPAction *action)
370     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
373 static void
374 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
376     action->view->tipsMessageContext()->clear();
379 static void
380 sp_ui_menu_select(gpointer object, gpointer tip)
382     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
383     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
386 static void
387 sp_ui_menu_deselect(gpointer object)
389     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
390     view->tipsMessageContext()->clear();
393 /**
394  * sp_ui_menuitem_add_icon
395  *
396  * Creates and attaches a scaled icon to the given menu item.
397  *
398  */
399 void
400 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
402     GtkWidget *icon;
404     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
405     gtk_widget_show(icon);
406     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
407 } // end of sp_ui_menu_add_icon
409 /**
410  * sp_ui_menu_append_item
411  *
412  * Appends a UI item with specific info for Inkscape/Sodipodi.
413  *
414  */
415 static GtkWidget *
416 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
417                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
418                         gpointer data, gboolean with_mnemonic = TRUE )
420     GtkWidget *item;
422     if (stock) {
423         item = gtk_image_menu_item_new_from_stock(stock, NULL);
424     } else if (label) {
425         item = (with_mnemonic)
426             ? gtk_image_menu_item_new_with_mnemonic(label) :
427             gtk_image_menu_item_new_with_label(label);
428     } else {
429         item = gtk_separator_menu_item_new();
430     }
432     gtk_widget_show(item);
434     if (callback) {
435         g_signal_connect(G_OBJECT(item), "activate", callback, data);
436     }
438     if (tip && view) {
439         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
440         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
441         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
442     }
444     gtk_menu_append(GTK_MENU(menu), item);
446     return item;
448 } // end of sp_ui_menu_append_item()
450 /**
451 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
452  */
453 static gchar const *
454 sp_key_name(guint keyval)
456     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
457        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
458     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
460     if      (!strcmp(n, "asciicircum"))  return "^";
461     else if (!strcmp(n, "parenleft"  ))  return "(";
462     else if (!strcmp(n, "parenright" ))  return ")";
463     else if (!strcmp(n, "plus"       ))  return "+";
464     else if (!strcmp(n, "minus"      ))  return "-";
465     else if (!strcmp(n, "asterisk"   ))  return "*";
466     else if (!strcmp(n, "KP_Multiply"))  return "*";
467     else if (!strcmp(n, "Delete"     ))  return "Del";
468     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
469     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
470     else if (!strcmp(n, "grave"      ))  return "`";
471     else if (!strcmp(n, "numbersign" ))  return "#";
472     else if (!strcmp(n, "bar"        ))  return "|";
473     else if (!strcmp(n, "slash"      ))  return "/";
474     else if (!strcmp(n, "exclam"     ))  return "!";
475     else if (!strcmp(n, "percent"    ))  return "%";
476     else return n;
480 /**
481  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
482  * \param c Points to a buffer at least 256 bytes long.
483  */
484 void
485 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
487     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
488      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
489      * Will probably need to change sp_shortcut_invoke callers.
490      *
491      * The existing gtk_label_new_with_mnemonic call can be replaced with
492      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
493      * gtk_label_set_text_with_mnemonic(lbl, str).
494      */
495     static GtkAccelLabelClass const &accel_lbl_cls
496         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
498     struct { unsigned test; char const *name; } const modifier_tbl[] = {
499         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
500         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
501         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
502     };
504     gchar *p = c;
505     gchar *end = p + 256;
507     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
508         if ((shortcut & modifier_tbl[i].test)
509             && (p < end))
510         {
511             p += g_snprintf(p, end - p, "%s%s",
512                             modifier_tbl[i].name,
513                             accel_lbl_cls.mod_separator);
514         }
515     }
516     if (p < end) {
517         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
518     }
519     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
522 void
523 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
525     SPAction     *action;
526     unsigned int shortcut;
527     gchar        *s;
528     gchar        key[256];
529     gchar        *atitle;
531     action = verb->get_action(NULL);
532     if (!action)
533         return;
535     atitle = sp_action_get_title(action);
537     s = g_stpcpy(c, atitle);
539     g_free(atitle);
541     shortcut = sp_shortcut_get_primary(verb);
542     if (shortcut) {
543         s = g_stpcpy(s, " (");
544         sp_ui_shortcut_string(shortcut, key);
545         s = g_stpcpy(s, key);
546         s = g_stpcpy(s, ")");
547     }
551 /**
552  * sp_ui_menu_append_item_from_verb
553  *
554  * Appends a custom menu UI from a verb.
555  *
556  */
558 static GtkWidget *
559 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
561     SPAction *action;
562     GtkWidget *item;
564     if (verb->get_code() == SP_VERB_NONE) {
566         item = gtk_separator_menu_item_new();
568     } else {
569         unsigned int shortcut;
571         action = verb->get_action(view);
573         if (!action) return NULL;
575         shortcut = sp_shortcut_get_primary(verb);
576         if (shortcut) {
577             gchar c[256];
578             sp_ui_shortcut_string(shortcut, c);
579             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
580             GtkWidget *const name_lbl = gtk_label_new("");
581             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
582             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
583             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
584             GtkWidget *const accel_lbl = gtk_label_new(c);
585             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
586             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
587             gtk_widget_show_all(hb);
588             if (radio) {
589                 item = gtk_radio_menu_item_new (group);
590             } else {
591                 item = gtk_image_menu_item_new();
592             }
593             gtk_container_add((GtkContainer *) item, hb);
594         } else {
595             if (radio) {
596                 item = gtk_radio_menu_item_new (group);
597             } else {
598                 item = gtk_image_menu_item_new ();
599             }
600             GtkWidget *const name_lbl = gtk_label_new("");
601             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
602             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
603             gtk_container_add((GtkContainer *) item, name_lbl);
604         }
606         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
607         if (!action->sensitive) {
608             gtk_widget_set_sensitive(item, FALSE);
609         }
611         if (action->image) {
612             sp_ui_menuitem_add_icon(item, action->image);
613         }
614         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
615         g_signal_connect( G_OBJECT(item), "activate",
616                           G_CALLBACK(sp_ui_menu_activate), action );
618         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
619         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
620     }
622     gtk_widget_show(item);
623     gtk_menu_append(GTK_MENU(menu), item);
625     return item;
627 } // end of sp_ui_menu_append_item_from_verb
630 static void
631 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
633     gchar const *pref = (gchar const *) user_data;
634     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
636     gchar *pref_path;
637     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
638         pref_path = g_strconcat("focus.", pref, NULL);
639     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
640         pref_path = g_strconcat("fullscreen.", pref, NULL);
641     } else {
642         pref_path = g_strconcat("window.", pref, NULL);
643         }
645     gboolean checked = gtk_check_menu_item_get_active(menuitem);
646     prefs_set_int_attribute(pref_path, "state", checked);
647         g_free(pref_path);
649     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
652 static gboolean
653 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
655     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
657     gchar const *pref = (gchar const *) user_data;
658     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
660     gchar const *pref_path;
661     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
662         pref_path = g_strconcat("fullscreen.", pref, NULL);
663     else
664         pref_path = g_strconcat("window.", pref, NULL);
666     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
668     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
669     gtk_check_menu_item_set_active(menuitem, ison);
670     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
672     return FALSE;
676 void
677 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
678                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
679                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
680                                        Inkscape::Verb *verb)
682     GtkWidget *item;
684     unsigned int shortcut = 0;
685     SPAction *action = NULL;
687     if (verb) {
688         shortcut = sp_shortcut_get_primary(verb);
689         action = verb->get_action(view);
690     }
692     if (verb && shortcut) {
693         gchar c[256];
694         sp_ui_shortcut_string(shortcut, c);
696         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
698         {
699             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
700             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
701             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
702         }
704         {
705             GtkWidget *l = gtk_label_new(c);
706             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
707             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
708         }
710         gtk_widget_show_all(hb);
712         item = gtk_check_menu_item_new();
713         gtk_container_add((GtkContainer *) item, hb);
714     } else {
715         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
716         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
717         item = gtk_check_menu_item_new();
718         gtk_container_add((GtkContainer *) item, l);
719     }
720 #if 0
721     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
722     if (!action->sensitive) {
723         gtk_widget_set_sensitive(item, FALSE);
724     }
725 #endif
726     gtk_widget_show(item);
728     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
730     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
732     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
733     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
735     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
736     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
739 static void
740 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
742     sp_file_open(uri, NULL);
745 static void
746 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
748     sp_file_new(uri);
751 void
752 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
754     std::list<gchar *> sources;
755     sources.push_back( profile_path("templates") ); // first try user's local dir
756     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
758     // Use this loop to iterate through a list of possible document locations.
759     while (!sources.empty()) {
760         gchar *dirname = sources.front();
762         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
763             GError *err = 0;
764             GDir *dir = g_dir_open(dirname, 0, &err);
766             if (dir) {
767                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
768                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
769                         continue; // skip non-svg files
771                     gchar *basename = g_path_get_basename(file);
772                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
773                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
775                     gchar const *filepath = g_build_filename(dirname, file, NULL);
776                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
777                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
778                     g_free(dupfile);
779                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
780                     g_free(filename);
782                     gtk_widget_show(item);
783                     // how does "filepath" ever get freed?
784                     g_signal_connect(G_OBJECT(item),
785                                      "activate",
786                                      G_CALLBACK(sp_file_new_from_template),
787                                      (gpointer) filepath);
789                     if (view) {
790                         // set null tip for now; later use a description from the template file
791                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
792                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
793                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
794                     }
796                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
797                 }
798                 g_dir_close(dir);
799             }
800         }
802         // toss the dirname
803         g_free(dirname);
804         sources.pop_front();
805     }
808 void
809 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
811     gchar const **recent = prefs_get_recent_files();
812     if (recent) {
813         int i;
815         for (i = 0; recent[i] != NULL; i += 2) {
816             gchar const *uri = recent[i];
817             gchar const *name = recent[i + 1];
819             GtkWidget *item = gtk_menu_item_new_with_label(name);
820             gtk_widget_show(item);
821             g_signal_connect(G_OBJECT(item),
822                              "activate",
823                              G_CALLBACK(sp_recent_open),
824                              (gpointer)uri);
825             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
826         }
828         g_free(recent);
829     } else {
830         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
831         gtk_widget_show(item);
832         gtk_widget_set_sensitive(item, FALSE);
833         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
834     }
837 void
838 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
840     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
841     //                                       checkitem_toggled, checkitem_update, 0);
842     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
843                                            checkitem_toggled, checkitem_update, 0);
844     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
845                                            checkitem_toggled, checkitem_update, 0);
846     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
847                                            checkitem_toggled, checkitem_update, 0);
848     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
849                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
850     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
851                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
852     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
853                                            checkitem_toggled, checkitem_update, 0);
854     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
855                                            checkitem_toggled, checkitem_update, 0);
858 /** \brief  This function turns XML into a menu
859     \param  menus  This is the XML that defines the menu
860     \param  menu   Menu to be added to
861     \param  view   The View that this menu is being built for
863     This function is realitively simple as it just goes through the XML
864     and parses the individual elements.  In the case of a submenu, it
865     just calls itself recursively.  Because it is only reasonable to have
866     a couple of submenus, it is unlikely this will go more than two or
867     three times.
869     In the case of an unreconginzed verb, a menu item is made to identify
870     the verb that is missing, and display that.  The menu item is also made
871     insensitive.
872 */
873 void
874 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
876     if (menus == NULL) return;
877     if (menu == NULL)  return;
878     GSList *group = NULL;
880     for (Inkscape::XML::Node *menu_pntr = menus;
881          menu_pntr != NULL;
882          menu_pntr = menu_pntr->next()) {
883         if (!strcmp(menu_pntr->name(), "submenu")) {
884             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
885             GtkWidget *submenu = gtk_menu_new();
886             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
887             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
888             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
889             continue;
890         }
891         if (!strcmp(menu_pntr->name(), "verb")) {
892             gchar const *verb_name = menu_pntr->attribute("verb-id");
893             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
895             if (verb != NULL) {
896                 if (menu_pntr->attribute("radio") != NULL) {
897                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
898                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
899                     if (menu_pntr->attribute("default") != NULL) {
900                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
901                     }
902                 } else {
903                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
904                     group = NULL;
905                 }
906             } else {
907                 gchar string[120];
908                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
909                 string[119] = '\0'; /* may not be terminated */
910                 GtkWidget *item = gtk_menu_item_new_with_label(string);
911                 gtk_widget_set_sensitive(item, false);
912                 gtk_widget_show(item);
913                 gtk_menu_append(GTK_MENU(menu), item);
914             }
915             continue;
916         }
917         if (!strcmp(menu_pntr->name(), "separator")
918                 // This was spelt wrong in the original version
919                 // and so this is for backward compatibility.  It can
920                 // probably be dropped after the 0.44 release.
921              || !strcmp(menu_pntr->name(), "seperator")) {
922             GtkWidget *item = gtk_separator_menu_item_new();
923             gtk_widget_show(item);
924             gtk_menu_append(GTK_MENU(menu), item);
925             continue;
926         }
927         if (!strcmp(menu_pntr->name(), "template-list")) {
928             sp_menu_append_new_templates(menu, view);
929             continue;
930         }
931         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
932             sp_menu_append_recent_documents(menu, view);
933             continue;
934         }
935         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
936             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
937             continue;
938         }
939     }
942 /** \brief  Build the main tool bar
943     \param  view  View to build the bar for
945     Currently the main tool bar is built as a dynamic XML menu using
946     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
947     pass it to get items attached to it.
948 */
949 GtkWidget *
950 sp_ui_main_menubar(Inkscape::UI::View::View *view)
952     GtkWidget *mbar = gtk_menu_bar_new();
954 #ifdef GDK_WINDOWING_QUARTZ
955         ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
956 #endif
958     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
960 #ifdef GDK_WINDOWING_QUARTZ
961         return NULL;
962 #else
963     return mbar;
964 #endif
967 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
968     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
971 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
972     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
973     sp_desktop_selection(desktop)->clear();
976 GtkWidget *
977 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
979     GtkWidget *m;
980     SPDesktop *dt;
982     dt = static_cast<SPDesktop*>(view);
984     m = gtk_menu_new();
986     /* Undo and Redo */
987     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
988     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
990     /* Separator */
991     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
993     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
994     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
995     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
997     /* Separator */
998     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1000     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1001     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1003     /* Item menu */
1004     if (item) {
1005         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1006         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1007     }
1009     /* layer menu */
1010     SPGroup *group=NULL;
1011     if (item) {
1012         if (SP_IS_GROUP(item)) {
1013             group = SP_GROUP(item);
1014         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1015             group = SP_GROUP(SP_OBJECT_PARENT(item));
1016         }
1017     }
1019     if (( group && group != dt->currentLayer() ) ||
1020         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1021         /* Separator */
1022         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1023     }
1025     if ( group && group != dt->currentLayer() ) {
1026         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1027         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1028         GtkWidget *w = gtk_menu_item_new_with_label(label);
1029         g_free(label);
1030         g_object_set_data(G_OBJECT(w), "group", group);
1031         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1032         gtk_widget_show(w);
1033         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1034     }
1036     if ( dt->currentLayer() != dt->currentRoot() ) {
1037         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1038             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1039             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1040             gtk_widget_show(w);
1041             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1043         }
1044     }
1046     return m;
1049 /* Drag and Drop */
1050 void
1051 sp_ui_drag_data_received(GtkWidget *widget,
1052                          GdkDragContext *drag_context,
1053                          gint x, gint y,
1054                          GtkSelectionData *data,
1055                          guint info,
1056                          guint /*event_time*/,
1057                          gpointer /*user_data*/)
1059     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1060     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1062     switch (info) {
1063 #if ENABLE_MAGIC_COLORS
1064         case APP_X_INKY_COLOR:
1065         {
1066             int destX = 0;
1067             int destY = 0;
1068             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1069             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1071             SPItem *item = desktop->item_at_point( where, true );
1072             if ( item )
1073             {
1074                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1076                 if ( data->length >= 8 ) {
1077                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1079                     gchar c[64] = {0};
1080                     // Careful about endian issues.
1081                     guint16* dataVals = (guint16*)data->data;
1082                     sp_svg_write_color( c, sizeof(c),
1083                                         SP_RGBA32_U_COMPOSE(
1084                                             0x0ff & (dataVals[0] >> 8),
1085                                             0x0ff & (dataVals[1] >> 8),
1086                                             0x0ff & (dataVals[2] >> 8),
1087                                             0xff // can't have transparency in the color itself
1088                                             //0x0ff & (data->data[3] >> 8),
1089                                             ));
1090                     SPCSSAttr *css = sp_repr_css_attr_new();
1091                     bool updatePerformed = false;
1093                     if ( data->length > 14 ) {
1094                         int flags = dataVals[4];
1096                         // piggie-backed palette entry info
1097                         int index = dataVals[5];
1098                         Glib::ustring palName;
1099                         for ( int i = 0; i < dataVals[6]; i++ ) {
1100                             palName += (gunichar)dataVals[7+i];
1101                         }
1103                         // Now hook in a magic tag of some sort.
1104                         if ( !palName.empty() && (flags & 1) ) {
1105                             gchar* str = g_strdup_printf("%d|", index);
1106                             palName.insert( 0, str );
1107                             g_free(str);
1108                             str = 0;
1110                             sp_object_setAttribute( SP_OBJECT(item),
1111                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1112                                                     palName.c_str(),
1113                                                     false );
1114                             item->updateRepr();
1116                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1117                             updatePerformed = true;
1118                         }
1119                     }
1121                     if ( !updatePerformed ) {
1122                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1123                     }
1125                     sp_desktop_apply_css_recursive( item, css, true );
1126                     item->updateRepr();
1128                     sp_document_done( doc , SP_VERB_NONE, 
1129                                       _("Drop color"));
1131                     if ( srgbProf ) {
1132                         cmsCloseProfile( srgbProf );
1133                     }
1134                 }
1135             }
1136         }
1137         break;
1138 #endif // ENABLE_MAGIC_COLORS
1140         case APP_X_COLOR:
1141         {
1142             int destX = 0;
1143             int destY = 0;
1144             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1145             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1146             NR::Point const button_dt(desktop->w2d(where));
1147             NR::Point const button_doc(desktop->dt2doc(button_dt));
1149             if ( data->length == 8 ) {
1150                 gchar c[64] = {0};
1151                 // Careful about endian issues.
1152                 guint16* dataVals = (guint16*)data->data;
1153                 sp_svg_write_color( c, 64,
1154                                     SP_RGBA32_U_COMPOSE(
1155                                         0x0ff & (dataVals[0] >> 8),
1156                                         0x0ff & (dataVals[1] >> 8),
1157                                         0x0ff & (dataVals[2] >> 8),
1158                                         0xff // can't have transparency in the color itself
1159                                         //0x0ff & (data->data[3] >> 8),
1160                                         ));
1162                 SPItem *item = desktop->item_at_point( where, true );
1164                 bool consumed = false;
1165                 if (desktop->event_context && desktop->event_context->get_drag()) {
1166                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1167                     if (consumed) {
1168                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1169                         desktop->event_context->get_drag()->updateDraggers();
1170                     }
1171                 }
1173                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1174                 //    consumed = sp_text_context_drop_color(c, button_doc);
1175                 //    if (consumed) {
1176                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1177                 //    }
1178                 //}
1180                 if (!consumed && item) {
1181                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1182                     if (fillnotstroke && 
1183                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1184                         Path *livarot_path = Path_for_item(item, true, true);
1185                         livarot_path->ConvertWithBackData(0.04);
1187                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1188                         if (position) {
1189                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1190                             NR::Point delta = nearest - button_doc;
1191                             delta = desktop->d2w(delta);
1192                             double stroke_tolerance =
1193                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1194                                   desktop->current_zoom() *
1195                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1196                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1197                                   : 0.0)
1198                                 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1200                             if (NR::L2 (delta) < stroke_tolerance) {
1201                                 fillnotstroke = false;
1202                             }
1203                         }
1204                         delete livarot_path;
1205                     }
1207                     SPCSSAttr *css = sp_repr_css_attr_new();
1208                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1210                     sp_desktop_apply_css_recursive( item, css, true );
1211                     item->updateRepr();
1213                     sp_document_done( doc , SP_VERB_NONE, 
1214                                       _("Drop color"));
1215                 }
1216             }
1217         }
1218         break;
1220         case SVG_DATA:
1221         case SVG_XML_DATA: {
1222             gchar *svgdata = (gchar *)data->data;
1224             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1226             if (rnewdoc == NULL) {
1227                 sp_ui_error_dialog(_("Could not parse SVG data"));
1228                 return;
1229             }
1231             Inkscape::XML::Node *repr = rnewdoc->root();
1232             gchar const *style = repr->attribute("style");
1234             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1235             newgroup->setAttribute("style", style);
1237             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1238             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1239                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1240                 newgroup->appendChild(newchild);
1241             }
1243             Inkscape::GC::release(rnewdoc);
1245             // Add it to the current layer
1247             // Greg's edits to add intelligent positioning of svg drops
1248             SPObject *new_obj = NULL;
1249             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1251             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1252             selection->set(SP_ITEM(new_obj));
1253             // To move the imported object, we must temporarily set the "transform pattern with
1254             // object" option.
1255             {
1256                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1257                 prefs_set_int_attribute("options.transform", "pattern", 1);
1258                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1259                 boost::optional<Geom::Rect> sel_bbox = selection->bounds_2geom();
1260                 if (sel_bbox) {
1261                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1262                     sp_selection_move_relative(selection, m);
1263                 }
1264                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1265             }
1267             Inkscape::GC::release(newgroup);
1268             sp_document_done(doc, SP_VERB_NONE, 
1269                              _("Drop SVG"));
1270             break;
1271         }
1273         case URI_LIST: {
1274             gchar *uri = (gchar *)data->data;
1275             sp_ui_import_files(uri);
1276             break;
1277         }
1279         case PNG_DATA:
1280         case JPEG_DATA:
1281         case IMAGE_DATA: {
1282             char tmp[1024];
1284             StringOutputStream outs;
1285             Base64OutputStream b64out(outs);
1286             b64out.setColumnWidth(0);
1288             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1290             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1292             for ( int i = 0; i < data->length; i++ ) {
1293                 b64out.put( data->data[i] );
1294             }
1295             b64out.close();
1298             Glib::ustring str = outs.getString();
1300             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1301             str.insert( 0, tmp );
1302             newImage->setAttribute("xlink:href", str.c_str());
1304             GError *error = NULL;
1305             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1306             if ( loader ) {
1307                 error = NULL;
1308                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1309                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1310                     if ( pbuf ) {
1311                         int width = gdk_pixbuf_get_width(pbuf);
1312                         int height = gdk_pixbuf_get_height(pbuf);
1313                         snprintf( tmp, sizeof(tmp), "%d", width );
1314                         newImage->setAttribute("width", tmp);
1316                         snprintf( tmp, sizeof(tmp), "%d", height );
1317                         newImage->setAttribute("height", tmp);
1318                     }
1319                 }
1320             }
1322             // Add it to the current layer
1323             desktop->currentLayer()->appendChildRepr(newImage);
1325             Inkscape::GC::release(newImage);
1326             sp_document_done( doc , SP_VERB_NONE, 
1327                               _("Drop bitmap image"));
1328             break;
1329         }
1330     }
1333 #include "gradient-context.h"
1335 void sp_ui_drag_motion( GtkWidget */*widget*/,
1336                         GdkDragContext */*drag_context*/,
1337                         gint /*x*/, gint /*y*/,
1338                         GtkSelectionData */*data*/,
1339                         guint /*info*/,
1340                         guint /*event_time*/,
1341                         gpointer /*user_data*/)
1343 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1344 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1347 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1350 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1351                               GdkDragContext */*drag_context*/,
1352                               guint /*event_time*/,
1353                               gpointer /*user_data*/ )
1355 //     g_message("drag-n-drop leave                at %d", event_time);
1358 static void
1359 sp_ui_import_files(gchar *buffer)
1361     GList *list = gnome_uri_list_extract_filenames(buffer);
1362     if (!list)
1363         return;
1364     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1365     g_list_foreach(list, (GFunc) g_free, NULL);
1366     g_list_free(list);
1369 static void
1370 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1372     if (filename) {
1373         if (strlen((char const *)filename) > 2)
1374             sp_ui_import_one_file((char const *)filename);
1375     }
1378 static void
1379 sp_ui_import_one_file(char const *filename)
1381     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1382     if (!doc) return;
1384     if (filename == NULL) return;
1386     // Pass off to common implementation
1387     // TODO might need to get the proper type of Inkscape::Extension::Extension
1388     file_import( doc, filename, NULL );
1391 void
1392 sp_ui_error_dialog(gchar const *message)
1394     GtkWidget *dlg;
1395     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1397     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1398                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1399     sp_transientize(dlg);
1400     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1401     gtk_dialog_run(GTK_DIALOG(dlg));
1402     gtk_widget_destroy(dlg);
1403     g_free(safeMsg);
1406 bool
1407 sp_ui_overwrite_file(gchar const *filename)
1409     bool return_value = FALSE;
1411     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1412         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1413         gchar* baseName = g_path_get_basename( filename );
1414         gchar* dirName = g_path_get_dirname( filename );
1415         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1416                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1417                                                                 GTK_MESSAGE_QUESTION,
1418                                                                 GTK_BUTTONS_NONE,
1419                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1420                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1421                                                                 baseName,
1422                                                                 dirName
1423             );
1424         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1425                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1426                                 _("Replace"), GTK_RESPONSE_YES,
1427                                 NULL );
1428         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1430         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1431             return_value = TRUE;
1432         } else {
1433             return_value = FALSE;
1434         }
1435         gtk_widget_destroy(dialog);
1436         g_free( baseName );
1437         g_free( dirName );
1438     } else {
1439         return_value = TRUE;
1440     }
1442     return return_value;
1445 static void
1446 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1448     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1451 static void
1452 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1454     void *child = GTK_BIN (data)->child;
1455     //child is either
1456     //- a GtkHBox, whose first child is a label displaying name if the menu
1457     //item has an accel key
1458     //- a GtkLabel if the menu has no accel key
1459     if (GTK_IS_LABEL(child)) {
1460         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1461     } else if (GTK_IS_HBOX(child)) {
1462         gtk_label_set_markup_with_mnemonic(
1463         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1464         name.c_str());
1465     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1466     //a menu item in yet another way...
1470 /*
1471   Local Variables:
1472   mode:c++
1473   c-file-style:"stroustrup"
1474   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1475   indent-tabs-mode:nil
1476   fill-column:99
1477   End:
1478 */
1479 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :