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