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