1 /** @file
2 * @brief Main UI stuff
3 */
4 /* Authors:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * Frank Felfe <innerspace@iname.com>
7 * bulia byak <buliabyak@users.sf.net>
8 *
9 * Copyright (C) 1999-2005 authors
10 * Copyright (C) 2001-2002 Ximian, Inc.
11 * Copyright (C) 2004 David Turner
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <gtk/gtk.h>
21 #include <glib.h>
23 #include "inkscape-private.h"
24 #include "extension/db.h"
25 #include "extension/effect.h"
26 #include "extension/input.h"
27 #include "widgets/icon.h"
28 #include "preferences.h"
29 #include "path-prefix.h"
30 #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-gradient-fns.h"
44 #include "sp-gradient.h"
45 #include "sp-flowtext.h"
46 #include "sp-namedview.h"
47 #include "ui/view/view.h"
48 #include "helper/action.h"
49 #include "helper/gnome-utils.h"
50 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "dialogs/dialog-events.h"
53 #include "message-context.h"
54 #include "ui/uxmanager.h"
56 // Added for color drag-n-drop
57 #if ENABLE_LCMS
58 #include "lcms.h"
59 #endif // ENABLE_LCMS
60 #include "display/sp-canvas.h"
61 #include "color.h"
62 #include "svg/svg-color.h"
63 #include "desktop-style.h"
64 #include "style.h"
65 #include "event-context.h"
66 #include "gradient-drag.h"
67 #include "widgets/ege-paint-def.h"
69 // Include Mac OS X menu synchronization on native OSX build
70 #ifdef GDK_WINDOWING_QUARTZ
71 #include "ige-mac-menu.h"
72 #endif
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 APP_OSWB_COLOR,
85 } ui_drop_target_info;
87 static GtkTargetEntry ui_drop_target_entries [] = {
88 {(gchar *)"text/uri-list", 0, URI_LIST },
89 {(gchar *)"image/svg+xml", 0, SVG_XML_DATA },
90 {(gchar *)"image/svg", 0, SVG_DATA },
91 {(gchar *)"image/png", 0, PNG_DATA },
92 {(gchar *)"image/jpeg", 0, JPEG_DATA },
93 #if ENABLE_MAGIC_COLORS
94 {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
95 #endif // ENABLE_MAGIC_COLORS
96 {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR },
97 {(gchar *)"application/x-color", 0, APP_X_COLOR }
98 };
100 static GtkTargetEntry *completeDropTargets = 0;
101 static int completeDropTargetsCount = 0;
102 static bool temporarily_block_actions = false;
104 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
105 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
106 static void sp_ui_import_files(gchar *buffer);
107 static void sp_ui_import_one_file(char const *filename);
108 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
109 static void sp_ui_drag_data_received(GtkWidget *widget,
110 GdkDragContext *drag_context,
111 gint x, gint y,
112 GtkSelectionData *data,
113 guint info,
114 guint event_time,
115 gpointer user_data);
116 static void sp_ui_drag_motion( GtkWidget *widget,
117 GdkDragContext *drag_context,
118 gint x, gint y,
119 GtkSelectionData *data,
120 guint info,
121 guint event_time,
122 gpointer user_data );
123 static void sp_ui_drag_leave( GtkWidget *widget,
124 GdkDragContext *drag_context,
125 guint event_time,
126 gpointer user_data );
127 static void sp_ui_menu_item_set_sensitive(SPAction *action,
128 unsigned int sensitive,
129 void *data);
130 static void sp_ui_menu_item_set_name(SPAction *action,
131 Glib::ustring name,
132 void *data);
133 static void sp_recent_open(GtkRecentChooser *, gpointer);
135 static void injectRenamedIcons();
137 SPActionEventVector menu_item_event_vector = {
138 {NULL},
139 NULL,
140 NULL, /* set_active */
141 sp_ui_menu_item_set_sensitive, /* set_sensitive */
142 NULL, /* set_shortcut */
143 sp_ui_menu_item_set_name /* set_name */
144 };
146 static const int MIN_ONSCREEN_DISTANCE = 50;
148 void
149 sp_create_window(SPViewWidget *vw, gboolean editable)
150 {
151 g_return_if_fail(vw != NULL);
152 g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
154 Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
156 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
157 gtk_widget_show(GTK_WIDGET(vw));
159 if (editable) {
160 g_object_set_data(G_OBJECT(vw), "window", win);
162 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
163 SPDesktop* desktop = desktop_widget->desktop;
165 desktop_widget->window = win;
167 win->set_data("desktop", desktop);
168 win->set_data("desktopwidget", desktop_widget);
170 win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
171 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
172 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
174 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
175 gint prefs_geometry =
176 (2==prefs->getInt("/options/savewindowgeometry/value", 0));
177 if (prefs_geometry) {
178 gint pw = prefs->getInt("/desktop/geometry/width", -1);
179 gint ph = prefs->getInt("/desktop/geometry/height", -1);
180 gint px = prefs->getInt("/desktop/geometry/x", -1);
181 gint py = prefs->getInt("/desktop/geometry/y", -1);
182 gint full = prefs->getBool("/desktop/geometry/fullscreen");
183 gint maxed = prefs->getBool("/desktop/geometry/maximized");
184 if (pw>0 && ph>0) {
185 gint w = MIN(gdk_screen_width(), pw);
186 gint h = MIN(gdk_screen_height(), ph);
187 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
188 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
189 if (w>0 && h>0) {
190 x = MIN(gdk_screen_width() - w, x);
191 y = MIN(gdk_screen_height() - h, y);
192 desktop->setWindowSize(w, h);
193 }
195 // Only restore xy for the first window so subsequent windows don't overlap exactly
196 // with first. (Maybe rule should be only restore xy if it's different from xy of
197 // other desktops?)
199 // Empirically it seems that active_desktop==this desktop only the first time a
200 // desktop is created.
201 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
202 if (active_desktop == desktop || active_desktop==NULL) {
203 desktop->setWindowPosition(Geom::Point(x, y));
204 }
205 }
206 if (maxed) {
207 win->maximize();
208 }
209 if (full) {
210 win->fullscreen();
211 }
212 }
214 } else {
215 gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
216 }
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 SPDesktop *dt = SP_ACTIVE_DESKTOP;
317 if (dt == NULL) {
318 return;
319 }
321 if (dt->shutdown()) {
322 return; // Shutdown operation has been canceled, so do nothing
323 }
325 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
326 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
327 dt->destroyWidget();
328 }
331 /**
332 * sp_ui_close_all
333 *
334 * This function is called to exit the program, and iterates through all
335 * open document view windows, attempting to close each in turn. If the
336 * view has unsaved information, the user will be prompted to save,
337 * discard, or cancel.
338 *
339 * Returns FALSE if the user cancels the close_all operation, TRUE
340 * otherwise.
341 */
342 unsigned int
343 sp_ui_close_all(void)
344 {
345 /* Iterate through all the windows, destroying each in the order they
346 become active */
347 while (SP_ACTIVE_DESKTOP) {
348 SPDesktop *dt = SP_ACTIVE_DESKTOP;
349 if (dt->shutdown()) {
350 /* The user canceled the operation, so end doing the close */
351 return FALSE;
352 }
353 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
354 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
355 dt->destroyWidget();
356 }
358 return TRUE;
359 }
361 /*
362 * Some day when the right-click menus are ready to start working
363 * smarter with the verbs, we'll need to change this NULL being
364 * sent to sp_action_perform to something useful, or set some kind
365 * of global "right-clicked position" variable for actions to
366 * investigate when they're called.
367 */
368 static void
369 sp_ui_menu_activate(void */*object*/, SPAction *action)
370 {
371 if (!temporarily_block_actions) {
372 sp_action_perform(action, NULL);
373 }
374 }
376 static void
377 sp_ui_menu_select_action(void */*object*/, SPAction *action)
378 {
379 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
380 }
382 static void
383 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
384 {
385 action->view->tipsMessageContext()->clear();
386 }
388 static void
389 sp_ui_menu_select(gpointer object, gpointer tip)
390 {
391 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
392 view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
393 }
395 static void
396 sp_ui_menu_deselect(gpointer object)
397 {
398 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
399 view->tipsMessageContext()->clear();
400 }
402 /**
403 * sp_ui_menuitem_add_icon
404 *
405 * Creates and attaches a scaled icon to the given menu item.
406 *
407 */
408 void
409 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
410 {
411 static bool iconsInjected = false;
412 if ( !iconsInjected ) {
413 iconsInjected = true;
414 injectRenamedIcons();
415 }
416 GtkWidget *icon;
418 icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
419 gtk_widget_show(icon);
420 gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
421 } // end of sp_ui_menu_add_icon
423 /**
424 * sp_ui_menu_append_item
425 *
426 * Appends a UI item with specific info for Inkscape/Sodipodi.
427 *
428 */
429 static GtkWidget *
430 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
431 gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
432 gpointer data, gboolean with_mnemonic = TRUE )
433 {
434 GtkWidget *item;
436 if (stock) {
437 item = gtk_image_menu_item_new_from_stock(stock, NULL);
438 } else if (label) {
439 item = (with_mnemonic)
440 ? gtk_image_menu_item_new_with_mnemonic(label) :
441 gtk_image_menu_item_new_with_label(label);
442 } else {
443 item = gtk_separator_menu_item_new();
444 }
446 gtk_widget_show(item);
448 if (callback) {
449 g_signal_connect(G_OBJECT(item), "activate", callback, data);
450 }
452 if (tip && view) {
453 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
454 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
455 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
456 }
458 gtk_menu_append(GTK_MENU(menu), item);
460 return item;
462 } // end of sp_ui_menu_append_item()
464 void
465 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
466 {
467 SPAction *action;
468 unsigned int shortcut;
469 gchar *s;
470 gchar *atitle;
472 action = verb->get_action(NULL);
473 if (!action)
474 return;
476 atitle = sp_action_get_title(action);
478 s = g_stpcpy(c, atitle);
480 g_free(atitle);
482 shortcut = sp_shortcut_get_primary(verb);
483 if (shortcut!=GDK_VoidSymbol) {
484 gchar* key = sp_shortcut_get_label(shortcut);
485 s = g_stpcpy(s, " (");
486 s = g_stpcpy(s, key);
487 s = g_stpcpy(s, ")");
488 g_free(key);
489 }
490 }
493 /**
494 * sp_ui_menu_append_item_from_verb
495 *
496 * Appends a custom menu UI from a verb.
497 *
498 */
500 static GtkWidget *
501 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
502 {
503 SPAction *action;
504 GtkWidget *item;
506 if (verb->get_code() == SP_VERB_NONE) {
508 item = gtk_separator_menu_item_new();
510 } else {
511 unsigned int shortcut;
513 action = verb->get_action(view);
515 if (!action) return NULL;
517 shortcut = sp_shortcut_get_primary(verb);
518 if (shortcut!=GDK_VoidSymbol) {
519 gchar* c = sp_shortcut_get_label(shortcut);
520 GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
521 GtkWidget *const name_lbl = gtk_label_new("");
522 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
523 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
524 gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
525 GtkWidget *const accel_lbl = gtk_label_new(c);
526 gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
527 gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
528 gtk_widget_show_all(hb);
529 if (radio) {
530 item = gtk_radio_menu_item_new (group);
531 } else {
532 item = gtk_image_menu_item_new();
533 }
534 gtk_container_add((GtkContainer *) item, hb);
535 g_free(c);
536 } else {
537 if (radio) {
538 item = gtk_radio_menu_item_new (group);
539 } else {
540 item = gtk_image_menu_item_new ();
541 }
542 GtkWidget *const name_lbl = gtk_label_new("");
543 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
544 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
545 gtk_container_add((GtkContainer *) item, name_lbl);
546 }
548 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
549 if (!action->sensitive) {
550 gtk_widget_set_sensitive(item, FALSE);
551 }
553 if (action->image) {
554 sp_ui_menuitem_add_icon(item, action->image);
555 }
556 gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
557 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
558 g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
559 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
560 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
561 }
563 gtk_widget_show(item);
564 gtk_menu_append(GTK_MENU(menu), item);
566 return item;
568 } // end of sp_ui_menu_append_item_from_verb
571 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
572 {
573 Glib::ustring prefPath;
575 if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
576 prefPath = "/focus/";
577 } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
578 prefPath = "/fullscreen/";
579 } else {
580 prefPath = "/window/";
581 }
583 return prefPath;
584 }
586 static void
587 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
588 {
589 gchar const *pref = (gchar const *) user_data;
590 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
592 Glib::ustring pref_path = getLayoutPrefPath( view );
593 pref_path += pref;
594 pref_path += "/state";
596 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
597 gboolean checked = gtk_check_menu_item_get_active(menuitem);
598 prefs->setBool(pref_path, checked);
600 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
601 }
603 static gboolean
604 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
605 {
606 GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
608 gchar const *pref = (gchar const *) user_data;
609 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
611 Glib::ustring pref_path = getLayoutPrefPath( view );
612 pref_path += pref;
613 pref_path += "/state";
615 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
616 bool ison = prefs->getBool(pref_path, true);
618 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
619 gtk_check_menu_item_set_active(menuitem, ison);
620 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
622 return FALSE;
623 }
625 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
626 {
627 if ( gtk_check_menu_item_get_active(menuitem) ) {
628 gint taskNum = GPOINTER_TO_INT(userData);
629 taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
631 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
633 // note: this will change once more options are in the task set support:
634 Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
635 }
636 }
639 /**
640 * \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
641 */
643 static gboolean
644 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
645 {
646 SPAction *action = (SPAction *) user_data;
647 g_assert(action->id != NULL);
649 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
650 SPDesktop *dt = static_cast<SPDesktop*>(view);
651 Inkscape::RenderMode mode = dt->getMode();
653 bool new_state = false;
654 if (!strcmp(action->id, "ViewModeNormal")) {
655 new_state = mode == Inkscape::RENDERMODE_NORMAL;
656 } else if (!strcmp(action->id, "ViewModeNoFilters")) {
657 new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
658 } else if (!strcmp(action->id, "ViewModeOutline")) {
659 new_state = mode == Inkscape::RENDERMODE_OUTLINE;
660 } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
661 new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
662 } else {
663 g_warning("update_view_menu does not handle this verb");
664 }
666 if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
667 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
668 // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
669 // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
670 // This emission however should not invoke any actions, hence we block it here:
671 temporarily_block_actions = true;
672 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
673 temporarily_block_actions = false;
674 }
675 }
677 return FALSE;
678 }
680 void
681 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
682 void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
683 gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
684 Inkscape::Verb *verb)
685 {
686 unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
687 SPAction *action = (verb) ? verb->get_action(view) : 0;
688 GtkWidget *item = gtk_check_menu_item_new();
690 if (verb && shortcut!=GDK_VoidSymbol) {
691 gchar* c = sp_shortcut_get_label(shortcut);
693 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
695 {
696 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
697 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
698 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
699 }
701 {
702 GtkWidget *l = gtk_label_new(c);
703 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
704 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
705 }
707 gtk_widget_show_all(hb);
709 gtk_container_add((GtkContainer *) item, hb);
710 g_free(c);
711 } else {
712 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
713 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
714 gtk_container_add((GtkContainer *) item, l);
715 }
716 #if 0
717 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
718 if (!action->sensitive) {
719 gtk_widget_set_sensitive(item, FALSE);
720 }
721 #endif
722 gtk_widget_show(item);
724 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
726 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
728 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
729 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
731 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
732 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
733 }
735 static void
736 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
737 {
738 // dealing with the bizarre filename convention in Inkscape for now
739 gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
740 gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
741 gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
742 sp_file_open(utf8_fn, NULL);
743 g_free(utf8_fn);
744 g_free(local_fn);
745 g_free(uri);
746 }
748 static void
749 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
750 {
751 sp_file_new(uri);
752 }
754 void
755 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
756 {
757 std::list<gchar *> sources;
758 sources.push_back( profile_path("templates") ); // first try user's local dir
759 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
761 // Use this loop to iterate through a list of possible document locations.
762 while (!sources.empty()) {
763 gchar *dirname = sources.front();
765 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
766 GError *err = 0;
767 GDir *dir = g_dir_open(dirname, 0, &err);
769 if (dir) {
770 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
771 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
772 continue; // skip non-svg files
774 gchar *basename = g_path_get_basename(file);
775 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
776 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
778 gchar const *filepath = g_build_filename(dirname, file, NULL);
779 gchar *dupfile = g_strndup(file, strlen(file) - 4);
780 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
781 g_free(dupfile);
782 GtkWidget *item = gtk_menu_item_new_with_label(filename);
783 g_free(filename);
785 gtk_widget_show(item);
786 // how does "filepath" ever get freed?
787 g_signal_connect(G_OBJECT(item),
788 "activate",
789 G_CALLBACK(sp_file_new_from_template),
790 (gpointer) filepath);
792 if (view) {
793 // set null tip for now; later use a description from the template file
794 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
795 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
796 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
797 }
799 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
800 }
801 g_dir_close(dir);
802 }
803 }
805 // toss the dirname
806 g_free(dirname);
807 sources.pop_front();
808 }
809 }
811 void
812 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
813 {
814 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
815 // checkitem_toggled, checkitem_update, 0);
816 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
817 checkitem_toggled, checkitem_update, 0);
818 sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
819 checkitem_toggled, checkitem_update, 0);
820 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
821 checkitem_toggled, checkitem_update, 0);
822 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
823 checkitem_toggled, checkitem_update, 0);
824 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
825 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
826 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
827 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
828 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
829 checkitem_toggled, checkitem_update, 0);
830 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
831 checkitem_toggled, checkitem_update, 0);
832 }
835 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
836 {
837 gchar const* data[] = {
838 _("Default"), _("Default interface setup"),
839 _("Custom"), _("Set the custom task"),
840 _("Wide"), _("Setup for widescreen work"),
841 0, 0
842 };
844 GSList *group = 0;
845 int count = 0;
846 gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
847 for (gchar const **strs = data; strs[0]; strs += 2, count++)
848 {
849 GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
850 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
851 if ( count == active )
852 {
853 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
854 }
856 g_object_set_data( G_OBJECT(item), "view", view );
857 g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
858 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
859 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
861 gtk_widget_show( item );
862 gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
863 }
864 }
867 /** @brief Observer that updates the recent list's max document count */
868 class MaxRecentObserver : public Inkscape::Preferences::Observer {
869 public:
870 MaxRecentObserver(GtkWidget *recent_menu) :
871 Observer("/options/maxrecentdocuments/value"),
872 _rm(recent_menu)
873 {}
874 virtual void notify(Inkscape::Preferences::Entry const &e) {
875 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
876 // hack: the recent menu doesn't repopulate after changing the limit, so we force it
877 g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
878 }
879 private:
880 GtkWidget *_rm;
881 };
883 /** \brief This function turns XML into a menu
884 \param menus This is the XML that defines the menu
885 \param menu Menu to be added to
886 \param view The View that this menu is being built for
888 This function is realitively simple as it just goes through the XML
889 and parses the individual elements. In the case of a submenu, it
890 just calls itself recursively. Because it is only reasonable to have
891 a couple of submenus, it is unlikely this will go more than two or
892 three times.
894 In the case of an unrecognized verb, a menu item is made to identify
895 the verb that is missing, and display that. The menu item is also made
896 insensitive.
897 */
898 void
899 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
900 {
901 if (menus == NULL) return;
902 if (menu == NULL) return;
903 GSList *group = NULL;
905 for (Inkscape::XML::Node *menu_pntr = menus;
906 menu_pntr != NULL;
907 menu_pntr = menu_pntr->next()) {
908 if (!strcmp(menu_pntr->name(), "submenu")) {
909 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
910 GtkWidget *submenu = gtk_menu_new();
911 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
912 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
913 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
914 continue;
915 }
916 if (!strcmp(menu_pntr->name(), "verb")) {
917 gchar const *verb_name = menu_pntr->attribute("verb-id");
918 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
920 if (verb != NULL) {
921 if (menu_pntr->attribute("radio") != NULL) {
922 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
923 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
924 if (menu_pntr->attribute("default") != NULL) {
925 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
926 }
927 if (verb->get_code() != SP_VERB_NONE) {
928 SPAction *action = verb->get_action(view);
929 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
930 }
931 } else {
932 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
933 group = NULL;
934 }
935 } else {
936 gchar string[120];
937 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
938 string[119] = '\0'; /* may not be terminated */
939 GtkWidget *item = gtk_menu_item_new_with_label(string);
940 gtk_widget_set_sensitive(item, false);
941 gtk_widget_show(item);
942 gtk_menu_append(GTK_MENU(menu), item);
943 }
944 continue;
945 }
946 if (!strcmp(menu_pntr->name(), "separator")
947 // This was spelt wrong in the original version
948 // and so this is for backward compatibility. It can
949 // probably be dropped after the 0.44 release.
950 || !strcmp(menu_pntr->name(), "seperator")) {
951 GtkWidget *item = gtk_separator_menu_item_new();
952 gtk_widget_show(item);
953 gtk_menu_append(GTK_MENU(menu), item);
954 continue;
955 }
956 if (!strcmp(menu_pntr->name(), "template-list")) {
957 sp_menu_append_new_templates(menu, view);
958 continue;
959 }
960 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
961 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
963 // create recent files menu
964 int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
965 GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
966 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
967 // sort most recently used documents first to preserve previous behavior
968 gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
969 g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
971 // add filter to only open files added by Inkscape
972 GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
973 gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
974 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
976 gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
977 gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
979 GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
980 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
982 gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
983 // this will just sit and update the list's item count
984 static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
985 prefs->addObserver(*mro);
986 continue;
987 }
988 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
989 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
990 continue;
991 }
992 if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
993 addTaskMenuItems(GTK_MENU(menu), view);
994 continue;
995 }
996 }
997 }
999 /** \brief Build the main tool bar
1000 \param view View to build the bar for
1002 Currently the main tool bar is built as a dynamic XML menu using
1003 \c sp_ui_build_dyn_menus. This function builds the bar, and then
1004 pass it to get items attached to it.
1005 */
1006 GtkWidget *
1007 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1008 {
1009 GtkWidget *mbar = gtk_menu_bar_new();
1011 #ifdef GDK_WINDOWING_QUARTZ
1012 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1013 #endif
1015 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1017 #ifdef GDK_WINDOWING_QUARTZ
1018 return NULL;
1019 #else
1020 return mbar;
1021 #endif
1022 }
1024 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1025 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1026 }
1028 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1029 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1030 sp_desktop_selection(desktop)->clear();
1031 }
1033 GtkWidget *
1034 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1035 {
1036 GtkWidget *m;
1037 SPDesktop *dt;
1039 dt = static_cast<SPDesktop*>(view);
1041 m = gtk_menu_new();
1043 /* Undo and Redo */
1044 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1045 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1047 /* Separator */
1048 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1050 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1051 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1052 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1054 /* Separator */
1055 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1057 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1058 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1060 /* Item menu */
1061 if (item) {
1062 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1063 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1064 }
1066 /* layer menu */
1067 SPGroup *group=NULL;
1068 if (item) {
1069 if (SP_IS_GROUP(item)) {
1070 group = SP_GROUP(item);
1071 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1072 group = SP_GROUP(SP_OBJECT_PARENT(item));
1073 }
1074 }
1076 if (( group && group != dt->currentLayer() ) ||
1077 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1078 /* Separator */
1079 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1080 }
1082 if ( group && group != dt->currentLayer() ) {
1083 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1084 gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1085 GtkWidget *w = gtk_menu_item_new_with_label(label);
1086 g_free(label);
1087 g_object_set_data(G_OBJECT(w), "group", group);
1088 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1089 gtk_widget_show(w);
1090 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1091 }
1093 if ( dt->currentLayer() != dt->currentRoot() ) {
1094 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1095 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1096 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1097 gtk_widget_show(w);
1098 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1100 }
1101 }
1103 return m;
1104 }
1106 /* Drag and Drop */
1107 void
1108 sp_ui_drag_data_received(GtkWidget *widget,
1109 GdkDragContext *drag_context,
1110 gint x, gint y,
1111 GtkSelectionData *data,
1112 guint info,
1113 guint /*event_time*/,
1114 gpointer /*user_data*/)
1115 {
1116 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1117 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1119 switch (info) {
1120 #if ENABLE_MAGIC_COLORS
1121 case APP_X_INKY_COLOR:
1122 {
1123 int destX = 0;
1124 int destY = 0;
1125 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1126 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1128 SPItem *item = desktop->item_at_point( where, true );
1129 if ( item )
1130 {
1131 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1133 if ( data->length >= 8 ) {
1134 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1136 gchar c[64] = {0};
1137 // Careful about endian issues.
1138 guint16* dataVals = (guint16*)data->data;
1139 sp_svg_write_color( c, sizeof(c),
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 ));
1147 SPCSSAttr *css = sp_repr_css_attr_new();
1148 bool updatePerformed = false;
1150 if ( data->length > 14 ) {
1151 int flags = dataVals[4];
1153 // piggie-backed palette entry info
1154 int index = dataVals[5];
1155 Glib::ustring palName;
1156 for ( int i = 0; i < dataVals[6]; i++ ) {
1157 palName += (gunichar)dataVals[7+i];
1158 }
1160 // Now hook in a magic tag of some sort.
1161 if ( !palName.empty() && (flags & 1) ) {
1162 gchar* str = g_strdup_printf("%d|", index);
1163 palName.insert( 0, str );
1164 g_free(str);
1165 str = 0;
1167 sp_object_setAttribute( SP_OBJECT(item),
1168 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1169 palName.c_str(),
1170 false );
1171 item->updateRepr();
1173 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1174 updatePerformed = true;
1175 }
1176 }
1178 if ( !updatePerformed ) {
1179 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1180 }
1182 sp_desktop_apply_css_recursive( item, css, true );
1183 item->updateRepr();
1185 sp_document_done( doc , SP_VERB_NONE,
1186 _("Drop color"));
1188 if ( srgbProf ) {
1189 cmsCloseProfile( srgbProf );
1190 }
1191 }
1192 }
1193 }
1194 break;
1195 #endif // ENABLE_MAGIC_COLORS
1197 case APP_X_COLOR:
1198 {
1199 int destX = 0;
1200 int destY = 0;
1201 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1202 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1203 Geom::Point const button_dt(desktop->w2d(where));
1204 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1206 if ( data->length == 8 ) {
1207 gchar colorspec[64] = {0};
1208 // Careful about endian issues.
1209 guint16* dataVals = (guint16*)data->data;
1210 sp_svg_write_color( colorspec, sizeof(colorspec),
1211 SP_RGBA32_U_COMPOSE(
1212 0x0ff & (dataVals[0] >> 8),
1213 0x0ff & (dataVals[1] >> 8),
1214 0x0ff & (dataVals[2] >> 8),
1215 0xff // can't have transparency in the color itself
1216 //0x0ff & (data->data[3] >> 8),
1217 ));
1219 SPItem *item = desktop->item_at_point( where, true );
1221 bool consumed = false;
1222 if (desktop->event_context && desktop->event_context->get_drag()) {
1223 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1224 if (consumed) {
1225 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1226 desktop->event_context->get_drag()->updateDraggers();
1227 }
1228 }
1230 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1231 // consumed = sp_text_context_drop_color(c, button_doc);
1232 // if (consumed) {
1233 // sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1234 // }
1235 //}
1237 if (!consumed && item) {
1238 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1239 if (fillnotstroke &&
1240 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1241 Path *livarot_path = Path_for_item(item, true, true);
1242 livarot_path->ConvertWithBackData(0.04);
1244 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1245 if (position) {
1246 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1247 Geom::Point delta = nearest - button_doc;
1248 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1249 delta = desktop->d2w(delta);
1250 double stroke_tolerance =
1251 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1252 desktop->current_zoom() *
1253 SP_OBJECT_STYLE (item)->stroke_width.computed *
1254 to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1255 : 0.0)
1256 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1258 if (Geom::L2 (delta) < stroke_tolerance) {
1259 fillnotstroke = false;
1260 }
1261 }
1262 delete livarot_path;
1263 }
1265 SPCSSAttr *css = sp_repr_css_attr_new();
1266 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1268 sp_desktop_apply_css_recursive( item, css, true );
1269 item->updateRepr();
1271 sp_document_done( doc , SP_VERB_NONE,
1272 _("Drop color"));
1273 }
1274 }
1275 }
1276 break;
1278 case APP_OSWB_COLOR:
1279 {
1280 bool worked = false;
1281 Glib::ustring colorspec;
1282 if ( data->format == 8 ) {
1283 ege::PaintDef color;
1284 worked = color.fromMIMEData("application/x-oswb-color",
1285 reinterpret_cast<char*>(data->data),
1286 data->length,
1287 data->format);
1288 if ( worked ) {
1289 if ( color.getType() == ege::PaintDef::CLEAR ) {
1290 colorspec = ""; // TODO check if this is sufficient
1291 } else if ( color.getType() == ege::PaintDef::NONE ) {
1292 colorspec = "none";
1293 } else {
1294 unsigned int r = color.getR();
1295 unsigned int g = color.getG();
1296 unsigned int b = color.getB();
1298 SPGradient* matches = 0;
1299 const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
1300 for (const GSList *item = gradients; item; item = item->next) {
1301 SPGradient* grad = SP_GRADIENT(item->data);
1302 if ( color.descr == grad->getId() ) {
1303 if ( grad->hasStops() ) {
1304 matches = grad;
1305 break;
1306 }
1307 }
1308 }
1309 if (matches) {
1310 colorspec = "url(#";
1311 colorspec += matches->getId();
1312 colorspec += ")";
1313 } else {
1314 gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1315 colorspec = tmp;
1316 g_free(tmp);
1317 }
1318 }
1319 }
1320 }
1321 if ( worked ) {
1322 int destX = 0;
1323 int destY = 0;
1324 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1325 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1326 Geom::Point const button_dt(desktop->w2d(where));
1327 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1329 SPItem *item = desktop->item_at_point( where, true );
1331 bool consumed = false;
1332 if (desktop->event_context && desktop->event_context->get_drag()) {
1333 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1334 if (consumed) {
1335 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1336 desktop->event_context->get_drag()->updateDraggers();
1337 }
1338 }
1340 if (!consumed && item) {
1341 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1342 if (fillnotstroke &&
1343 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1344 Path *livarot_path = Path_for_item(item, true, true);
1345 livarot_path->ConvertWithBackData(0.04);
1347 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1348 if (position) {
1349 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1350 Geom::Point delta = nearest - button_doc;
1351 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1352 delta = desktop->d2w(delta);
1353 double stroke_tolerance =
1354 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1355 desktop->current_zoom() *
1356 SP_OBJECT_STYLE (item)->stroke_width.computed *
1357 to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1358 : 0.0)
1359 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1361 if (Geom::L2 (delta) < stroke_tolerance) {
1362 fillnotstroke = false;
1363 }
1364 }
1365 delete livarot_path;
1366 }
1368 SPCSSAttr *css = sp_repr_css_attr_new();
1369 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1371 sp_desktop_apply_css_recursive( item, css, true );
1372 item->updateRepr();
1374 sp_document_done( doc , SP_VERB_NONE,
1375 _("Drop color"));
1376 }
1377 }
1378 }
1379 break;
1381 case SVG_DATA:
1382 case SVG_XML_DATA: {
1383 gchar *svgdata = (gchar *)data->data;
1385 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1387 if (rnewdoc == NULL) {
1388 sp_ui_error_dialog(_("Could not parse SVG data"));
1389 return;
1390 }
1392 Inkscape::XML::Node *repr = rnewdoc->root();
1393 gchar const *style = repr->attribute("style");
1395 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1396 newgroup->setAttribute("style", style);
1398 Inkscape::XML::Document * xml_doc = sp_document_repr_doc(doc);
1399 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1400 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1401 newgroup->appendChild(newchild);
1402 }
1404 Inkscape::GC::release(rnewdoc);
1406 // Add it to the current layer
1408 // Greg's edits to add intelligent positioning of svg drops
1409 SPObject *new_obj = NULL;
1410 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1412 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1413 selection->set(SP_ITEM(new_obj));
1415 // move to mouse pointer
1416 {
1417 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1418 Geom::OptRect sel_bbox = selection->bounds();
1419 if (sel_bbox) {
1420 Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1421 sp_selection_move_relative(selection, m, false);
1422 }
1423 }
1425 Inkscape::GC::release(newgroup);
1426 sp_document_done(doc, SP_VERB_NONE,
1427 _("Drop SVG"));
1428 break;
1429 }
1431 case URI_LIST: {
1432 gchar *uri = (gchar *)data->data;
1433 sp_ui_import_files(uri);
1434 break;
1435 }
1437 case PNG_DATA:
1438 case JPEG_DATA:
1439 case IMAGE_DATA: {
1440 const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1442 Inkscape::Extension::DB::InputList o;
1443 Inkscape::Extension::db.get_input_list(o);
1444 Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1445 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1446 ++i;
1447 }
1448 Inkscape::Extension::Extension *ext = *i;
1449 bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1450 ext->set_param_optiongroup("link", "embed");
1451 ext->set_gui(false);
1453 gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1454 g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1455 file_import(doc, filename, ext);
1456 g_free(filename);
1458 ext->set_param_optiongroup("link", save ? "embed" : "link");
1459 ext->set_gui(true);
1460 sp_document_done( doc , SP_VERB_NONE,
1461 _("Drop bitmap image"));
1462 break;
1463 }
1464 }
1465 }
1467 #include "gradient-context.h"
1469 void sp_ui_drag_motion( GtkWidget */*widget*/,
1470 GdkDragContext */*drag_context*/,
1471 gint /*x*/, gint /*y*/,
1472 GtkSelectionData */*data*/,
1473 guint /*info*/,
1474 guint /*event_time*/,
1475 gpointer /*user_data*/)
1476 {
1477 // SPDocument *doc = SP_ACTIVE_DOCUMENT;
1478 // SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1481 // g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
1482 }
1484 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1485 GdkDragContext */*drag_context*/,
1486 guint /*event_time*/,
1487 gpointer /*user_data*/ )
1488 {
1489 // g_message("drag-n-drop leave at %d", event_time);
1490 }
1492 static void
1493 sp_ui_import_files(gchar *buffer)
1494 {
1495 GList *list = gnome_uri_list_extract_filenames(buffer);
1496 if (!list)
1497 return;
1498 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1499 g_list_foreach(list, (GFunc) g_free, NULL);
1500 g_list_free(list);
1501 }
1503 static void
1504 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1505 {
1506 if (filename) {
1507 if (strlen((char const *)filename) > 2)
1508 sp_ui_import_one_file((char const *)filename);
1509 }
1510 }
1512 static void
1513 sp_ui_import_one_file(char const *filename)
1514 {
1515 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1516 if (!doc) return;
1518 if (filename == NULL) return;
1520 // Pass off to common implementation
1521 // TODO might need to get the proper type of Inkscape::Extension::Extension
1522 file_import( doc, filename, NULL );
1523 }
1525 void
1526 sp_ui_error_dialog(gchar const *message)
1527 {
1528 GtkWidget *dlg;
1529 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1531 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1532 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1533 sp_transientize(dlg);
1534 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1535 gtk_dialog_run(GTK_DIALOG(dlg));
1536 gtk_widget_destroy(dlg);
1537 g_free(safeMsg);
1538 }
1540 bool
1541 sp_ui_overwrite_file(gchar const *filename)
1542 {
1543 bool return_value = FALSE;
1545 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1546 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1547 gchar* baseName = g_path_get_basename( filename );
1548 gchar* dirName = g_path_get_dirname( filename );
1549 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1550 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1551 GTK_MESSAGE_QUESTION,
1552 GTK_BUTTONS_NONE,
1553 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1554 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1555 baseName,
1556 dirName
1557 );
1558 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1559 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1560 _("Replace"), GTK_RESPONSE_YES,
1561 NULL );
1562 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1564 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1565 return_value = TRUE;
1566 } else {
1567 return_value = FALSE;
1568 }
1569 gtk_widget_destroy(dialog);
1570 g_free( baseName );
1571 g_free( dirName );
1572 } else {
1573 return_value = TRUE;
1574 }
1576 return return_value;
1577 }
1579 static void
1580 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1581 {
1582 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1583 }
1585 static void
1586 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1587 {
1588 void *child = GTK_BIN (data)->child;
1589 //child is either
1590 //- a GtkHBox, whose first child is a label displaying name if the menu
1591 //item has an accel key
1592 //- a GtkLabel if the menu has no accel key
1593 if (GTK_IS_LABEL(child)) {
1594 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1595 } else if (GTK_IS_HBOX(child)) {
1596 gtk_label_set_markup_with_mnemonic(
1597 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1598 name.c_str());
1599 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1600 //a menu item in yet another way...
1601 }
1603 void injectRenamedIcons()
1604 {
1605 Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
1607 std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed;
1608 renamed.push_back(std::make_pair("gtk-file", "document-x-generic"));
1609 renamed.push_back(std::make_pair("gtk-directory", "folder"));
1611 for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) {
1612 bool hasIcon = iconTheme->has_icon(it->first);
1613 bool hasSecondIcon = iconTheme->has_icon(it->second);
1615 if ( !hasIcon && hasSecondIcon ) {
1616 Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second);
1617 for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
1618 Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
1619 if ( pb ) {
1620 // install a private copy of the pixbuf to avoid pinning a theme
1621 Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
1622 Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
1623 }
1624 }
1625 }
1626 }
1627 }
1630 /*
1631 Local Variables:
1632 mode:c++
1633 c-file-style:"stroustrup"
1634 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1635 indent-tabs-mode:nil
1636 fill-column:99
1637 End:
1638 */
1639 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :