Code

No more NRMatrix or NRPoint.
[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     {"text/uri-list", 0, URI_LIST},
93     {"image/svg+xml", 0, SVG_XML_DATA},
94     {"image/svg",     0, SVG_DATA},
95     {"image/png",     0, PNG_DATA},
96     {"image/jpeg",    0, JPEG_DATA},
97 #if ENABLE_MAGIC_COLORS
98     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
99 #endif // ENABLE_MAGIC_COLORS
100     {"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     if (editable) {
156                 g_object_set_data(G_OBJECT(vw), "window", win);
157                 
158                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
159                 SPDesktop* desktop = desktop_widget->desktop;
160                 
161                 desktop_widget->window = win;
163         /* fixme: doesn't allow making window any smaller than this */
164         win->set_default_size(640, 480);
165                 
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(NR::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     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
221     gtk_widget_show(GTK_WIDGET(vw));
223     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
224     {
225         std::vector<gchar*> types;
227         GSList *list = gdk_pixbuf_get_formats();
228         while ( list ) {
229             int i = 0;
230             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
231             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
232             for ( i = 0; typesXX[i]; i++ ) {
233                 types.push_back(g_strdup(typesXX[i]));
234             }
235             g_strfreev(typesXX);
237             list = g_slist_next(list);
238         }
239         completeDropTargetsCount = nui_drop_target_entries + types.size();
240         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
241         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
242             completeDropTargets[i] = ui_drop_target_entries[i];
243         }
244         int pos = nui_drop_target_entries;
246         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
247             completeDropTargets[pos].target = *it;
248             completeDropTargets[pos].flags = 0;
249             completeDropTargets[pos].info = IMAGE_DATA;
250             pos++;
251         }
252     }
254     gtk_drag_dest_set((GtkWidget*)win->gobj(),
255                       GTK_DEST_DEFAULT_ALL,
256                       completeDropTargets,
257                       completeDropTargetsCount,
258                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
261     g_signal_connect(G_OBJECT(win->gobj()),
262                      "drag_data_received",
263                      G_CALLBACK(sp_ui_drag_data_received),
264                      NULL);
265     g_signal_connect(G_OBJECT(win->gobj()),
266                      "drag_motion",
267                      G_CALLBACK(sp_ui_drag_motion),
268                      NULL);
269     g_signal_connect(G_OBJECT(win->gobj()),
270                      "drag_leave",
271                      G_CALLBACK(sp_ui_drag_leave),
272                      NULL);
273     win->show();
275     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
276     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
279 void
280 sp_ui_new_view()
282     SPDocument *document;
283     SPViewWidget *dtw;
285     document = SP_ACTIVE_DOCUMENT;
286     if (!document) return;
288     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
289     g_return_if_fail(dtw != NULL);
291     sp_create_window(dtw, TRUE);
292     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
293     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
296 /* TODO: not yet working */
297 /* To be re-enabled (by adding to menu) once it works. */
298 void
299 sp_ui_new_view_preview()
301     SPDocument *document;
302     SPViewWidget *dtw;
304     document = SP_ACTIVE_DOCUMENT;
305     if (!document) return;
307     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
308     g_return_if_fail(dtw != NULL);
309     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
311     sp_create_window(dtw, FALSE);
314 /**
315  * \param widget unused
316  */
317 void
318 sp_ui_close_view(GtkWidget */*widget*/)
320     if (SP_ACTIVE_DESKTOP == NULL) {
321         return;
322     }
323     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
324         return;
325     }
326     SP_ACTIVE_DESKTOP->destroyWidget();
330 /**
331  *  sp_ui_close_all
332  *
333  *  This function is called to exit the program, and iterates through all
334  *  open document view windows, attempting to close each in turn.  If the
335  *  view has unsaved information, the user will be prompted to save,
336  *  discard, or cancel.
337  *
338  *  Returns FALSE if the user cancels the close_all operation, TRUE
339  *  otherwise.
340  */
341 unsigned int
342 sp_ui_close_all(void)
344     /* Iterate through all the windows, destroying each in the order they
345        become active */
346     while (SP_ACTIVE_DESKTOP) {
347         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
348             /* The user cancelled the operation, so end doing the close */
349             return FALSE;
350         }
351         SP_ACTIVE_DESKTOP->destroyWidget();
352     }
354     return TRUE;
357 /*
358  * Some day when the right-click menus are ready to start working
359  * smarter with the verbs, we'll need to change this NULL being
360  * sent to sp_action_perform to something useful, or set some kind
361  * of global "right-clicked position" variable for actions to
362  * investigate when they're called.
363  */
364 static void
365 sp_ui_menu_activate(void */*object*/, SPAction *action)
367     sp_action_perform(action, NULL);
370 static void
371 sp_ui_menu_select_action(void */*object*/, SPAction *action)
373     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
376 static void
377 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
379     action->view->tipsMessageContext()->clear();
382 static void
383 sp_ui_menu_select(gpointer object, gpointer tip)
385     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
386     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
389 static void
390 sp_ui_menu_deselect(gpointer object)
392     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
393     view->tipsMessageContext()->clear();
396 /**
397  * sp_ui_menuitem_add_icon
398  *
399  * Creates and attaches a scaled icon to the given menu item.
400  *
401  */
402 void
403 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
405     GtkWidget *icon;
407     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
408     gtk_widget_show(icon);
409     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
410 } // end of sp_ui_menu_add_icon
412 /**
413  * sp_ui_menu_append_item
414  *
415  * Appends a UI item with specific info for Inkscape/Sodipodi.
416  *
417  */
418 static GtkWidget *
419 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
420                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
421                         gpointer data, gboolean with_mnemonic = TRUE )
423     GtkWidget *item;
425     if (stock) {
426         item = gtk_image_menu_item_new_from_stock(stock, NULL);
427     } else if (label) {
428         item = (with_mnemonic)
429             ? gtk_image_menu_item_new_with_mnemonic(label) :
430             gtk_image_menu_item_new_with_label(label);
431     } else {
432         item = gtk_separator_menu_item_new();
433     }
435     gtk_widget_show(item);
437     if (callback) {
438         g_signal_connect(G_OBJECT(item), "activate", callback, data);
439     }
441     if (tip && view) {
442         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
443         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
444         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
445     }
447     gtk_menu_append(GTK_MENU(menu), item);
449     return item;
451 } // end of sp_ui_menu_append_item()
453 /**
454 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
455  */
456 static gchar const *
457 sp_key_name(guint keyval)
459     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
460        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
461     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
463     if      (!strcmp(n, "asciicircum"))  return "^";
464     else if (!strcmp(n, "parenleft"  ))  return "(";
465     else if (!strcmp(n, "parenright" ))  return ")";
466     else if (!strcmp(n, "plus"       ))  return "+";
467     else if (!strcmp(n, "minus"      ))  return "-";
468     else if (!strcmp(n, "asterisk"   ))  return "*";
469     else if (!strcmp(n, "KP_Multiply"))  return "*";
470     else if (!strcmp(n, "Delete"     ))  return "Del";
471     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
472     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
473     else if (!strcmp(n, "grave"      ))  return "`";
474     else if (!strcmp(n, "numbersign" ))  return "#";
475     else if (!strcmp(n, "bar"        ))  return "|";
476     else if (!strcmp(n, "slash"      ))  return "/";
477     else if (!strcmp(n, "exclam"     ))  return "!";
478     else if (!strcmp(n, "percent"    ))  return "%";
479     else return n;
483 /**
484  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
485  * \param c Points to a buffer at least 256 bytes long.
486  */
487 void
488 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
490     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
491      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
492      * Will probably need to change sp_shortcut_invoke callers.
493      *
494      * The existing gtk_label_new_with_mnemonic call can be replaced with
495      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
496      * gtk_label_set_text_with_mnemonic(lbl, str).
497      */
498     static GtkAccelLabelClass const &accel_lbl_cls
499         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
501     struct { unsigned test; char const *name; } const modifier_tbl[] = {
502         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
503         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
504         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
505     };
507     gchar *p = c;
508     gchar *end = p + 256;
510     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
511         if ((shortcut & modifier_tbl[i].test)
512             && (p < end))
513         {
514             p += g_snprintf(p, end - p, "%s%s",
515                             modifier_tbl[i].name,
516                             accel_lbl_cls.mod_separator);
517         }
518     }
519     if (p < end) {
520         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
521     }
522     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
525 void
526 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
528     SPAction     *action;
529     unsigned int shortcut;
530     gchar        *s;
531     gchar        key[256];
532     gchar        *atitle;
534     action = verb->get_action(NULL);
535     if (!action)
536         return;
538     atitle = sp_action_get_title(action);
540     s = g_stpcpy(c, atitle);
542     g_free(atitle);
544     shortcut = sp_shortcut_get_primary(verb);
545     if (shortcut) {
546         s = g_stpcpy(s, " (");
547         sp_ui_shortcut_string(shortcut, key);
548         s = g_stpcpy(s, key);
549         s = g_stpcpy(s, ")");
550     }
554 /**
555  * sp_ui_menu_append_item_from_verb
556  *
557  * Appends a custom menu UI from a verb.
558  *
559  */
561 static GtkWidget *
562 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
564     SPAction *action;
565     GtkWidget *item;
567     if (verb->get_code() == SP_VERB_NONE) {
569         item = gtk_separator_menu_item_new();
571     } else {
572         unsigned int shortcut;
574         action = verb->get_action(view);
576         if (!action) return NULL;
578         shortcut = sp_shortcut_get_primary(verb);
579         if (shortcut) {
580             gchar c[256];
581             sp_ui_shortcut_string(shortcut, c);
582             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
583             GtkWidget *const name_lbl = gtk_label_new("");
584             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
585             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
586             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
587             GtkWidget *const accel_lbl = gtk_label_new(c);
588             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
589             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
590             gtk_widget_show_all(hb);
591             if (radio) {
592                 item = gtk_radio_menu_item_new (group);
593             } else {
594                 item = gtk_image_menu_item_new();
595             }
596             gtk_container_add((GtkContainer *) item, hb);
597         } else {
598             if (radio) {
599                 item = gtk_radio_menu_item_new (group);
600             } else {
601                 item = gtk_image_menu_item_new ();
602             }
603             GtkWidget *const name_lbl = gtk_label_new("");
604             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
605             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
606             gtk_container_add((GtkContainer *) item, name_lbl);
607         }
609         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
610         if (!action->sensitive) {
611             gtk_widget_set_sensitive(item, FALSE);
612         }
614         if (action->image) {
615             sp_ui_menuitem_add_icon(item, action->image);
616         }
617         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
618         g_signal_connect( G_OBJECT(item), "activate",
619                           G_CALLBACK(sp_ui_menu_activate), action );
621         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
622         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
623     }
625     gtk_widget_show(item);
626     gtk_menu_append(GTK_MENU(menu), item);
628     return item;
630 } // end of sp_ui_menu_append_item_from_verb
633 static void
634 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
636     gchar const *pref = (gchar const *) user_data;
637     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
639     gchar const *pref_path;
640     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
641         pref_path = g_strconcat("fullscreen.", pref, NULL);
642     else
643         pref_path = g_strconcat("window.", pref, NULL);
645     gboolean checked = gtk_check_menu_item_get_active(menuitem);
646     prefs_set_int_attribute(pref_path, "state", checked);
648     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
651 static gboolean
652 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
654     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
656     gchar const *pref = (gchar const *) user_data;
657     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
659     gchar const *pref_path;
660     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
661         pref_path = g_strconcat("fullscreen.", pref, NULL);
662     else
663         pref_path = g_strconcat("window.", pref, NULL);
665     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
667     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
668     gtk_check_menu_item_set_active(menuitem, ison);
669     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
671     return FALSE;
675 void
676 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
677                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
678                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
679                                        Inkscape::Verb *verb)
681     GtkWidget *item;
683     unsigned int shortcut = 0;
684     SPAction *action = NULL;
686     if (verb) {
687         shortcut = sp_shortcut_get_primary(verb);
688         action = verb->get_action(view);
689     }
691     if (verb && shortcut) {
692         gchar c[256];
693         sp_ui_shortcut_string(shortcut, c);
695         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
697         {
698             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
699             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
700             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
701         }
703         {
704             GtkWidget *l = gtk_label_new(c);
705             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
706             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
707         }
709         gtk_widget_show_all(hb);
711         item = gtk_check_menu_item_new();
712         gtk_container_add((GtkContainer *) item, hb);
713     } else {
714         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
715         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
716         item = gtk_check_menu_item_new();
717         gtk_container_add((GtkContainer *) item, l);
718     }
719 #if 0
720     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
721     if (!action->sensitive) {
722         gtk_widget_set_sensitive(item, FALSE);
723     }
724 #endif
725     gtk_widget_show(item);
727     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
729     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
731     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
732     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
734     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
735     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
738 static void
739 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
741     sp_file_open(uri, NULL);
744 static void
745 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
747     sp_file_new(uri);
750 void
751 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
753     std::list<gchar *> sources;
754     sources.push_back( profile_path("templates") ); // first try user's local dir
755     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
757     // Use this loop to iterate through a list of possible document locations.
758     while (!sources.empty()) {
759         gchar *dirname = sources.front();
761         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
762             GError *err = 0;
763             GDir *dir = g_dir_open(dirname, 0, &err);
765             if (dir) {
766                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
767                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
768                         continue; // skip non-svg files
770                     gchar *basename = g_path_get_basename(file);
771                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
772                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
774                     gchar const *filepath = g_build_filename(dirname, file, NULL);
775                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
776                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
777                     g_free(dupfile);
778                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
779                     g_free(filename);
781                     gtk_widget_show(item);
782                     // how does "filepath" ever get freed?
783                     g_signal_connect(G_OBJECT(item),
784                                      "activate",
785                                      G_CALLBACK(sp_file_new_from_template),
786                                      (gpointer) filepath);
788                     if (view) {
789                         // set null tip for now; later use a description from the template file
790                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
791                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
792                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
793                     }
795                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
796                 }
797                 g_dir_close(dir);
798             }
799         }
801         // toss the dirname
802         g_free(dirname);
803         sources.pop_front();
804     }
807 void
808 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
810     gchar const **recent = prefs_get_recent_files();
811     if (recent) {
812         int i;
814         for (i = 0; recent[i] != NULL; i += 2) {
815             gchar const *uri = recent[i];
816             gchar const *name = recent[i + 1];
818             GtkWidget *item = gtk_menu_item_new_with_label(name);
819             gtk_widget_show(item);
820             g_signal_connect(G_OBJECT(item),
821                              "activate",
822                              G_CALLBACK(sp_recent_open),
823                              (gpointer)uri);
824             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
825         }
827         g_free(recent);
828     } else {
829         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
830         gtk_widget_show(item);
831         gtk_widget_set_sensitive(item, FALSE);
832         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
833     }
836 void
837 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
839     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
840     //                                       checkitem_toggled, checkitem_update, 0);
841     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
842                                            checkitem_toggled, checkitem_update, 0);
843     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
844                                            checkitem_toggled, checkitem_update, 0);
845     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
846                                            checkitem_toggled, checkitem_update, 0);
847     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
848                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
849     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
850                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
851     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
852                                            checkitem_toggled, checkitem_update, 0);
853     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
854                                            checkitem_toggled, checkitem_update, 0);
857 /** \brief  This function turns XML into a menu
858     \param  menus  This is the XML that defines the menu
859     \param  menu   Menu to be added to
860     \param  view   The View that this menu is being built for
862     This function is realitively simple as it just goes through the XML
863     and parses the individual elements.  In the case of a submenu, it
864     just calls itself recursively.  Because it is only reasonable to have
865     a couple of submenus, it is unlikely this will go more than two or
866     three times.
868     In the case of an unreconginzed verb, a menu item is made to identify
869     the verb that is missing, and display that.  The menu item is also made
870     insensitive.
871 */
872 void
873 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
875     if (menus == NULL) return;
876     if (menu == NULL)  return;
877     GSList *group = NULL;
879     for (Inkscape::XML::Node *menu_pntr = menus;
880          menu_pntr != NULL;
881          menu_pntr = menu_pntr->next()) {
882         if (!strcmp(menu_pntr->name(), "submenu")) {
883             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
884             GtkWidget *submenu = gtk_menu_new();
885             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
886             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
887             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
888             continue;
889         }
890         if (!strcmp(menu_pntr->name(), "verb")) {
891             gchar const *verb_name = menu_pntr->attribute("verb-id");
892             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
894             if (verb != NULL) {
895                 if (menu_pntr->attribute("radio") != NULL) {
896                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
897                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
898                     if (menu_pntr->attribute("default") != NULL) {
899                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
900                     }
901                 } else {
902                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
903                     group = NULL;
904                 }
905             } else {
906                 gchar string[120];
907                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
908                 string[119] = '\0'; /* may not be terminated */
909                 GtkWidget *item = gtk_menu_item_new_with_label(string);
910                 gtk_widget_set_sensitive(item, false);
911                 gtk_widget_show(item);
912                 gtk_menu_append(GTK_MENU(menu), item);
913             }
914             continue;
915         }
916         if (!strcmp(menu_pntr->name(), "separator")
917                 // This was spelt wrong in the original version
918                 // and so this is for backward compatibility.  It can
919                 // probably be dropped after the 0.44 release.
920              || !strcmp(menu_pntr->name(), "seperator")) {
921             GtkWidget *item = gtk_separator_menu_item_new();
922             gtk_widget_show(item);
923             gtk_menu_append(GTK_MENU(menu), item);
924             continue;
925         }
926         if (!strcmp(menu_pntr->name(), "template-list")) {
927             sp_menu_append_new_templates(menu, view);
928             continue;
929         }
930         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
931             sp_menu_append_recent_documents(menu, view);
932             continue;
933         }
934         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
935             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
936             continue;
937         }
938     }
941 /** \brief  Build the main tool bar
942     \param  view  View to build the bar for
944     Currently the main tool bar is built as a dynamic XML menu using
945     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
946     pass it to get items attached to it.
947 */
948 GtkWidget *
949 sp_ui_main_menubar(Inkscape::UI::View::View *view)
951     GtkWidget *mbar = gtk_menu_bar_new();
953 #ifdef GDK_WINDOWING_QUARTZ
954         ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
955 #endif
957     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
959 #ifdef GDK_WINDOWING_QUARTZ
960         return NULL;
961 #else
962     return mbar;
963 #endif
966 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
967     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
970 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
971     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
972     sp_desktop_selection(desktop)->clear();
975 GtkWidget *
976 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
978     GtkWidget *m;
979     SPDesktop *dt;
981     dt = static_cast<SPDesktop*>(view);
983     m = gtk_menu_new();
985     /* Undo and Redo */
986     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
987     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
989     /* Separator */
990     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
992     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
993     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
994     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
996     /* Separator */
997     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
999     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1000     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1002     /* Item menu */
1003     if (item) {
1004         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1005         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1006     }
1008     /* layer menu */
1009     SPGroup *group=NULL;
1010     if (item) {
1011         if (SP_IS_GROUP(item)) {
1012             group = SP_GROUP(item);
1013         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1014             group = SP_GROUP(SP_OBJECT_PARENT(item));
1015         }
1016     }
1018     if (( group && group != dt->currentLayer() ) ||
1019         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1020         /* Separator */
1021         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1022     }
1024     if ( group && group != dt->currentLayer() ) {
1025         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1026         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1027         GtkWidget *w = gtk_menu_item_new_with_label(label);
1028         g_free(label);
1029         g_object_set_data(G_OBJECT(w), "group", group);
1030         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1031         gtk_widget_show(w);
1032         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1033     }
1035     if ( dt->currentLayer() != dt->currentRoot() ) {
1036         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1037             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1038             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1039             gtk_widget_show(w);
1040             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1042         }
1043     }
1045     return m;
1048 /* Drag and Drop */
1049 void
1050 sp_ui_drag_data_received(GtkWidget *widget,
1051                          GdkDragContext *drag_context,
1052                          gint x, gint y,
1053                          GtkSelectionData *data,
1054                          guint info,
1055                          guint /*event_time*/,
1056                          gpointer /*user_data*/)
1058     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1059     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1061     switch (info) {
1062 #if ENABLE_MAGIC_COLORS
1063         case APP_X_INKY_COLOR:
1064         {
1065             int destX = 0;
1066             int destY = 0;
1067             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1068             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1070             SPItem *item = desktop->item_at_point( where, true );
1071             if ( item )
1072             {
1073                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1075                 if ( data->length >= 8 ) {
1076                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1078                     gchar c[64] = {0};
1079                     // Careful about endian issues.
1080                     guint16* dataVals = (guint16*)data->data;
1081                     sp_svg_write_color( c, sizeof(c),
1082                                         SP_RGBA32_U_COMPOSE(
1083                                             0x0ff & (dataVals[0] >> 8),
1084                                             0x0ff & (dataVals[1] >> 8),
1085                                             0x0ff & (dataVals[2] >> 8),
1086                                             0xff // can't have transparency in the color itself
1087                                             //0x0ff & (data->data[3] >> 8),
1088                                             ));
1089                     SPCSSAttr *css = sp_repr_css_attr_new();
1090                     bool updatePerformed = false;
1092                     if ( data->length > 14 ) {
1093                         int flags = dataVals[4];
1095                         // piggie-backed palette entry info
1096                         int index = dataVals[5];
1097                         Glib::ustring palName;
1098                         for ( int i = 0; i < dataVals[6]; i++ ) {
1099                             palName += (gunichar)dataVals[7+i];
1100                         }
1102                         // Now hook in a magic tag of some sort.
1103                         if ( !palName.empty() && (flags & 1) ) {
1104                             gchar* str = g_strdup_printf("%d|", index);
1105                             palName.insert( 0, str );
1106                             g_free(str);
1107                             str = 0;
1109                             sp_object_setAttribute( SP_OBJECT(item),
1110                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1111                                                     palName.c_str(),
1112                                                     false );
1113                             item->updateRepr();
1115                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1116                             updatePerformed = true;
1117                         }
1118                     }
1120                     if ( !updatePerformed ) {
1121                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1122                     }
1124                     sp_desktop_apply_css_recursive( item, css, true );
1125                     item->updateRepr();
1127                     sp_document_done( doc , SP_VERB_NONE, 
1128                                       _("Drop color"));
1130                     if ( srgbProf ) {
1131                         cmsCloseProfile( srgbProf );
1132                     }
1133                 }
1134             }
1135         }
1136         break;
1137 #endif // ENABLE_MAGIC_COLORS
1139         case APP_X_COLOR:
1140         {
1141             int destX = 0;
1142             int destY = 0;
1143             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1144             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1145             NR::Point const button_dt(desktop->w2d(where));
1146             NR::Point const button_doc(desktop->dt2doc(button_dt));
1148             if ( data->length == 8 ) {
1149                 gchar c[64] = {0};
1150                 // Careful about endian issues.
1151                 guint16* dataVals = (guint16*)data->data;
1152                 sp_svg_write_color( c, 64,
1153                                     SP_RGBA32_U_COMPOSE(
1154                                         0x0ff & (dataVals[0] >> 8),
1155                                         0x0ff & (dataVals[1] >> 8),
1156                                         0x0ff & (dataVals[2] >> 8),
1157                                         0xff // can't have transparency in the color itself
1158                                         //0x0ff & (data->data[3] >> 8),
1159                                         ));
1161                 SPItem *item = desktop->item_at_point( where, true );
1163                 bool consumed = false;
1164                 if (desktop->event_context && desktop->event_context->get_drag()) {
1165                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1166                     if (consumed) {
1167                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1168                         desktop->event_context->get_drag()->updateDraggers();
1169                     }
1170                 }
1172                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1173                 //    consumed = sp_text_context_drop_color(c, button_doc);
1174                 //    if (consumed) {
1175                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1176                 //    }
1177                 //}
1179                 if (!consumed && item) {
1180                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1181                     if (fillnotstroke && 
1182                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1183                         Path *livarot_path = Path_for_item(item, true, true);
1184                         livarot_path->ConvertWithBackData(0.04);
1186                         NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1187                         if (position) {
1188                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1189                             NR::Point delta = nearest - button_doc;
1190                             delta = desktop->d2w(delta);
1191                             double stroke_tolerance =
1192                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1193                                   desktop->current_zoom() *
1194                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1195                                   NR::expansion(sp_item_i2d_affine(item)) * 0.5
1196                                   : 0.0)
1197                                 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1199                             if (NR::L2 (delta) < stroke_tolerance) {
1200                                 fillnotstroke = false;
1201                             }
1202                         }
1203                         delete livarot_path;
1204                     }
1206                     SPCSSAttr *css = sp_repr_css_attr_new();
1207                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1209                     sp_desktop_apply_css_recursive( item, css, true );
1210                     item->updateRepr();
1212                     sp_document_done( doc , SP_VERB_NONE, 
1213                                       _("Drop color"));
1214                 }
1215             }
1216         }
1217         break;
1219         case SVG_DATA:
1220         case SVG_XML_DATA: {
1221             gchar *svgdata = (gchar *)data->data;
1223             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1225             if (rnewdoc == NULL) {
1226                 sp_ui_error_dialog(_("Could not parse SVG data"));
1227                 return;
1228             }
1230             Inkscape::XML::Node *repr = rnewdoc->root();
1231             gchar const *style = repr->attribute("style");
1233             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1234             newgroup->setAttribute("style", style);
1236             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1237             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1238                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1239                 newgroup->appendChild(newchild);
1240             }
1242             Inkscape::GC::release(rnewdoc);
1244             // Add it to the current layer
1246             // Greg's edits to add intelligent positioning of svg drops
1247             SPObject *new_obj = NULL;
1248             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1250             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1251             selection->set(SP_ITEM(new_obj));
1252             // To move the imported object, we must temporarily set the "transform pattern with
1253             // object" option.
1254             {
1255                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1256                 prefs_set_int_attribute("options.transform", "pattern", 1);
1257                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1258                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1259                 if (sel_bbox) {
1260                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1261                     sp_selection_move_relative(selection, m);
1262                 }
1263                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1264             }
1266             Inkscape::GC::release(newgroup);
1267             sp_document_done(doc, SP_VERB_NONE, 
1268                              _("Drop SVG"));
1269             break;
1270         }
1272         case URI_LIST: {
1273             gchar *uri = (gchar *)data->data;
1274             sp_ui_import_files(uri);
1275             break;
1276         }
1278         case PNG_DATA:
1279         case JPEG_DATA:
1280         case IMAGE_DATA: {
1281             char tmp[1024];
1283             StringOutputStream outs;
1284             Base64OutputStream b64out(outs);
1285             b64out.setColumnWidth(0);
1287             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1289             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1291             for ( int i = 0; i < data->length; i++ ) {
1292                 b64out.put( data->data[i] );
1293             }
1294             b64out.close();
1297             Glib::ustring str = outs.getString();
1299             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1300             str.insert( 0, tmp );
1301             newImage->setAttribute("xlink:href", str.c_str());
1303             GError *error = NULL;
1304             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1305             if ( loader ) {
1306                 error = NULL;
1307                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1308                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1309                     if ( pbuf ) {
1310                         int width = gdk_pixbuf_get_width(pbuf);
1311                         int height = gdk_pixbuf_get_height(pbuf);
1312                         snprintf( tmp, sizeof(tmp), "%d", width );
1313                         newImage->setAttribute("width", tmp);
1315                         snprintf( tmp, sizeof(tmp), "%d", height );
1316                         newImage->setAttribute("height", tmp);
1317                     }
1318                 }
1319             }
1321             // Add it to the current layer
1322             desktop->currentLayer()->appendChildRepr(newImage);
1324             Inkscape::GC::release(newImage);
1325             sp_document_done( doc , SP_VERB_NONE, 
1326                               _("Drop bitmap image"));
1327             break;
1328         }
1329     }
1332 #include "gradient-context.h"
1334 void sp_ui_drag_motion( GtkWidget */*widget*/,
1335                         GdkDragContext */*drag_context*/,
1336                         gint /*x*/, gint /*y*/,
1337                         GtkSelectionData */*data*/,
1338                         guint /*info*/,
1339                         guint /*event_time*/,
1340                         gpointer /*user_data*/)
1342 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1343 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1346 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1349 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1350                               GdkDragContext */*drag_context*/,
1351                               guint /*event_time*/,
1352                               gpointer /*user_data*/ )
1354 //     g_message("drag-n-drop leave                at %d", event_time);
1357 static void
1358 sp_ui_import_files(gchar *buffer)
1360     GList *list = gnome_uri_list_extract_filenames(buffer);
1361     if (!list)
1362         return;
1363     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1364     g_list_foreach(list, (GFunc) g_free, NULL);
1365     g_list_free(list);
1368 static void
1369 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1371     if (filename) {
1372         if (strlen((char const *)filename) > 2)
1373             sp_ui_import_one_file((char const *)filename);
1374     }
1377 static void
1378 sp_ui_import_one_file(char const *filename)
1380     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1381     if (!doc) return;
1383     if (filename == NULL) return;
1385     // Pass off to common implementation
1386     // TODO might need to get the proper type of Inkscape::Extension::Extension
1387     file_import( doc, filename, NULL );
1390 void
1391 sp_ui_error_dialog(gchar const *message)
1393     GtkWidget *dlg;
1394     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1396     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1397                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1398     sp_transientize(dlg);
1399     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1400     gtk_dialog_run(GTK_DIALOG(dlg));
1401     gtk_widget_destroy(dlg);
1402     g_free(safeMsg);
1405 bool
1406 sp_ui_overwrite_file(gchar const *filename)
1408     bool return_value = FALSE;
1410     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1411         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1412         gchar* baseName = g_path_get_basename( filename );
1413         gchar* dirName = g_path_get_dirname( filename );
1414         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1415                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1416                                                                 GTK_MESSAGE_QUESTION,
1417                                                                 GTK_BUTTONS_NONE,
1418                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1419                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1420                                                                 baseName,
1421                                                                 dirName
1422             );
1423         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1424                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1425                                 _("Replace"), GTK_RESPONSE_YES,
1426                                 NULL );
1427         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1429         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1430             return_value = TRUE;
1431         } else {
1432             return_value = FALSE;
1433         }
1434         gtk_widget_destroy(dialog);
1435         g_free( baseName );
1436         g_free( dirName );
1437     } else {
1438         return_value = TRUE;
1439     }
1441     return return_value;
1444 static void
1445 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1447     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1450 static void
1451 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1453     void *child = GTK_BIN (data)->child;
1454     //child is either
1455     //- a GtkHBox, whose first child is a label displaying name if the menu
1456     //item has an accel key
1457     //- a GtkLabel if the menu has no accel key
1458     if (GTK_IS_LABEL(child)) {
1459         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1460     } else if (GTK_IS_HBOX(child)) {
1461         gtk_label_set_markup_with_mnemonic(
1462         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1463         name.c_str());
1464     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1465     //a menu item in yet another way...
1469 /*
1470   Local Variables:
1471   mode:c++
1472   c-file-style:"stroustrup"
1473   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1474   indent-tabs-mode:nil
1475   fill-column:99
1476   End:
1477 */
1478 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :