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