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