Code

Adding some no-color to drag-n-drop
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /** @file
4  * @brief 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 <glib.h>
25 #include "inkscape-private.h"
26 #include "extension/effect.h"
27 #include "widgets/icon.h"
28 #include "preferences.h"
29 #include "path-prefix.h"
30 #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"
46 #include "helper/action.h"
47 #include "helper/gnome-utils.h"
48 #include "helper/window.h"
49 #include "io/sys.h"
50 #include "dialogs/dialog-events.h"
51 #include "message-context.h"
53 // Added for color drag-n-drop
54 #if ENABLE_LCMS
55 #include "lcms.h"
56 #endif // ENABLE_LCMS
57 #include "display/sp-canvas.h"
58 #include "color.h"
59 #include "svg/svg-color.h"
60 #include "desktop-style.h"
61 #include "style.h"
62 #include "event-context.h"
63 #include "gradient-drag.h"
65 // Include Mac OS X menu synchronization on native OSX build
66 #ifdef GDK_WINDOWING_QUARTZ
67 #include "ige-mac-menu.h"
68 #endif
70 /* Drag and Drop */
71 typedef enum {
72     URI_LIST,
73     SVG_XML_DATA,
74     SVG_DATA,
75     PNG_DATA,
76     JPEG_DATA,
77     IMAGE_DATA,
78     APP_X_INKY_COLOR,
79     APP_X_COLOR,
80     APP_X_NOCOLOR
81 } ui_drop_target_info;
83 static GtkTargetEntry ui_drop_target_entries [] = {
84     {(gchar *)"text/uri-list",                0, URI_LIST        },
85     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
86     {(gchar *)"image/svg",                    0, SVG_DATA        },
87     {(gchar *)"image/png",                    0, PNG_DATA        },
88     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
89 #if ENABLE_MAGIC_COLORS
90     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
91 #endif // ENABLE_MAGIC_COLORS
92     {(gchar *)"application/x-inkscape-nocolor",          0, APP_X_NOCOLOR     },
93     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
94 };
96 static GtkTargetEntry *completeDropTargets = 0;
97 static int completeDropTargetsCount = 0;
99 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
100 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
101 static void sp_ui_import_files(gchar *buffer);
102 static void sp_ui_import_one_file(char const *filename);
103 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
104 static void sp_ui_drag_data_received(GtkWidget *widget,
105                                      GdkDragContext *drag_context,
106                                      gint x, gint y,
107                                      GtkSelectionData *data,
108                                      guint info,
109                                      guint event_time,
110                                      gpointer user_data);
111 static void sp_ui_drag_motion( 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_leave( GtkWidget *widget,
119                               GdkDragContext *drag_context,
120                               guint event_time,
121                               gpointer user_data );
122 static void sp_ui_menu_item_set_sensitive(SPAction *action,
123                                           unsigned int sensitive,
124                                           void *data);
125 static void sp_ui_menu_item_set_name(SPAction *action,
126                                      Glib::ustring name,
127                                      void *data);
128 static void sp_recent_open(GtkRecentChooser *, gpointer);
130 SPActionEventVector menu_item_event_vector = {
131     {NULL},
132     NULL,
133     NULL, /* set_active */
134     sp_ui_menu_item_set_sensitive, /* set_sensitive */
135     NULL, /* set_shortcut */
136     sp_ui_menu_item_set_name /* set_name */
137 };
139 static const int MIN_ONSCREEN_DISTANCE = 50;
141 void
142 sp_create_window(SPViewWidget *vw, gboolean editable)
144     g_return_if_fail(vw != NULL);
145     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
147     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
149     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
150     gtk_widget_show(GTK_WIDGET(vw));
152     if (editable) {
153                 g_object_set_data(G_OBJECT(vw), "window", win);
155                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
156                 SPDesktop* desktop = desktop_widget->desktop;
158                 desktop_widget->window = win;
160         win->set_data("desktop", desktop);
161         win->set_data("desktopwidget", desktop_widget);
163         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
164                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
165                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
167         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
168         gint prefs_geometry =
169             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
170         if (prefs_geometry) {
171             gint pw = prefs->getInt("/desktop/geometry/width", -1);
172             gint ph = prefs->getInt("/desktop/geometry/height", -1);
173             gint px = prefs->getInt("/desktop/geometry/x", -1);
174             gint py = prefs->getInt("/desktop/geometry/y", -1);
175             gint full = prefs->getBool("/desktop/geometry/fullscreen");
176             gint maxed = prefs->getBool("/desktop/geometry/maximized");
177             if (pw>0 && ph>0) {
178                 gint w = MIN(gdk_screen_width(), pw);
179                 gint h = MIN(gdk_screen_height(), ph);
180                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
181                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
182                 if (w>0 && h>0 && x>0 && y>0) {
183                     x = MIN(gdk_screen_width() - w, x);
184                     y = MIN(gdk_screen_height() - h, y);
185                 }
186                 if (w>0 && h>0) {
187                     desktop->setWindowSize(w, h);
188                 }
190                 // Only restore xy for the first window so subsequent windows don't overlap exactly
191                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
192                 // other desktops?)
194                 // Empirically it seems that active_desktop==this desktop only the first time a
195                 // desktop is created.
196                 if (x>0 && y>0) {
197                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
198                     if (active_desktop == desktop || active_desktop==NULL) {
199                         desktop->setWindowPosition(Geom::Point(x, y));
200                     }
201                 }
202             }
203             if (maxed) {
204                 win->maximize();
205             }
206             if (full) {
207                 win->fullscreen();
208             }
209         }
211     } else {
212         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
213     }
215     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
216     {
217         std::vector<gchar*> types;
219         GSList *list = gdk_pixbuf_get_formats();
220         while ( list ) {
221             int i = 0;
222             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
223             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
224             for ( i = 0; typesXX[i]; i++ ) {
225                 types.push_back(g_strdup(typesXX[i]));
226             }
227             g_strfreev(typesXX);
229             list = g_slist_next(list);
230         }
231         completeDropTargetsCount = nui_drop_target_entries + types.size();
232         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
233         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
234             completeDropTargets[i] = ui_drop_target_entries[i];
235         }
236         int pos = nui_drop_target_entries;
238         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
239             completeDropTargets[pos].target = *it;
240             completeDropTargets[pos].flags = 0;
241             completeDropTargets[pos].info = IMAGE_DATA;
242             pos++;
243         }
244     }
246     gtk_drag_dest_set((GtkWidget*)win->gobj(),
247                       GTK_DEST_DEFAULT_ALL,
248                       completeDropTargets,
249                       completeDropTargetsCount,
250                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
253     g_signal_connect(G_OBJECT(win->gobj()),
254                      "drag_data_received",
255                      G_CALLBACK(sp_ui_drag_data_received),
256                      NULL);
257     g_signal_connect(G_OBJECT(win->gobj()),
258                      "drag_motion",
259                      G_CALLBACK(sp_ui_drag_motion),
260                      NULL);
261     g_signal_connect(G_OBJECT(win->gobj()),
262                      "drag_leave",
263                      G_CALLBACK(sp_ui_drag_leave),
264                      NULL);
265     win->show();
267     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
268     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
271 void
272 sp_ui_new_view()
274     SPDocument *document;
275     SPViewWidget *dtw;
277     document = SP_ACTIVE_DOCUMENT;
278     if (!document) return;
280     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
281     g_return_if_fail(dtw != NULL);
283     sp_create_window(dtw, TRUE);
284     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
285     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
288 /* TODO: not yet working */
289 /* To be re-enabled (by adding to menu) once it works. */
290 void
291 sp_ui_new_view_preview()
293     SPDocument *document;
294     SPViewWidget *dtw;
296     document = SP_ACTIVE_DOCUMENT;
297     if (!document) return;
299     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
300     g_return_if_fail(dtw != NULL);
301     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
303     sp_create_window(dtw, FALSE);
306 /**
307  * \param widget unused
308  */
309 void
310 sp_ui_close_view(GtkWidget */*widget*/)
312     if (SP_ACTIVE_DESKTOP == NULL) {
313         return;
314     }
315     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
316         return;
317     }
318     SP_ACTIVE_DESKTOP->destroyWidget();
322 /**
323  *  sp_ui_close_all
324  *
325  *  This function is called to exit the program, and iterates through all
326  *  open document view windows, attempting to close each in turn.  If the
327  *  view has unsaved information, the user will be prompted to save,
328  *  discard, or cancel.
329  *
330  *  Returns FALSE if the user cancels the close_all operation, TRUE
331  *  otherwise.
332  */
333 unsigned int
334 sp_ui_close_all(void)
336     /* Iterate through all the windows, destroying each in the order they
337        become active */
338     while (SP_ACTIVE_DESKTOP) {
339         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
340             /* The user cancelled the operation, so end doing the close */
341             return FALSE;
342         }
343         SP_ACTIVE_DESKTOP->destroyWidget();
344     }
346     return TRUE;
349 /*
350  * Some day when the right-click menus are ready to start working
351  * smarter with the verbs, we'll need to change this NULL being
352  * sent to sp_action_perform to something useful, or set some kind
353  * of global "right-clicked position" variable for actions to
354  * investigate when they're called.
355  */
356 static void
357 sp_ui_menu_activate(void */*object*/, SPAction *action)
359     sp_action_perform(action, NULL);
362 static void
363 sp_ui_menu_select_action(void */*object*/, SPAction *action)
365     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
368 static void
369 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
371     action->view->tipsMessageContext()->clear();
374 static void
375 sp_ui_menu_select(gpointer object, gpointer tip)
377     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
378     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
381 static void
382 sp_ui_menu_deselect(gpointer object)
384     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
385     view->tipsMessageContext()->clear();
388 /**
389  * sp_ui_menuitem_add_icon
390  *
391  * Creates and attaches a scaled icon to the given menu item.
392  *
393  */
394 void
395 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
397     GtkWidget *icon;
399     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
400     gtk_widget_show(icon);
401     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
402 } // end of sp_ui_menu_add_icon
404 /**
405  * sp_ui_menu_append_item
406  *
407  * Appends a UI item with specific info for Inkscape/Sodipodi.
408  *
409  */
410 static GtkWidget *
411 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
412                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
413                         gpointer data, gboolean with_mnemonic = TRUE )
415     GtkWidget *item;
417     if (stock) {
418         item = gtk_image_menu_item_new_from_stock(stock, NULL);
419     } else if (label) {
420         item = (with_mnemonic)
421             ? gtk_image_menu_item_new_with_mnemonic(label) :
422             gtk_image_menu_item_new_with_label(label);
423     } else {
424         item = gtk_separator_menu_item_new();
425     }
427     gtk_widget_show(item);
429     if (callback) {
430         g_signal_connect(G_OBJECT(item), "activate", callback, data);
431     }
433     if (tip && view) {
434         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
435         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
436         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
437     }
439     gtk_menu_append(GTK_MENU(menu), item);
441     return item;
443 } // end of sp_ui_menu_append_item()
445 /**
446 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
447  */
448 static gchar const *
449 sp_key_name(guint keyval)
451     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
452        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
453     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
455     if      (!strcmp(n, "asciicircum"))  return "^";
456     else if (!strcmp(n, "parenleft"  ))  return "(";
457     else if (!strcmp(n, "parenright" ))  return ")";
458     else if (!strcmp(n, "plus"       ))  return "+";
459     else if (!strcmp(n, "minus"      ))  return "-";
460     else if (!strcmp(n, "asterisk"   ))  return "*";
461     else if (!strcmp(n, "KP_Multiply"))  return "*";
462     else if (!strcmp(n, "Delete"     ))  return "Del";
463     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
464     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
465     else if (!strcmp(n, "grave"      ))  return "`";
466     else if (!strcmp(n, "numbersign" ))  return "#";
467     else if (!strcmp(n, "bar"        ))  return "|";
468     else if (!strcmp(n, "slash"      ))  return "/";
469     else if (!strcmp(n, "exclam"     ))  return "!";
470     else if (!strcmp(n, "percent"    ))  return "%";
471     else return n;
475 /**
476  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
477  * \param c Points to a buffer at least 256 bytes long.
478  */
479 void
480 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
482     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
483      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
484      * Will probably need to change sp_shortcut_invoke callers.
485      *
486      * The existing gtk_label_new_with_mnemonic call can be replaced with
487      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
488      * gtk_label_set_text_with_mnemonic(lbl, str).
489      */
490     static GtkAccelLabelClass const &accel_lbl_cls
491         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
493     struct { unsigned test; char const *name; } const modifier_tbl[] = {
494         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
495         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
496         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
497     };
499     gchar *p = c;
500     gchar *end = p + 256;
502     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
503         if ((shortcut & modifier_tbl[i].test)
504             && (p < end))
505         {
506             p += g_snprintf(p, end - p, "%s%s",
507                             modifier_tbl[i].name,
508                             accel_lbl_cls.mod_separator);
509         }
510     }
511     if (p < end) {
512         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
513     }
514     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
517 void
518 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
520     SPAction     *action;
521     unsigned int shortcut;
522     gchar        *s;
523     gchar        key[256];
524     gchar        *atitle;
526     action = verb->get_action(NULL);
527     if (!action)
528         return;
530     atitle = sp_action_get_title(action);
532     s = g_stpcpy(c, atitle);
534     g_free(atitle);
536     shortcut = sp_shortcut_get_primary(verb);
537     if (shortcut) {
538         s = g_stpcpy(s, " (");
539         sp_ui_shortcut_string(shortcut, key);
540         s = g_stpcpy(s, key);
541         s = g_stpcpy(s, ")");
542     }
546 /**
547  * sp_ui_menu_append_item_from_verb
548  *
549  * Appends a custom menu UI from a verb.
550  *
551  */
553 static GtkWidget *
554 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
556     SPAction *action;
557     GtkWidget *item;
559     if (verb->get_code() == SP_VERB_NONE) {
561         item = gtk_separator_menu_item_new();
563     } else {
564         unsigned int shortcut;
566         action = verb->get_action(view);
568         if (!action) return NULL;
570         shortcut = sp_shortcut_get_primary(verb);
571         if (shortcut) {
572             gchar c[256];
573             sp_ui_shortcut_string(shortcut, c);
574             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
575             GtkWidget *const name_lbl = gtk_label_new("");
576             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
577             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
578             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
579             GtkWidget *const accel_lbl = gtk_label_new(c);
580             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
581             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
582             gtk_widget_show_all(hb);
583             if (radio) {
584                 item = gtk_radio_menu_item_new (group);
585             } else {
586                 item = gtk_image_menu_item_new();
587             }
588             gtk_container_add((GtkContainer *) item, hb);
589         } else {
590             if (radio) {
591                 item = gtk_radio_menu_item_new (group);
592             } else {
593                 item = gtk_image_menu_item_new ();
594             }
595             GtkWidget *const name_lbl = gtk_label_new("");
596             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
597             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
598             gtk_container_add((GtkContainer *) item, name_lbl);
599         }
601         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
602         if (!action->sensitive) {
603             gtk_widget_set_sensitive(item, FALSE);
604         }
606         if (action->image) {
607             sp_ui_menuitem_add_icon(item, action->image);
608         }
609         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
610         g_signal_connect( G_OBJECT(item), "activate",
611                           G_CALLBACK(sp_ui_menu_activate), action );
613         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
614         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
615     }
617     gtk_widget_show(item);
618     gtk_menu_append(GTK_MENU(menu), item);
620     return item;
622 } // end of sp_ui_menu_append_item_from_verb
625 static void
626 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
628     gchar const *pref = (gchar const *) user_data;
629     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
631     Glib::ustring pref_path;
632     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
633         pref_path = "/focus/";
634     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
635         pref_path = "/fullscreen/";
636     } else {
637         pref_path = "/window/";
638     }
639     pref_path += pref;
640     pref_path += "/state";
642     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
643     gboolean checked = gtk_check_menu_item_get_active(menuitem);
644     prefs->setBool(pref_path, checked);
646     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
649 static gboolean
650 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
652     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
654     gchar const *pref = (gchar const *) user_data;
655     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
657     Glib::ustring pref_path;
658     if ((static_cast<SPDesktop*>(view))->is_fullscreen()) {
659         pref_path = "/fullscreen/";
660     } else {
661         pref_path = "/window/";
662     }
663     pref_path += pref;
665     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
666     bool ison = prefs->getBool(pref_path + "/state", true);
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(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
742     // dealing with the bizarre filename convention in Inkscape for now
743     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
744     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
745     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
746     sp_file_open(utf8_fn, NULL);
747     g_free(utf8_fn);
748     g_free(local_fn);
749     g_free(uri);
752 static void
753 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
755     sp_file_new(uri);
758 void
759 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
761     std::list<gchar *> sources;
762     sources.push_back( profile_path("templates") ); // first try user's local dir
763     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
765     // Use this loop to iterate through a list of possible document locations.
766     while (!sources.empty()) {
767         gchar *dirname = sources.front();
769         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
770             GError *err = 0;
771             GDir *dir = g_dir_open(dirname, 0, &err);
773             if (dir) {
774                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
775                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
776                         continue; // skip non-svg files
778                     gchar *basename = g_path_get_basename(file);
779                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
780                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
782                     gchar const *filepath = g_build_filename(dirname, file, NULL);
783                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
784                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
785                     g_free(dupfile);
786                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
787                     g_free(filename);
789                     gtk_widget_show(item);
790                     // how does "filepath" ever get freed?
791                     g_signal_connect(G_OBJECT(item),
792                                      "activate",
793                                      G_CALLBACK(sp_file_new_from_template),
794                                      (gpointer) filepath);
796                     if (view) {
797                         // set null tip for now; later use a description from the template file
798                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
799                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
800                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
801                     }
803                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
804                 }
805                 g_dir_close(dir);
806             }
807         }
809         // toss the dirname
810         g_free(dirname);
811         sources.pop_front();
812     }
815 void
816 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
818     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
819     //                                       checkitem_toggled, checkitem_update, 0);
820     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
821                                            checkitem_toggled, checkitem_update, 0);
822     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
823                                                                                    checkitem_toggled, checkitem_update, 0);
824     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
825                                            checkitem_toggled, checkitem_update, 0);
826     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
827                                            checkitem_toggled, checkitem_update, 0);
828     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
829                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
830     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
831                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
832     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
833                                            checkitem_toggled, checkitem_update, 0);
834     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
835                                            checkitem_toggled, checkitem_update, 0);
838 /** @brief Observer that updates the recent list's max document count */
839 class MaxRecentObserver : public Inkscape::Preferences::Observer {
840 public:
841     MaxRecentObserver(GtkWidget *recent_menu) :
842         Observer("/options/maxrecentdocuments/value"),
843         _rm(recent_menu)
844     {}
845     virtual void notify(Inkscape::Preferences::Entry const &e) {
846         gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
847         // hack: the recent menu doesn't repopulate after changing the limit, so we force it
848         g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
849     }
850 private:
851     GtkWidget *_rm;
852 };
854 /** \brief  This function turns XML into a menu
855     \param  menus  This is the XML that defines the menu
856     \param  menu   Menu to be added to
857     \param  view   The View that this menu is being built for
859     This function is realitively simple as it just goes through the XML
860     and parses the individual elements.  In the case of a submenu, it
861     just calls itself recursively.  Because it is only reasonable to have
862     a couple of submenus, it is unlikely this will go more than two or
863     three times.
865     In the case of an unreconginzed verb, a menu item is made to identify
866     the verb that is missing, and display that.  The menu item is also made
867     insensitive.
868 */
869 void
870 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
872     if (menus == NULL) return;
873     if (menu == NULL)  return;
874     GSList *group = NULL;
876     for (Inkscape::XML::Node *menu_pntr = menus;
877          menu_pntr != NULL;
878          menu_pntr = menu_pntr->next()) {
879         if (!strcmp(menu_pntr->name(), "submenu")) {
880             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
881             GtkWidget *submenu = gtk_menu_new();
882             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
883             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
884             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
885             continue;
886         }
887         if (!strcmp(menu_pntr->name(), "verb")) {
888             gchar const *verb_name = menu_pntr->attribute("verb-id");
889             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
891             if (verb != NULL) {
892                 if (menu_pntr->attribute("radio") != NULL) {
893                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
894                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
895                     if (menu_pntr->attribute("default") != NULL) {
896                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
897                     }
898                 } else {
899                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
900                     group = NULL;
901                 }
902             } else {
903                 gchar string[120];
904                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
905                 string[119] = '\0'; /* may not be terminated */
906                 GtkWidget *item = gtk_menu_item_new_with_label(string);
907                 gtk_widget_set_sensitive(item, false);
908                 gtk_widget_show(item);
909                 gtk_menu_append(GTK_MENU(menu), item);
910             }
911             continue;
912         }
913         if (!strcmp(menu_pntr->name(), "separator")
914                 // This was spelt wrong in the original version
915                 // and so this is for backward compatibility.  It can
916                 // probably be dropped after the 0.44 release.
917              || !strcmp(menu_pntr->name(), "seperator")) {
918             GtkWidget *item = gtk_separator_menu_item_new();
919             gtk_widget_show(item);
920             gtk_menu_append(GTK_MENU(menu), item);
921             continue;
922         }
923         if (!strcmp(menu_pntr->name(), "template-list")) {
924             sp_menu_append_new_templates(menu, view);
925             continue;
926         }
927         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
928             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
930             // create recent files menu
931             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
932             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
933             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
934             // sort most recently used documents first to preserve previous behavior
935             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
936             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
938             // add filter to only open files added by Inkscape
939             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
940             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
941             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
943             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
944             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
946             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
947             // this will just sit and update the list's item count
948             static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
949             prefs->addObserver(*mro);
950             continue;
951         }
952         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
953             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
954             continue;
955         }
956     }
959 /** \brief  Build the main tool bar
960     \param  view  View to build the bar for
962     Currently the main tool bar is built as a dynamic XML menu using
963     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
964     pass it to get items attached to it.
965 */
966 GtkWidget *
967 sp_ui_main_menubar(Inkscape::UI::View::View *view)
969     GtkWidget *mbar = gtk_menu_bar_new();
971 #ifdef GDK_WINDOWING_QUARTZ
972         ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
973 #endif
975     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
977 #ifdef GDK_WINDOWING_QUARTZ
978         return NULL;
979 #else
980     return mbar;
981 #endif
984 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
985     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
988 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
989     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
990     sp_desktop_selection(desktop)->clear();
993 GtkWidget *
994 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
996     GtkWidget *m;
997     SPDesktop *dt;
999     dt = static_cast<SPDesktop*>(view);
1001     m = gtk_menu_new();
1003     /* Undo and Redo */
1004     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1005     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1007     /* Separator */
1008     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1010     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1011     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1012     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1014     /* Separator */
1015     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1017     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1018     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1020     /* Item menu */
1021     if (item) {
1022         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1023         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1024     }
1026     /* layer menu */
1027     SPGroup *group=NULL;
1028     if (item) {
1029         if (SP_IS_GROUP(item)) {
1030             group = SP_GROUP(item);
1031         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1032             group = SP_GROUP(SP_OBJECT_PARENT(item));
1033         }
1034     }
1036     if (( group && group != dt->currentLayer() ) ||
1037         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1038         /* Separator */
1039         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1040     }
1042     if ( group && group != dt->currentLayer() ) {
1043         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1044         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1045         GtkWidget *w = gtk_menu_item_new_with_label(label);
1046         g_free(label);
1047         g_object_set_data(G_OBJECT(w), "group", group);
1048         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1049         gtk_widget_show(w);
1050         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1051     }
1053     if ( dt->currentLayer() != dt->currentRoot() ) {
1054         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1055             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1056             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1057             gtk_widget_show(w);
1058             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1060         }
1061     }
1063     return m;
1066 /* Drag and Drop */
1067 void
1068 sp_ui_drag_data_received(GtkWidget *widget,
1069                          GdkDragContext *drag_context,
1070                          gint x, gint y,
1071                          GtkSelectionData *data,
1072                          guint info,
1073                          guint /*event_time*/,
1074                          gpointer /*user_data*/)
1076     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1077     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1079     switch (info) {
1080 #if ENABLE_MAGIC_COLORS
1081         case APP_X_INKY_COLOR:
1082         {
1083             int destX = 0;
1084             int destY = 0;
1085             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1086             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1088             SPItem *item = desktop->item_at_point( where, true );
1089             if ( item )
1090             {
1091                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1093                 if ( data->length >= 8 ) {
1094                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1096                     gchar c[64] = {0};
1097                     // Careful about endian issues.
1098                     guint16* dataVals = (guint16*)data->data;
1099                     sp_svg_write_color( c, sizeof(c),
1100                                         SP_RGBA32_U_COMPOSE(
1101                                             0x0ff & (dataVals[0] >> 8),
1102                                             0x0ff & (dataVals[1] >> 8),
1103                                             0x0ff & (dataVals[2] >> 8),
1104                                             0xff // can't have transparency in the color itself
1105                                             //0x0ff & (data->data[3] >> 8),
1106                                             ));
1107                     SPCSSAttr *css = sp_repr_css_attr_new();
1108                     bool updatePerformed = false;
1110                     if ( data->length > 14 ) {
1111                         int flags = dataVals[4];
1113                         // piggie-backed palette entry info
1114                         int index = dataVals[5];
1115                         Glib::ustring palName;
1116                         for ( int i = 0; i < dataVals[6]; i++ ) {
1117                             palName += (gunichar)dataVals[7+i];
1118                         }
1120                         // Now hook in a magic tag of some sort.
1121                         if ( !palName.empty() && (flags & 1) ) {
1122                             gchar* str = g_strdup_printf("%d|", index);
1123                             palName.insert( 0, str );
1124                             g_free(str);
1125                             str = 0;
1127                             sp_object_setAttribute( SP_OBJECT(item),
1128                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1129                                                     palName.c_str(),
1130                                                     false );
1131                             item->updateRepr();
1133                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1134                             updatePerformed = true;
1135                         }
1136                     }
1138                     if ( !updatePerformed ) {
1139                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1140                     }
1142                     sp_desktop_apply_css_recursive( item, css, true );
1143                     item->updateRepr();
1145                     sp_document_done( doc , SP_VERB_NONE,
1146                                       _("Drop color"));
1148                     if ( srgbProf ) {
1149                         cmsCloseProfile( srgbProf );
1150                     }
1151                 }
1152             }
1153         }
1154         break;
1155 #endif // ENABLE_MAGIC_COLORS
1157         case APP_X_COLOR:
1158         {
1159             int destX = 0;
1160             int destY = 0;
1161             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1162             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1163             Geom::Point const button_dt(desktop->w2d(where));
1164             Geom::Point const button_doc(desktop->dt2doc(button_dt));
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                                         ));
1179                 SPItem *item = desktop->item_at_point( where, true );
1181                 bool consumed = false;
1182                 if (desktop->event_context && desktop->event_context->get_drag()) {
1183                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1184                     if (consumed) {
1185                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1186                         desktop->event_context->get_drag()->updateDraggers();
1187                     }
1188                 }
1190                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1191                 //    consumed = sp_text_context_drop_color(c, button_doc);
1192                 //    if (consumed) {
1193                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1194                 //    }
1195                 //}
1197                 if (!consumed && item) {
1198                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1199                     if (fillnotstroke &&
1200                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1201                         Path *livarot_path = Path_for_item(item, true, true);
1202                         livarot_path->ConvertWithBackData(0.04);
1204                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1205                         if (position) {
1206                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1207                             Geom::Point delta = nearest - button_doc;
1208                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1209                             delta = desktop->d2w(delta);
1210                             double stroke_tolerance =
1211                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1212                                   desktop->current_zoom() *
1213                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1214                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1215                                   : 0.0)
1216                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1218                             if (Geom::L2 (delta) < stroke_tolerance) {
1219                                 fillnotstroke = false;
1220                             }
1221                         }
1222                         delete livarot_path;
1223                     }
1225                     SPCSSAttr *css = sp_repr_css_attr_new();
1226                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1228                     sp_desktop_apply_css_recursive( item, css, true );
1229                     item->updateRepr();
1231                     sp_document_done( doc , SP_VERB_NONE,
1232                                       _("Drop color"));
1233                 }
1234             }
1235         }
1236         break;
1238         case APP_X_NOCOLOR:
1239         {
1240             gchar* c = g_strdup("none"); // temp
1241             int destX = 0;
1242             int destY = 0;
1243             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1244             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1245             Geom::Point const button_dt(desktop->w2d(where));
1246             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1248             SPItem *item = desktop->item_at_point( where, true );
1250             bool consumed = false;
1251             if (desktop->event_context && desktop->event_context->get_drag()) {
1252                 consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1253                 if (consumed) {
1254                     sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1255                     desktop->event_context->get_drag()->updateDraggers();
1256                 }
1257             }
1259             if (!consumed && item) {
1260                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1261                 if (fillnotstroke &&
1262                     (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1263                     Path *livarot_path = Path_for_item(item, true, true);
1264                     livarot_path->ConvertWithBackData(0.04);
1266                     boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1267                     if (position) {
1268                         Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1269                         Geom::Point delta = nearest - button_doc;
1270                         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1271                         delta = desktop->d2w(delta);
1272                         double stroke_tolerance =
1273                             ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1274                               desktop->current_zoom() *
1275                               SP_OBJECT_STYLE (item)->stroke_width.computed *
1276                               to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1277                               : 0.0)
1278                             + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1280                         if (Geom::L2 (delta) < stroke_tolerance) {
1281                             fillnotstroke = false;
1282                         }
1283                     }
1284                     delete livarot_path;
1285                 }
1287                 SPCSSAttr *css = sp_repr_css_attr_new();
1288                 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1290                 sp_desktop_apply_css_recursive( item, css, true );
1291                 item->updateRepr();
1293                 sp_document_done( doc , SP_VERB_NONE,
1294                                   _("Drop color"));
1295             }
1296             g_free(c);
1297         }
1298         break;
1300         case SVG_DATA:
1301         case SVG_XML_DATA: {
1302             gchar *svgdata = (gchar *)data->data;
1304             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1306             if (rnewdoc == NULL) {
1307                 sp_ui_error_dialog(_("Could not parse SVG data"));
1308                 return;
1309             }
1311             Inkscape::XML::Node *repr = rnewdoc->root();
1312             gchar const *style = repr->attribute("style");
1314             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1315             newgroup->setAttribute("style", style);
1317             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1318             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1319                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1320                 newgroup->appendChild(newchild);
1321             }
1323             Inkscape::GC::release(rnewdoc);
1325             // Add it to the current layer
1327             // Greg's edits to add intelligent positioning of svg drops
1328             SPObject *new_obj = NULL;
1329             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1331             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1332             selection->set(SP_ITEM(new_obj));
1333             // To move the imported object, we must temporarily set the "transform pattern with
1334             // object" option.
1335             {
1336                 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1337                 bool const saved_pref = prefs->getBool("/options/transform/pattern", true);
1338                 prefs->setBool("/options/transform/pattern", true);
1339                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1340                 Geom::OptRect sel_bbox = selection->bounds();
1341                 if (sel_bbox) {
1342                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1343                     sp_selection_move_relative(selection, m);
1344                 }
1345                 prefs->setBool("/options/transform/pattern", saved_pref);
1346             }
1348             Inkscape::GC::release(newgroup);
1349             sp_document_done(doc, SP_VERB_NONE,
1350                              _("Drop SVG"));
1351             break;
1352         }
1354         case URI_LIST: {
1355             gchar *uri = (gchar *)data->data;
1356             sp_ui_import_files(uri);
1357             break;
1358         }
1360         case PNG_DATA:
1361         case JPEG_DATA:
1362         case IMAGE_DATA: {
1363             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1364             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1365             gchar *atom_name = gdk_atom_name(data->type);
1366             
1367             // this formula taken from Glib docs
1368             guint needed_size = data->length * 4 / 3 + data->length * 4 / (3 * 72) + 7;
1369             needed_size += 5 + 8 + strlen(atom_name); // 5 bytes for data:, 8 for ;base64,
1370             
1371             gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer;
1372             buf_work += g_sprintf(buffer, "data:%s;base64,", atom_name);
1373             
1374             gint state = 0, save = 0;
1375             g_base64_encode_step(data->data, data->length, TRUE, buf_work, &state, &save);
1376             g_base64_encode_close(TRUE, buf_work, &state, &save);
1378             newImage->setAttribute("xlink:href", buffer);
1379             g_free(buffer);
1381             GError *error = NULL;
1382             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1383             if ( loader ) {
1384                 error = NULL;
1385                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1386                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1387                     if ( pbuf ) {
1388                         char tmp[1024];
1389                         int width = gdk_pixbuf_get_width(pbuf);
1390                         int height = gdk_pixbuf_get_height(pbuf);
1391                         snprintf( tmp, sizeof(tmp), "%d", width );
1392                         newImage->setAttribute("width", tmp);
1394                         snprintf( tmp, sizeof(tmp), "%d", height );
1395                         newImage->setAttribute("height", tmp);
1396                     }
1397                 }
1398             }
1399             g_free(atom_name);
1401             // Add it to the current layer
1402             desktop->currentLayer()->appendChildRepr(newImage);
1404             Inkscape::GC::release(newImage);
1405             sp_document_done( doc , SP_VERB_NONE,
1406                               _("Drop bitmap image"));
1407             break;
1408         }
1409     }
1412 #include "gradient-context.h"
1414 void sp_ui_drag_motion( GtkWidget */*widget*/,
1415                         GdkDragContext */*drag_context*/,
1416                         gint /*x*/, gint /*y*/,
1417                         GtkSelectionData */*data*/,
1418                         guint /*info*/,
1419                         guint /*event_time*/,
1420                         gpointer /*user_data*/)
1422 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1423 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1426 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1429 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1430                               GdkDragContext */*drag_context*/,
1431                               guint /*event_time*/,
1432                               gpointer /*user_data*/ )
1434 //     g_message("drag-n-drop leave                at %d", event_time);
1437 static void
1438 sp_ui_import_files(gchar *buffer)
1440     GList *list = gnome_uri_list_extract_filenames(buffer);
1441     if (!list)
1442         return;
1443     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1444     g_list_foreach(list, (GFunc) g_free, NULL);
1445     g_list_free(list);
1448 static void
1449 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1451     if (filename) {
1452         if (strlen((char const *)filename) > 2)
1453             sp_ui_import_one_file((char const *)filename);
1454     }
1457 static void
1458 sp_ui_import_one_file(char const *filename)
1460     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1461     if (!doc) return;
1463     if (filename == NULL) return;
1465     // Pass off to common implementation
1466     // TODO might need to get the proper type of Inkscape::Extension::Extension
1467     file_import( doc, filename, NULL );
1470 void
1471 sp_ui_error_dialog(gchar const *message)
1473     GtkWidget *dlg;
1474     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1476     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1477                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1478     sp_transientize(dlg);
1479     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1480     gtk_dialog_run(GTK_DIALOG(dlg));
1481     gtk_widget_destroy(dlg);
1482     g_free(safeMsg);
1485 bool
1486 sp_ui_overwrite_file(gchar const *filename)
1488     bool return_value = FALSE;
1490     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1491         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1492         gchar* baseName = g_path_get_basename( filename );
1493         gchar* dirName = g_path_get_dirname( filename );
1494         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1495                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1496                                                                 GTK_MESSAGE_QUESTION,
1497                                                                 GTK_BUTTONS_NONE,
1498                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1499                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1500                                                                 baseName,
1501                                                                 dirName
1502             );
1503         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1504                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1505                                 _("Replace"), GTK_RESPONSE_YES,
1506                                 NULL );
1507         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1509         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1510             return_value = TRUE;
1511         } else {
1512             return_value = FALSE;
1513         }
1514         gtk_widget_destroy(dialog);
1515         g_free( baseName );
1516         g_free( dirName );
1517     } else {
1518         return_value = TRUE;
1519     }
1521     return return_value;
1524 static void
1525 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1527     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1530 static void
1531 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1533     void *child = GTK_BIN (data)->child;
1534     //child is either
1535     //- a GtkHBox, whose first child is a label displaying name if the menu
1536     //item has an accel key
1537     //- a GtkLabel if the menu has no accel key
1538     if (GTK_IS_LABEL(child)) {
1539         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1540     } else if (GTK_IS_HBOX(child)) {
1541         gtk_label_set_markup_with_mnemonic(
1542         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1543         name.c_str());
1544     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1545     //a menu item in yet another way...
1549 /*
1550   Local Variables:
1551   mode:c++
1552   c-file-style:"stroustrup"
1553   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1554   indent-tabs-mode:nil
1555   fill-column:99
1556   End:
1557 */
1558 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :