6806c69574fbb96df2571ba36eaac20592db0253
1 #define __SP_INTERFACE_C__
3 /**
4 * Main UI stuff
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Frank Felfe <innerspace@iname.com>
9 * bulia byak <buliabyak@users.sf.net>
10 *
11 * Copyright (C) 1999-2005 authors
12 * Copyright (C) 2001-2002 Ximian, Inc.
13 * Copyright (C) 2004 David Turner
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include "inkscape-private.h"
24 #include "extension/effect.h"
25 #include "widgets/icon.h"
26 #include "prefs-utils.h"
27 #include "path-prefix.h"
29 #include "shortcuts.h"
31 #include "document.h"
32 #include "desktop-handles.h"
33 #include "file.h"
34 #include "interface.h"
35 #include "desktop.h"
36 #include "ui/context-menu.h"
37 #include "selection.h"
38 #include "selection-chemistry.h"
39 #include "svg-view-widget.h"
40 #include "widgets/desktop-widget.h"
41 #include "sp-item-group.h"
42 #include "sp-text.h"
43 #include "sp-flowtext.h"
44 #include "sp-namedview.h"
45 #include "ui/view/view.h"
47 #include "helper/action.h"
48 #include "helper/gnome-utils.h"
49 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "io/stringstream.h"
53 #include "io/base64stream.h"
55 #include "dialogs/dialog-events.h"
57 #include "message-context.h"
59 // Added for color drag-n-drop
60 #if ENABLE_LCMS
61 #include "lcms.h"
62 #endif // ENABLE_LCMS
63 #include "display/sp-canvas.h"
64 #include "color.h"
65 #include "svg/svg-color.h"
66 #include "desktop-style.h"
67 #include "style.h"
68 #include "event-context.h"
69 #include "gradient-drag.h"
71 using Inkscape::IO::StringOutputStream;
72 using Inkscape::IO::Base64OutputStream;
74 /* Drag and Drop */
75 typedef enum {
76 URI_LIST,
77 SVG_XML_DATA,
78 SVG_DATA,
79 PNG_DATA,
80 JPEG_DATA,
81 IMAGE_DATA,
82 APP_X_INKY_COLOR,
83 APP_X_COLOR
84 } ui_drop_target_info;
86 static GtkTargetEntry ui_drop_target_entries [] = {
87 {"text/uri-list", 0, URI_LIST},
88 {"image/svg+xml", 0, SVG_XML_DATA},
89 {"image/svg", 0, SVG_DATA},
90 {"image/png", 0, PNG_DATA},
91 {"image/jpeg", 0, JPEG_DATA},
92 #if ENABLE_MAGIC_COLORS
93 {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
94 #endif // ENABLE_MAGIC_COLORS
95 {"application/x-color", 0, APP_X_COLOR}
96 };
98 static GtkTargetEntry *completeDropTargets = 0;
99 static int completeDropTargetsCount = 0;
101 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
102 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
103 static void sp_ui_import_files(gchar *buffer);
104 static void sp_ui_import_one_file(char const *filename);
105 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
106 static void sp_ui_drag_data_received(GtkWidget *widget,
107 GdkDragContext *drag_context,
108 gint x, gint y,
109 GtkSelectionData *data,
110 guint info,
111 guint event_time,
112 gpointer user_data);
113 static void sp_ui_menu_item_set_sensitive(SPAction *action,
114 unsigned int sensitive,
115 void *data);
116 static void sp_ui_menu_item_set_name(SPAction *action,
117 Glib::ustring name,
118 void *data);
120 SPActionEventVector menu_item_event_vector = {
121 {NULL},
122 NULL,
123 NULL, /* set_active */
124 sp_ui_menu_item_set_sensitive, /* set_sensitive */
125 NULL, /* set_shortcut */
126 sp_ui_menu_item_set_name /* set_name */
127 };
129 static const int MIN_ONSCREEN_DISTANCE = 50;
131 void
132 sp_create_window(SPViewWidget *vw, gboolean editable)
133 {
134 g_return_if_fail(vw != NULL);
135 g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
137 Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
139 if (editable) {
140 g_object_set_data(G_OBJECT(vw), "window", win);
142 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
143 SPDesktop* desktop = desktop_widget->desktop;
145 desktop_widget->window = win;
147 /* fixme: doesn't allow making window any smaller than this */
148 win->set_default_size(640, 480);
150 win->set_data("desktop", desktop);
151 win->set_data("desktopwidget", desktop_widget);
153 win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
154 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
155 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
157 gint prefs_geometry =
158 (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
159 if (prefs_geometry) {
160 gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
161 gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
162 gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
163 gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
164 gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
165 gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
166 if (pw>0 && ph>0) {
167 gint w = MIN(gdk_screen_width(), pw);
168 gint h = MIN(gdk_screen_height(), ph);
169 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
170 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
171 if (w>0 && h>0 && x>0 && y>0) {
172 x = MIN(gdk_screen_width() - w, x);
173 y = MIN(gdk_screen_height() - h, y);
174 }
175 if (w>0 && h>0) {
176 desktop->setWindowSize(w, h);
177 }
179 // Only restore xy for the first window so subsequent windows don't overlap exactly
180 // with first. (Maybe rule should be only restore xy if it's different from xy of
181 // other desktops?)
183 // Empirically it seems that active_desktop==this desktop only the first time a
184 // desktop is created.
185 if (x>0 && y>0) {
186 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
187 if (active_desktop == desktop || active_desktop==NULL) {
188 desktop->setWindowPosition(NR::Point(x, y));
189 }
190 }
191 }
192 if (maxed) {
193 win->maximize();
194 }
195 if (full) {
196 win->fullscreen();
197 }
198 }
200 } else {
201 gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
202 }
204 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
205 gtk_widget_show(GTK_WIDGET(vw));
207 if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
208 {
209 std::vector<gchar*> types;
211 GSList *list = gdk_pixbuf_get_formats();
212 while ( list ) {
213 int i = 0;
214 GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
215 gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
216 for ( i = 0; typesXX[i]; i++ ) {
217 types.push_back(g_strdup(typesXX[i]));
218 }
219 g_strfreev(typesXX);
221 list = g_slist_next(list);
222 }
223 completeDropTargetsCount = nui_drop_target_entries + types.size();
224 completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
225 for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
226 completeDropTargets[i] = ui_drop_target_entries[i];
227 }
228 int pos = nui_drop_target_entries;
230 for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
231 completeDropTargets[pos].target = *it;
232 completeDropTargets[pos].flags = 0;
233 completeDropTargets[pos].info = IMAGE_DATA;
234 pos++;
235 }
236 }
238 gtk_drag_dest_set((GtkWidget*)win->gobj(),
239 GTK_DEST_DEFAULT_ALL,
240 completeDropTargets,
241 completeDropTargetsCount,
242 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
243 g_signal_connect(G_OBJECT(win->gobj()),
244 "drag_data_received",
245 G_CALLBACK(sp_ui_drag_data_received),
246 NULL);
247 win->show();
249 // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
250 inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
251 }
253 void
254 sp_ui_new_view()
255 {
256 SPDocument *document;
257 SPViewWidget *dtw;
259 document = SP_ACTIVE_DOCUMENT;
260 if (!document) return;
262 dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
263 g_return_if_fail(dtw != NULL);
265 sp_create_window(dtw, TRUE);
266 sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
267 sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
268 }
270 /* TODO: not yet working */
271 /* To be re-enabled (by adding to menu) once it works. */
272 void
273 sp_ui_new_view_preview()
274 {
275 SPDocument *document;
276 SPViewWidget *dtw;
278 document = SP_ACTIVE_DOCUMENT;
279 if (!document) return;
281 dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
282 g_return_if_fail(dtw != NULL);
283 sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
285 sp_create_window(dtw, FALSE);
286 }
288 /**
289 * \param widget unused
290 */
291 void
292 sp_ui_close_view(GtkWidget *widget)
293 {
294 if (SP_ACTIVE_DESKTOP == NULL) {
295 return;
296 }
297 if ((SP_ACTIVE_DESKTOP)->shutdown()) {
298 return;
299 }
300 SP_ACTIVE_DESKTOP->destroyWidget();
301 }
304 /**
305 * sp_ui_close_all
306 *
307 * This function is called to exit the program, and iterates through all
308 * open document view windows, attempting to close each in turn. If the
309 * view has unsaved information, the user will be prompted to save,
310 * discard, or cancel.
311 *
312 * Returns FALSE if the user cancels the close_all operation, TRUE
313 * otherwise.
314 */
315 unsigned int
316 sp_ui_close_all(void)
317 {
318 /* Iterate through all the windows, destroying each in the order they
319 become active */
320 while (SP_ACTIVE_DESKTOP) {
321 if ((SP_ACTIVE_DESKTOP)->shutdown()) {
322 /* The user cancelled the operation, so end doing the close */
323 return FALSE;
324 }
325 SP_ACTIVE_DESKTOP->destroyWidget();
326 }
328 return TRUE;
329 }
331 /*
332 * Some day when the right-click menus are ready to start working
333 * smarter with the verbs, we'll need to change this NULL being
334 * sent to sp_action_perform to something useful, or set some kind
335 * of global "right-clicked position" variable for actions to
336 * investigate when they're called.
337 */
338 static void
339 sp_ui_menu_activate(void *object, SPAction *action)
340 {
341 sp_action_perform(action, NULL);
342 }
344 static void
345 sp_ui_menu_select_action(void *object, SPAction *action)
346 {
347 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
348 }
350 static void
351 sp_ui_menu_deselect_action(void *object, SPAction *action)
352 {
353 action->view->tipsMessageContext()->clear();
354 }
356 static void
357 sp_ui_menu_select(gpointer object, gpointer tip)
358 {
359 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
360 view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
361 }
363 static void
364 sp_ui_menu_deselect(gpointer object)
365 {
366 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
367 view->tipsMessageContext()->clear();
368 }
370 /**
371 * sp_ui_menuitem_add_icon
372 *
373 * Creates and attaches a scaled icon to the given menu item.
374 *
375 */
376 void
377 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
378 {
379 GtkWidget *icon;
381 icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
382 gtk_widget_show(icon);
383 gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
384 } // end of sp_ui_menu_add_icon
386 /**
387 * sp_ui_menu_append_item
388 *
389 * Appends a UI item with specific info for Inkscape/Sodipodi.
390 *
391 */
392 static GtkWidget *
393 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
394 gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
395 gpointer data, gboolean with_mnemonic = TRUE )
396 {
397 GtkWidget *item;
399 if (stock) {
400 item = gtk_image_menu_item_new_from_stock(stock, NULL);
401 } else if (label) {
402 item = (with_mnemonic)
403 ? gtk_image_menu_item_new_with_mnemonic(label) :
404 gtk_image_menu_item_new_with_label(label);
405 } else {
406 item = gtk_separator_menu_item_new();
407 }
409 gtk_widget_show(item);
411 if (callback) {
412 g_signal_connect(G_OBJECT(item), "activate", callback, data);
413 }
415 if (tip && view) {
416 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
417 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
418 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
419 }
421 gtk_menu_append(GTK_MENU(menu), item);
423 return item;
425 } // end of sp_ui_menu_append_item()
427 /**
428 \brief a wrapper around gdk_keyval_name producing (when possible) characters, not names
429 */
430 static gchar const *
431 sp_key_name(guint keyval)
432 {
433 /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
434 simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
435 gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
437 if (!strcmp(n, "asciicircum")) return "^";
438 else if (!strcmp(n, "parenleft" )) return "(";
439 else if (!strcmp(n, "parenright" )) return ")";
440 else if (!strcmp(n, "plus" )) return "+";
441 else if (!strcmp(n, "minus" )) return "-";
442 else if (!strcmp(n, "asterisk" )) return "*";
443 else if (!strcmp(n, "KP_Multiply")) return "*";
444 else if (!strcmp(n, "Delete" )) return "Del";
445 else if (!strcmp(n, "Page_Up" )) return "PgUp";
446 else if (!strcmp(n, "Page_Down" )) return "PgDn";
447 else if (!strcmp(n, "grave" )) return "`";
448 else if (!strcmp(n, "numbersign" )) return "#";
449 else if (!strcmp(n, "bar" )) return "|";
450 else if (!strcmp(n, "slash" )) return "/";
451 else if (!strcmp(n, "exclam" )) return "!";
452 else return n;
453 }
456 /**
457 * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
458 * \param c Points to a buffer at least 256 bytes long.
459 */
460 void
461 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
462 {
463 /* TODO: This function shouldn't exist. Our callers should use GtkAccelLabel instead of
464 * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
465 * Will probably need to change sp_shortcut_invoke callers.
466 *
467 * The existing gtk_label_new_with_mnemonic call can be replaced with
468 * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
469 * gtk_label_set_text_with_mnemonic(lbl, str).
470 */
471 static GtkAccelLabelClass const &accel_lbl_cls
472 = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
474 struct { unsigned test; char const *name; } const modifier_tbl[] = {
475 { SP_SHORTCUT_SHIFT_MASK, accel_lbl_cls.mod_name_shift },
476 { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
477 { SP_SHORTCUT_ALT_MASK, accel_lbl_cls.mod_name_alt }
478 };
480 gchar *p = c;
481 gchar *end = p + 256;
483 for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
484 if ((shortcut & modifier_tbl[i].test)
485 && (p < end))
486 {
487 p += g_snprintf(p, end - p, "%s%s",
488 modifier_tbl[i].name,
489 accel_lbl_cls.mod_separator);
490 }
491 }
492 if (p < end) {
493 p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
494 }
495 end[-1] = '\0'; // snprintf doesn't guarantee to nul-terminate the string.
496 }
498 void
499 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
500 {
501 SPAction *action;
502 unsigned int shortcut;
503 gchar *s;
504 gchar key[256];
505 gchar *atitle;
507 action = verb->get_action(NULL);
508 if (!action)
509 return;
511 atitle = sp_action_get_title(action);
513 s = g_stpcpy(c, atitle);
515 g_free(atitle);
517 shortcut = sp_shortcut_get_primary(verb);
518 if (shortcut) {
519 s = g_stpcpy(s, " (");
520 sp_ui_shortcut_string(shortcut, key);
521 s = g_stpcpy(s, key);
522 s = g_stpcpy(s, ")");
523 }
524 }
527 /**
528 * sp_ui_menu_append_item_from_verb
529 *
530 * Appends a custom menu UI from a verb.
531 *
532 */
534 static GtkWidget *
535 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
536 {
537 SPAction *action;
538 GtkWidget *item;
540 if (verb->get_code() == SP_VERB_NONE) {
542 item = gtk_separator_menu_item_new();
544 } else {
545 unsigned int shortcut;
547 action = verb->get_action(view);
549 if (!action) return NULL;
551 shortcut = sp_shortcut_get_primary(verb);
552 if (shortcut) {
553 gchar c[256];
554 sp_ui_shortcut_string(shortcut, c);
555 GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
556 GtkWidget *const name_lbl = gtk_label_new("");
557 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
558 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
559 gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
560 GtkWidget *const accel_lbl = gtk_label_new(c);
561 gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
562 gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
563 gtk_widget_show_all(hb);
564 if (radio) {
565 item = gtk_radio_menu_item_new (group);
566 } else {
567 item = gtk_image_menu_item_new();
568 }
569 gtk_container_add((GtkContainer *) item, hb);
570 } else {
571 if (radio) {
572 item = gtk_radio_menu_item_new (group);
573 } else {
574 item = gtk_image_menu_item_new ();
575 }
576 GtkWidget *const name_lbl = gtk_label_new("");
577 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
578 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
579 gtk_container_add((GtkContainer *) item, name_lbl);
580 }
582 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
583 if (!action->sensitive) {
584 gtk_widget_set_sensitive(item, FALSE);
585 }
587 if (action->image) {
588 sp_ui_menuitem_add_icon(item, action->image);
589 }
590 gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
591 g_signal_connect( G_OBJECT(item), "activate",
592 G_CALLBACK(sp_ui_menu_activate), action );
594 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
595 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
596 }
598 gtk_widget_show(item);
599 gtk_menu_append(GTK_MENU(menu), item);
601 return item;
603 } // end of sp_ui_menu_append_item_from_verb
606 static void
607 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
608 {
609 gchar const *pref = (gchar const *) user_data;
610 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
612 gchar const *pref_path;
613 if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
614 pref_path = g_strconcat("fullscreen.", pref, NULL);
615 else
616 pref_path = g_strconcat("window.", pref, NULL);
618 gboolean checked = gtk_check_menu_item_get_active(menuitem);
619 prefs_set_int_attribute(pref_path, "state", checked);
621 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
622 }
624 static gboolean
625 checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
626 {
627 GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
629 gchar const *pref = (gchar const *) user_data;
630 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
632 gchar const *pref_path;
633 if ((static_cast<SPDesktop*>(view))->is_fullscreen())
634 pref_path = g_strconcat("fullscreen.", pref, NULL);
635 else
636 pref_path = g_strconcat("window.", pref, NULL);
638 gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
640 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
641 gtk_check_menu_item_set_active(menuitem, ison);
642 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
644 return FALSE;
645 }
648 void
649 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
650 void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
651 gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
652 Inkscape::Verb *verb)
653 {
654 GtkWidget *item;
656 unsigned int shortcut = 0;
657 SPAction *action = NULL;
659 if (verb) {
660 shortcut = sp_shortcut_get_primary(verb);
661 action = verb->get_action(view);
662 }
664 if (verb && shortcut) {
665 gchar c[256];
666 sp_ui_shortcut_string(shortcut, c);
668 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
670 {
671 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
672 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
673 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
674 }
676 {
677 GtkWidget *l = gtk_label_new(c);
678 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
679 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
680 }
682 gtk_widget_show_all(hb);
684 item = gtk_check_menu_item_new();
685 gtk_container_add((GtkContainer *) item, hb);
686 } else {
687 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
688 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
689 item = gtk_check_menu_item_new();
690 gtk_container_add((GtkContainer *) item, l);
691 }
692 #if 0
693 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
694 if (!action->sensitive) {
695 gtk_widget_set_sensitive(item, FALSE);
696 }
697 #endif
698 gtk_widget_show(item);
700 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
702 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
704 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
705 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
707 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
708 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
709 }
711 static void
712 sp_recent_open(GtkWidget *widget, gchar const *uri)
713 {
714 sp_file_open(uri, NULL);
715 }
717 static void
718 sp_file_new_from_template(GtkWidget *widget, gchar const *uri)
719 {
720 sp_file_new(uri);
721 }
723 void
724 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
725 {
726 std::list<gchar *> sources;
727 sources.push_back( profile_path("templates") ); // first try user's local dir
728 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
730 // Use this loop to iterate through a list of possible document locations.
731 while (!sources.empty()) {
732 gchar *dirname = sources.front();
734 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
735 GError *err = 0;
736 GDir *dir = g_dir_open(dirname, 0, &err);
738 if (dir) {
739 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
740 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
741 continue; // skip non-svg files
743 gchar *basename = g_path_get_basename(file);
744 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
745 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
747 gchar const *filepath = g_build_filename(dirname, file, NULL);
748 gchar *dupfile = g_strndup(file, strlen(file) - 4);
749 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
750 g_free(dupfile);
751 GtkWidget *item = gtk_menu_item_new_with_label(filename);
752 g_free(filename);
754 gtk_widget_show(item);
755 // how does "filepath" ever get freed?
756 g_signal_connect(G_OBJECT(item),
757 "activate",
758 G_CALLBACK(sp_file_new_from_template),
759 (gpointer) filepath);
761 if (view) {
762 // set null tip for now; later use a description from the template file
763 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
764 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
765 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
766 }
768 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
769 }
770 g_dir_close(dir);
771 }
772 }
774 // toss the dirname
775 g_free(dirname);
776 sources.pop_front();
777 }
778 }
780 void
781 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
782 {
783 gchar const **recent = prefs_get_recent_files();
784 if (recent) {
785 int i;
787 for (i = 0; recent[i] != NULL; i += 2) {
788 gchar const *uri = recent[i];
789 gchar const *name = recent[i + 1];
791 GtkWidget *item = gtk_menu_item_new_with_label(name);
792 gtk_widget_show(item);
793 g_signal_connect(G_OBJECT(item),
794 "activate",
795 G_CALLBACK(sp_recent_open),
796 (gpointer)uri);
797 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
798 }
800 g_free(recent);
801 } else {
802 GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
803 gtk_widget_show(item);
804 gtk_widget_set_sensitive(item, FALSE);
805 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
806 }
807 }
809 void
810 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
811 {
812 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
813 // checkitem_toggled, checkitem_update, 0);
814 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
815 checkitem_toggled, checkitem_update, 0);
816 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
817 checkitem_toggled, checkitem_update, 0);
818 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
819 checkitem_toggled, checkitem_update, 0);
820 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
821 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
822 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
823 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
824 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
825 checkitem_toggled, checkitem_update, 0);
826 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
827 checkitem_toggled, checkitem_update, 0);
828 }
830 /** \brief This function turns XML into a menu
831 \param menus This is the XML that defines the menu
832 \param menu Menu to be added to
833 \param view The View that this menu is being built for
835 This function is realitively simple as it just goes through the XML
836 and parses the individual elements. In the case of a submenu, it
837 just calls itself recursively. Because it is only reasonable to have
838 a couple of submenus, it is unlikely this will go more than two or
839 three times.
841 In the case of an unreconginzed verb, a menu item is made to identify
842 the verb that is missing, and display that. The menu item is also made
843 insensitive.
844 */
845 void
846 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
847 {
848 if (menus == NULL) return;
849 if (menu == NULL) return;
850 GSList *group = NULL;
852 for (Inkscape::XML::Node *menu_pntr = menus;
853 menu_pntr != NULL;
854 menu_pntr = menu_pntr->next()) {
855 if (!strcmp(menu_pntr->name(), "submenu")) {
856 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
857 GtkWidget *submenu = gtk_menu_new();
858 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
859 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
860 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
861 continue;
862 }
863 if (!strcmp(menu_pntr->name(), "verb")) {
864 gchar const *verb_name = menu_pntr->attribute("verb-id");
865 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
867 if (verb != NULL) {
868 if (menu_pntr->attribute("radio") != NULL) {
869 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
870 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
871 if (menu_pntr->attribute("default") != NULL) {
872 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
873 }
874 } else {
875 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
876 group = NULL;
877 }
878 } else {
879 gchar string[120];
880 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
881 string[119] = '\0'; /* may not be terminated */
882 GtkWidget *item = gtk_menu_item_new_with_label(string);
883 gtk_widget_set_sensitive(item, false);
884 gtk_widget_show(item);
885 gtk_menu_append(GTK_MENU(menu), item);
886 }
887 continue;
888 }
889 if (!strcmp(menu_pntr->name(), "separator")
890 // This was spelt wrong in the original version
891 // and so this is for backward compatibility. It can
892 // probably be dropped after the 0.44 release.
893 || !strcmp(menu_pntr->name(), "seperator")) {
894 GtkWidget *item = gtk_separator_menu_item_new();
895 gtk_widget_show(item);
896 gtk_menu_append(GTK_MENU(menu), item);
897 continue;
898 }
899 if (!strcmp(menu_pntr->name(), "template-list")) {
900 sp_menu_append_new_templates(menu, view);
901 continue;
902 }
903 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
904 sp_menu_append_recent_documents(menu, view);
905 continue;
906 }
907 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
908 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
909 continue;
910 }
911 }
912 }
914 /** \brief Build the main tool bar
915 \param view View to build the bar for
917 Currently the main tool bar is built as a dynamic XML menu using
918 \c sp_ui_build_dyn_menus. This function builds the bar, and then
919 pass it to get items attached to it.
920 */
921 GtkWidget *
922 sp_ui_main_menubar(Inkscape::UI::View::View *view)
923 {
924 GtkWidget *mbar = gtk_menu_bar_new();
926 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
928 return mbar;
929 }
931 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
932 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
933 }
935 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
936 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
937 sp_desktop_selection(desktop)->clear();
938 }
940 GtkWidget *
941 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
942 {
943 GtkWidget *m;
944 SPDesktop *dt;
946 dt = static_cast<SPDesktop*>(view);
948 m = gtk_menu_new();
950 /* Undo and Redo */
951 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
952 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
954 /* Separator */
955 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
957 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
958 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
959 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
961 /* Separator */
962 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
964 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
965 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
967 /* Item menu */
968 if (item) {
969 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
970 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
971 }
973 /* layer menu */
974 SPGroup *group=NULL;
975 if (item) {
976 if (SP_IS_GROUP(item)) {
977 group = SP_GROUP(item);
978 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
979 group = SP_GROUP(SP_OBJECT_PARENT(item));
980 }
981 }
983 if (( group && group != dt->currentLayer() ) ||
984 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
985 /* Separator */
986 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
987 }
989 if ( group && group != dt->currentLayer() ) {
990 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
991 gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
992 GtkWidget *w = gtk_menu_item_new_with_label(label);
993 g_free(label);
994 g_object_set_data(G_OBJECT(w), "group", group);
995 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
996 gtk_widget_show(w);
997 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
998 }
1000 if ( dt->currentLayer() != dt->currentRoot() ) {
1001 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1002 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1003 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1004 gtk_widget_show(w);
1005 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1007 }
1008 }
1010 return m;
1011 }
1013 /* Drag and Drop */
1014 void
1015 sp_ui_drag_data_received(GtkWidget *widget,
1016 GdkDragContext *drag_context,
1017 gint x, gint y,
1018 GtkSelectionData *data,
1019 guint info,
1020 guint event_time,
1021 gpointer user_data)
1022 {
1023 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1024 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1026 switch (info) {
1027 #if ENABLE_MAGIC_COLORS
1028 case APP_X_INKY_COLOR:
1029 {
1030 int destX = 0;
1031 int destY = 0;
1032 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1033 NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1035 SPItem *item = desktop->item_at_point( where, true );
1036 if ( item )
1037 {
1038 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1040 if ( data->length >= 8 ) {
1041 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1043 gchar c[64] = {0};
1044 // Careful about endian issues.
1045 guint16* dataVals = (guint16*)data->data;
1046 sp_svg_write_color( c, sizeof(c),
1047 SP_RGBA32_U_COMPOSE(
1048 0x0ff & (dataVals[0] >> 8),
1049 0x0ff & (dataVals[1] >> 8),
1050 0x0ff & (dataVals[2] >> 8),
1051 0xff // can't have transparency in the color itself
1052 //0x0ff & (data->data[3] >> 8),
1053 ));
1054 SPCSSAttr *css = sp_repr_css_attr_new();
1055 bool updatePerformed = false;
1057 if ( data->length > 14 ) {
1058 int flags = dataVals[4];
1060 // piggie-backed palette entry info
1061 int index = dataVals[5];
1062 Glib::ustring palName;
1063 for ( int i = 0; i < dataVals[6]; i++ ) {
1064 palName += (gunichar)dataVals[7+i];
1065 }
1067 // Now hook in a magic tag of some sort.
1068 if ( !palName.empty() && (flags & 1) ) {
1069 gchar* str = g_strdup_printf("%d|", index);
1070 palName.insert( 0, str );
1071 g_free(str);
1072 str = 0;
1074 sp_object_setAttribute( SP_OBJECT(item),
1075 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1076 palName.c_str(),
1077 false );
1078 item->updateRepr();
1080 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1081 updatePerformed = true;
1082 }
1083 }
1085 if ( !updatePerformed ) {
1086 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1087 }
1089 sp_desktop_apply_css_recursive( item, css, true );
1090 item->updateRepr();
1092 sp_document_done( doc , SP_VERB_NONE,
1093 _("Drop color"));
1095 if ( srgbProf ) {
1096 cmsCloseProfile( srgbProf );
1097 }
1098 }
1099 }
1100 }
1101 break;
1102 #endif // ENABLE_MAGIC_COLORS
1104 case APP_X_COLOR:
1105 {
1106 int destX = 0;
1107 int destY = 0;
1108 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1109 NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1110 NR::Point const button_dt(desktop->w2d(where));
1111 NR::Point const button_doc(desktop->dt2doc(button_dt));
1113 if ( data->length == 8 ) {
1114 gchar c[64] = {0};
1115 // Careful about endian issues.
1116 guint16* dataVals = (guint16*)data->data;
1117 sp_svg_write_color( c, 64,
1118 SP_RGBA32_U_COMPOSE(
1119 0x0ff & (dataVals[0] >> 8),
1120 0x0ff & (dataVals[1] >> 8),
1121 0x0ff & (dataVals[2] >> 8),
1122 0xff // can't have transparency in the color itself
1123 //0x0ff & (data->data[3] >> 8),
1124 ));
1126 SPItem *item = desktop->item_at_point( where, true );
1128 bool consumed = false;
1129 if (desktop->event_context && desktop->event_context->get_drag()) {
1130 consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1131 if (consumed) {
1132 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1133 desktop->event_context->get_drag()->updateDraggers();
1134 }
1135 }
1137 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1138 // consumed = sp_text_context_drop_color(c, button_doc);
1139 // if (consumed) {
1140 // sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1141 // }
1142 //}
1144 if (!consumed && item) {
1145 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1146 if (fillnotstroke &&
1147 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1148 Path *livarot_path = Path_for_item(item, true, true);
1149 livarot_path->ConvertWithBackData(0.04);
1151 NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1152 if (position) {
1153 NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1154 NR::Point delta = nearest - button_doc;
1155 delta = desktop->d2w(delta);
1156 double stroke_tolerance =
1157 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1158 desktop->current_zoom() *
1159 SP_OBJECT_STYLE (item)->stroke_width.computed *
1160 sp_item_i2d_affine (item).expansion() * 0.5
1161 : 0.0)
1162 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
1164 if (NR::L2 (delta) < stroke_tolerance) {
1165 fillnotstroke = false;
1166 }
1167 }
1168 delete livarot_path;
1169 }
1171 SPCSSAttr *css = sp_repr_css_attr_new();
1172 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1174 sp_desktop_apply_css_recursive( item, css, true );
1175 item->updateRepr();
1177 sp_document_done( doc , SP_VERB_NONE,
1178 _("Drop color"));
1179 }
1180 }
1181 }
1182 break;
1184 case SVG_DATA:
1185 case SVG_XML_DATA: {
1186 gchar *svgdata = (gchar *)data->data;
1188 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1190 if (rnewdoc == NULL) {
1191 sp_ui_error_dialog(_("Could not parse SVG data"));
1192 return;
1193 }
1195 Inkscape::XML::Node *repr = rnewdoc->root();
1196 gchar const *style = repr->attribute("style");
1198 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1199 newgroup->setAttribute("style", style);
1201 Inkscape::XML::Document * xml_doc = sp_document_repr_doc(doc);
1202 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1203 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1204 newgroup->appendChild(newchild);
1205 }
1207 Inkscape::GC::release(rnewdoc);
1209 // Add it to the current layer
1211 // Greg's edits to add intelligent positioning of svg drops
1212 SPObject *new_obj = NULL;
1213 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1215 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1216 selection->set(SP_ITEM(new_obj));
1217 // To move the imported object, we must temporarily set the "transform pattern with
1218 // object" option.
1219 {
1220 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1221 prefs_set_int_attribute("options.transform", "pattern", 1);
1222 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1223 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1224 if (sel_bbox) {
1225 NR::Point m( desktop->point() - sel_bbox->midpoint() );
1226 sp_selection_move_relative(selection, m);
1227 }
1228 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1229 }
1231 Inkscape::GC::release(newgroup);
1232 sp_document_done(doc, SP_VERB_NONE,
1233 _("Drop SVG"));
1234 break;
1235 }
1237 case URI_LIST: {
1238 gchar *uri = (gchar *)data->data;
1239 sp_ui_import_files(uri);
1240 break;
1241 }
1243 case PNG_DATA:
1244 case JPEG_DATA:
1245 case IMAGE_DATA: {
1246 char tmp[1024];
1248 StringOutputStream outs;
1249 Base64OutputStream b64out(outs);
1250 b64out.setColumnWidth(0);
1252 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1254 Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1256 for ( int i = 0; i < data->length; i++ ) {
1257 b64out.put( data->data[i] );
1258 }
1259 b64out.close();
1262 Glib::ustring str = outs.getString();
1264 snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1265 str.insert( 0, tmp );
1266 newImage->setAttribute("xlink:href", str.c_str());
1268 GError *error = NULL;
1269 GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1270 if ( loader ) {
1271 error = NULL;
1272 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1273 GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1274 if ( pbuf ) {
1275 int width = gdk_pixbuf_get_width(pbuf);
1276 int height = gdk_pixbuf_get_height(pbuf);
1277 snprintf( tmp, sizeof(tmp), "%d", width );
1278 newImage->setAttribute("width", tmp);
1280 snprintf( tmp, sizeof(tmp), "%d", height );
1281 newImage->setAttribute("height", tmp);
1282 }
1283 }
1284 }
1286 // Add it to the current layer
1287 desktop->currentLayer()->appendChildRepr(newImage);
1289 Inkscape::GC::release(newImage);
1290 sp_document_done( doc , SP_VERB_NONE,
1291 _("Drop bitmap image"));
1292 break;
1293 }
1294 }
1295 }
1297 static void
1298 sp_ui_import_files(gchar *buffer)
1299 {
1300 GList *list = gnome_uri_list_extract_filenames(buffer);
1301 if (!list)
1302 return;
1303 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1304 g_list_foreach(list, (GFunc) g_free, NULL);
1305 g_list_free(list);
1306 }
1308 static void
1309 sp_ui_import_one_file_with_check(gpointer filename, gpointer unused)
1310 {
1311 if (filename) {
1312 if (strlen((char const *)filename) > 2)
1313 sp_ui_import_one_file((char const *)filename);
1314 }
1315 }
1317 static void
1318 sp_ui_import_one_file(char const *filename)
1319 {
1320 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1321 if (!doc) return;
1323 if (filename == NULL) return;
1325 // Pass off to common implementation
1326 // TODO might need to get the proper type of Inkscape::Extension::Extension
1327 file_import( doc, filename, NULL );
1328 }
1330 void
1331 sp_ui_error_dialog(gchar const *message)
1332 {
1333 GtkWidget *dlg;
1334 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1336 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1337 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1338 sp_transientize(dlg);
1339 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1340 gtk_dialog_run(GTK_DIALOG(dlg));
1341 gtk_widget_destroy(dlg);
1342 g_free(safeMsg);
1343 }
1345 bool
1346 sp_ui_overwrite_file(gchar const *filename)
1347 {
1348 bool return_value = FALSE;
1350 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1351 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1352 gchar* baseName = g_path_get_basename( filename );
1353 gchar* dirName = g_path_get_dirname( filename );
1354 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1355 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1356 GTK_MESSAGE_QUESTION,
1357 GTK_BUTTONS_NONE,
1358 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1359 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1360 baseName,
1361 dirName
1362 );
1363 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1364 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1365 _("Replace"), GTK_RESPONSE_YES,
1366 NULL );
1367 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1369 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1370 return_value = TRUE;
1371 } else {
1372 return_value = FALSE;
1373 }
1374 gtk_widget_destroy(dialog);
1375 g_free( baseName );
1376 g_free( dirName );
1377 } else {
1378 return_value = TRUE;
1379 }
1381 return return_value;
1382 }
1384 static void
1385 sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data)
1386 {
1387 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1388 }
1390 static void
1391 sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data)
1392 {
1393 void *child = GTK_BIN (data)->child;
1394 //child is either
1395 //- a GtkHBox, whose first child is a label displaying name if the menu
1396 //item has an accel key
1397 //- a GtkLabel if the menu has no accel key
1398 if (GTK_IS_LABEL(child)) {
1399 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1400 } else if (GTK_IS_HBOX(child)) {
1401 gtk_label_set_markup_with_mnemonic(
1402 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1403 name.c_str());
1404 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1405 //a menu item in yet another way...
1406 }
1409 /*
1410 Local Variables:
1411 mode:c++
1412 c-file-style:"stroustrup"
1413 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1414 indent-tabs-mode:nil
1415 fill-column:99
1416 End:
1417 */
1418 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :