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 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
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 <glib.h>
25 #include "inkscape-private.h"
26 #include "extension/db.h"
27 #include "extension/effect.h"
28 #include "extension/input.h"
29 #include "widgets/icon.h"
30 #include "preferences.h"
31 #include "path-prefix.h"
32 #include "shortcuts.h"
33 #include "document.h"
34 #include "desktop-handles.h"
35 #include "file.h"
36 #include "interface.h"
37 #include "desktop.h"
38 #include "ui/context-menu.h"
39 #include "selection.h"
40 #include "selection-chemistry.h"
41 #include "svg-view-widget.h"
42 #include "widgets/desktop-widget.h"
43 #include "sp-item-group.h"
44 #include "sp-text.h"
45 #include "sp-gradient-fns.h"
46 #include "sp-gradient.h"
47 #include "sp-flowtext.h"
48 #include "sp-namedview.h"
49 #include "ui/view/view.h"
50 #include "helper/action.h"
51 #include "helper/gnome-utils.h"
52 #include "helper/window.h"
53 #include "io/sys.h"
54 #include "dialogs/dialog-events.h"
55 #include "message-context.h"
56 #include "ui/uxmanager.h"
58 // Added for color drag-n-drop
59 #if ENABLE_LCMS
60 #include "lcms.h"
61 #endif // ENABLE_LCMS
62 #include "display/sp-canvas.h"
63 #include "color.h"
64 #include "svg/svg-color.h"
65 #include "desktop-style.h"
66 #include "style.h"
67 #include "event-context.h"
68 #include "gradient-drag.h"
69 #include "widgets/ege-paint-def.h"
71 // Include Mac OS X menu synchronization on native OSX build
72 #ifdef GDK_WINDOWING_QUARTZ
73 #include "ige-mac-menu.h"
74 #endif
76 using Inkscape::DocumentUndo;
78 /* Drag and Drop */
79 typedef enum {
80 URI_LIST,
81 SVG_XML_DATA,
82 SVG_DATA,
83 PNG_DATA,
84 JPEG_DATA,
85 IMAGE_DATA,
86 APP_X_INKY_COLOR,
87 APP_X_COLOR,
88 APP_OSWB_COLOR,
89 } ui_drop_target_info;
91 static GtkTargetEntry ui_drop_target_entries [] = {
92 {(gchar *)"text/uri-list", 0, URI_LIST },
93 {(gchar *)"image/svg+xml", 0, SVG_XML_DATA },
94 {(gchar *)"image/svg", 0, SVG_DATA },
95 {(gchar *)"image/png", 0, PNG_DATA },
96 {(gchar *)"image/jpeg", 0, JPEG_DATA },
97 #if ENABLE_MAGIC_COLORS
98 {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
99 #endif // ENABLE_MAGIC_COLORS
100 {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR },
101 {(gchar *)"application/x-color", 0, APP_X_COLOR }
102 };
104 static GtkTargetEntry *completeDropTargets = 0;
105 static int completeDropTargetsCount = 0;
106 static bool temporarily_block_actions = false;
108 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
109 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
110 static void sp_ui_import_files(gchar *buffer);
111 static void sp_ui_import_one_file(char const *filename);
112 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
113 static void sp_ui_drag_data_received(GtkWidget *widget,
114 GdkDragContext *drag_context,
115 gint x, gint y,
116 GtkSelectionData *data,
117 guint info,
118 guint event_time,
119 gpointer user_data);
120 static void sp_ui_drag_motion( GtkWidget *widget,
121 GdkDragContext *drag_context,
122 gint x, gint y,
123 GtkSelectionData *data,
124 guint info,
125 guint event_time,
126 gpointer user_data );
127 static void sp_ui_drag_leave( GtkWidget *widget,
128 GdkDragContext *drag_context,
129 guint event_time,
130 gpointer user_data );
131 static void sp_ui_menu_item_set_sensitive(SPAction *action,
132 unsigned int sensitive,
133 void *data);
134 static void sp_ui_menu_item_set_name(SPAction *action,
135 Glib::ustring name,
136 void *data);
137 static void sp_recent_open(GtkRecentChooser *, gpointer);
139 static void injectRenamedIcons();
141 SPActionEventVector menu_item_event_vector = {
142 {NULL},
143 NULL,
144 NULL, /* set_active */
145 sp_ui_menu_item_set_sensitive, /* set_sensitive */
146 NULL, /* set_shortcut */
147 sp_ui_menu_item_set_name /* set_name */
148 };
150 static const int MIN_ONSCREEN_DISTANCE = 50;
152 void
153 sp_create_window(SPViewWidget *vw, gboolean editable)
154 {
155 g_return_if_fail(vw != NULL);
156 g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
158 Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
160 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
161 gtk_widget_show(GTK_WIDGET(vw));
163 if (editable) {
164 g_object_set_data(G_OBJECT(vw), "window", win);
166 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
167 SPDesktop* desktop = desktop_widget->desktop;
169 desktop_widget->window = win;
171 win->set_data("desktop", desktop);
172 win->set_data("desktopwidget", desktop_widget);
174 win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
175 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
176 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
178 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
179 gint prefs_geometry =
180 (2==prefs->getInt("/options/savewindowgeometry/value", 0));
181 if (prefs_geometry) {
182 gint pw = prefs->getInt("/desktop/geometry/width", -1);
183 gint ph = prefs->getInt("/desktop/geometry/height", -1);
184 gint px = prefs->getInt("/desktop/geometry/x", -1);
185 gint py = prefs->getInt("/desktop/geometry/y", -1);
186 gint full = prefs->getBool("/desktop/geometry/fullscreen");
187 gint maxed = prefs->getBool("/desktop/geometry/maximized");
188 if (pw>0 && ph>0) {
189 gint w = MIN(gdk_screen_width(), pw);
190 gint h = MIN(gdk_screen_height(), ph);
191 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
192 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
193 if (w>0 && h>0) {
194 x = MIN(gdk_screen_width() - w, x);
195 y = MIN(gdk_screen_height() - h, y);
196 desktop->setWindowSize(w, h);
197 }
199 // Only restore xy for the first window so subsequent windows don't overlap exactly
200 // with first. (Maybe rule should be only restore xy if it's different from xy of
201 // other desktops?)
203 // Empirically it seems that active_desktop==this desktop only the first time a
204 // desktop is created.
205 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
206 if (active_desktop == desktop || active_desktop==NULL) {
207 desktop->setWindowPosition(Geom::Point(x, y));
208 }
209 }
210 if (maxed) {
211 win->maximize();
212 }
213 if (full) {
214 win->fullscreen();
215 }
216 }
218 } else {
219 gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
220 }
222 if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
223 {
224 std::vector<gchar*> types;
226 GSList *list = gdk_pixbuf_get_formats();
227 while ( list ) {
228 int i = 0;
229 GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
230 gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
231 for ( i = 0; typesXX[i]; i++ ) {
232 types.push_back(g_strdup(typesXX[i]));
233 }
234 g_strfreev(typesXX);
236 list = g_slist_next(list);
237 }
238 completeDropTargetsCount = nui_drop_target_entries + types.size();
239 completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
240 for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
241 completeDropTargets[i] = ui_drop_target_entries[i];
242 }
243 int pos = nui_drop_target_entries;
245 for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
246 completeDropTargets[pos].target = *it;
247 completeDropTargets[pos].flags = 0;
248 completeDropTargets[pos].info = IMAGE_DATA;
249 pos++;
250 }
251 }
253 gtk_drag_dest_set((GtkWidget*)win->gobj(),
254 GTK_DEST_DEFAULT_ALL,
255 completeDropTargets,
256 completeDropTargetsCount,
257 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
260 g_signal_connect(G_OBJECT(win->gobj()),
261 "drag_data_received",
262 G_CALLBACK(sp_ui_drag_data_received),
263 NULL);
264 g_signal_connect(G_OBJECT(win->gobj()),
265 "drag_motion",
266 G_CALLBACK(sp_ui_drag_motion),
267 NULL);
268 g_signal_connect(G_OBJECT(win->gobj()),
269 "drag_leave",
270 G_CALLBACK(sp_ui_drag_leave),
271 NULL);
272 win->show();
274 // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
275 inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
276 }
278 void
279 sp_ui_new_view()
280 {
281 SPDocument *document;
282 SPViewWidget *dtw;
284 document = SP_ACTIVE_DOCUMENT;
285 if (!document) return;
287 dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
288 g_return_if_fail(dtw != NULL);
290 sp_create_window(dtw, TRUE);
291 sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
292 sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
293 }
295 /* TODO: not yet working */
296 /* To be re-enabled (by adding to menu) once it works. */
297 void
298 sp_ui_new_view_preview()
299 {
300 SPDocument *document;
301 SPViewWidget *dtw;
303 document = SP_ACTIVE_DOCUMENT;
304 if (!document) return;
306 dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
307 g_return_if_fail(dtw != NULL);
308 sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
310 sp_create_window(dtw, FALSE);
311 }
313 /**
314 * \param widget unused
315 */
316 void
317 sp_ui_close_view(GtkWidget */*widget*/)
318 {
319 SPDesktop *dt = SP_ACTIVE_DESKTOP;
321 if (dt == NULL) {
322 return;
323 }
325 if (dt->shutdown()) {
326 return; // Shutdown operation has been canceled, so do nothing
327 }
329 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
330 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
331 dt->destroyWidget();
332 }
335 /**
336 * sp_ui_close_all
337 *
338 * This function is called to exit the program, and iterates through all
339 * open document view windows, attempting to close each in turn. If the
340 * view has unsaved information, the user will be prompted to save,
341 * discard, or cancel.
342 *
343 * Returns FALSE if the user cancels the close_all operation, TRUE
344 * otherwise.
345 */
346 unsigned int
347 sp_ui_close_all(void)
348 {
349 /* Iterate through all the windows, destroying each in the order they
350 become active */
351 while (SP_ACTIVE_DESKTOP) {
352 SPDesktop *dt = SP_ACTIVE_DESKTOP;
353 if (dt->shutdown()) {
354 /* The user canceled the operation, so end doing the close */
355 return FALSE;
356 }
357 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
358 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
359 dt->destroyWidget();
360 }
362 return TRUE;
363 }
365 /*
366 * Some day when the right-click menus are ready to start working
367 * smarter with the verbs, we'll need to change this NULL being
368 * sent to sp_action_perform to something useful, or set some kind
369 * of global "right-clicked position" variable for actions to
370 * investigate when they're called.
371 */
372 static void
373 sp_ui_menu_activate(void */*object*/, SPAction *action)
374 {
375 if (!temporarily_block_actions) {
376 sp_action_perform(action, NULL);
377 }
378 }
380 static void
381 sp_ui_menu_select_action(void */*object*/, SPAction *action)
382 {
383 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
384 }
386 static void
387 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
388 {
389 action->view->tipsMessageContext()->clear();
390 }
392 static void
393 sp_ui_menu_select(gpointer object, gpointer tip)
394 {
395 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
396 view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
397 }
399 static void
400 sp_ui_menu_deselect(gpointer object)
401 {
402 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
403 view->tipsMessageContext()->clear();
404 }
406 /**
407 * sp_ui_menuitem_add_icon
408 *
409 * Creates and attaches a scaled icon to the given menu item.
410 *
411 */
412 void
413 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
414 {
415 static bool iconsInjected = false;
416 if ( !iconsInjected ) {
417 iconsInjected = true;
418 injectRenamedIcons();
419 }
420 GtkWidget *icon;
422 icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
423 gtk_widget_show(icon);
424 gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
425 } // end of sp_ui_menu_add_icon
427 /**
428 * sp_ui_menu_append_item
429 *
430 * Appends a UI item with specific info for Inkscape/Sodipodi.
431 *
432 */
433 static GtkWidget *
434 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
435 gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
436 gpointer data, gboolean with_mnemonic = TRUE )
437 {
438 GtkWidget *item;
440 if (stock) {
441 item = gtk_image_menu_item_new_from_stock(stock, NULL);
442 } else if (label) {
443 item = (with_mnemonic)
444 ? gtk_image_menu_item_new_with_mnemonic(label) :
445 gtk_image_menu_item_new_with_label(label);
446 } else {
447 item = gtk_separator_menu_item_new();
448 }
450 gtk_widget_show(item);
452 if (callback) {
453 g_signal_connect(G_OBJECT(item), "activate", callback, data);
454 }
456 if (tip && view) {
457 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
458 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
459 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
460 }
462 gtk_menu_append(GTK_MENU(menu), item);
464 return item;
466 } // end of sp_ui_menu_append_item()
468 void
469 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
470 {
471 SPAction *action;
472 unsigned int shortcut;
473 gchar *s;
474 gchar *atitle;
476 action = verb->get_action(NULL);
477 if (!action)
478 return;
480 atitle = sp_action_get_title(action);
482 s = g_stpcpy(c, atitle);
484 g_free(atitle);
486 shortcut = sp_shortcut_get_primary(verb);
487 if (shortcut!=GDK_VoidSymbol) {
488 gchar* key = sp_shortcut_get_label(shortcut);
489 s = g_stpcpy(s, " (");
490 s = g_stpcpy(s, key);
491 s = g_stpcpy(s, ")");
492 g_free(key);
493 }
494 }
497 /**
498 * sp_ui_menu_append_item_from_verb
499 *
500 * Appends a custom menu UI from a verb.
501 *
502 */
504 static GtkWidget *
505 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
506 {
507 SPAction *action;
508 GtkWidget *item;
510 if (verb->get_code() == SP_VERB_NONE) {
512 item = gtk_separator_menu_item_new();
514 } else {
515 unsigned int shortcut;
517 action = verb->get_action(view);
519 if (!action) return NULL;
521 shortcut = sp_shortcut_get_primary(verb);
522 if (shortcut!=GDK_VoidSymbol) {
523 gchar* c = sp_shortcut_get_label(shortcut);
524 GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
525 GtkWidget *const name_lbl = gtk_label_new("");
526 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
527 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
528 gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
529 GtkWidget *const accel_lbl = gtk_label_new(c);
530 gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
531 gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
532 gtk_widget_show_all(hb);
533 if (radio) {
534 item = gtk_radio_menu_item_new (group);
535 } else {
536 item = gtk_image_menu_item_new();
537 }
538 gtk_container_add((GtkContainer *) item, hb);
539 g_free(c);
540 } else {
541 if (radio) {
542 item = gtk_radio_menu_item_new (group);
543 } else {
544 item = gtk_image_menu_item_new ();
545 }
546 GtkWidget *const name_lbl = gtk_label_new("");
547 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
548 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
549 gtk_container_add((GtkContainer *) item, name_lbl);
550 }
552 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
553 if (!action->sensitive) {
554 gtk_widget_set_sensitive(item, FALSE);
555 }
557 if (action->image) {
558 sp_ui_menuitem_add_icon(item, action->image);
559 }
560 gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
561 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
562 g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
563 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
564 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
565 }
567 gtk_widget_show(item);
568 gtk_menu_append(GTK_MENU(menu), item);
570 return item;
572 } // end of sp_ui_menu_append_item_from_verb
575 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
576 {
577 Glib::ustring prefPath;
579 if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
580 prefPath = "/focus/";
581 } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
582 prefPath = "/fullscreen/";
583 } else {
584 prefPath = "/window/";
585 }
587 return prefPath;
588 }
590 static void
591 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
592 {
593 gchar const *pref = (gchar const *) user_data;
594 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
596 Glib::ustring pref_path = getLayoutPrefPath( view );
597 pref_path += pref;
598 pref_path += "/state";
600 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
601 gboolean checked = gtk_check_menu_item_get_active(menuitem);
602 prefs->setBool(pref_path, checked);
604 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
605 }
607 static gboolean
608 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
609 {
610 GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
612 gchar const *pref = (gchar const *) user_data;
613 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
615 Glib::ustring pref_path = getLayoutPrefPath( view );
616 pref_path += pref;
617 pref_path += "/state";
619 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
620 bool ison = prefs->getBool(pref_path, true);
622 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
623 gtk_check_menu_item_set_active(menuitem, ison);
624 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
626 return FALSE;
627 }
629 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
630 {
631 if ( gtk_check_menu_item_get_active(menuitem) ) {
632 gint taskNum = GPOINTER_TO_INT(userData);
633 taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
635 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
637 // note: this will change once more options are in the task set support:
638 Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
639 }
640 }
643 /**
644 * \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
645 */
647 static gboolean
648 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
649 {
650 SPAction *action = (SPAction *) user_data;
651 g_assert(action->id != NULL);
653 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
654 SPDesktop *dt = static_cast<SPDesktop*>(view);
655 Inkscape::RenderMode mode = dt->getMode();
657 bool new_state = false;
658 if (!strcmp(action->id, "ViewModeNormal")) {
659 new_state = mode == Inkscape::RENDERMODE_NORMAL;
660 } else if (!strcmp(action->id, "ViewModeNoFilters")) {
661 new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
662 } else if (!strcmp(action->id, "ViewModeOutline")) {
663 new_state = mode == Inkscape::RENDERMODE_OUTLINE;
664 } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
665 new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
666 } else {
667 g_warning("update_view_menu does not handle this verb");
668 }
670 if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
671 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
672 // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
673 // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
674 // This emission however should not invoke any actions, hence we block it here:
675 temporarily_block_actions = true;
676 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
677 temporarily_block_actions = false;
678 }
679 }
681 return FALSE;
682 }
684 void
685 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
686 void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
687 gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
688 Inkscape::Verb *verb)
689 {
690 unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
691 SPAction *action = (verb) ? verb->get_action(view) : 0;
692 GtkWidget *item = gtk_check_menu_item_new();
694 if (verb && shortcut!=GDK_VoidSymbol) {
695 gchar* c = sp_shortcut_get_label(shortcut);
697 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
699 {
700 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
701 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
702 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
703 }
705 {
706 GtkWidget *l = gtk_label_new(c);
707 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
708 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
709 }
711 gtk_widget_show_all(hb);
713 gtk_container_add((GtkContainer *) item, hb);
714 g_free(c);
715 } else {
716 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
717 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
718 gtk_container_add((GtkContainer *) item, l);
719 }
720 #if 0
721 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
722 if (!action->sensitive) {
723 gtk_widget_set_sensitive(item, FALSE);
724 }
725 #endif
726 gtk_widget_show(item);
728 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
730 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
732 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
733 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
735 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
736 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
737 }
739 static void
740 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
741 {
742 // dealing with the bizarre filename convention in Inkscape for now
743 gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
744 gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
745 gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
746 sp_file_open(utf8_fn, NULL);
747 g_free(utf8_fn);
748 g_free(local_fn);
749 g_free(uri);
750 }
752 static void
753 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
754 {
755 sp_file_new(uri);
756 }
758 void
759 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
760 {
761 std::list<gchar *> sources;
762 sources.push_back( profile_path("templates") ); // first try user's local dir
763 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
765 // Use this loop to iterate through a list of possible document locations.
766 while (!sources.empty()) {
767 gchar *dirname = sources.front();
769 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
770 GError *err = 0;
771 GDir *dir = g_dir_open(dirname, 0, &err);
773 if (dir) {
774 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
775 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
776 continue; // skip non-svg files
778 gchar *basename = g_path_get_basename(file);
779 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
780 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
782 gchar const *filepath = g_build_filename(dirname, file, NULL);
783 gchar *dupfile = g_strndup(file, strlen(file) - 4);
784 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
785 g_free(dupfile);
786 GtkWidget *item = gtk_menu_item_new_with_label(filename);
787 g_free(filename);
789 gtk_widget_show(item);
790 // how does "filepath" ever get freed?
791 g_signal_connect(G_OBJECT(item),
792 "activate",
793 G_CALLBACK(sp_file_new_from_template),
794 (gpointer) filepath);
796 if (view) {
797 // set null tip for now; later use a description from the template file
798 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
799 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
800 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
801 }
803 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
804 }
805 g_dir_close(dir);
806 }
807 }
809 // toss the dirname
810 g_free(dirname);
811 sources.pop_front();
812 }
813 }
815 void
816 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
817 {
818 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
819 // checkitem_toggled, checkitem_update, 0);
820 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
821 checkitem_toggled, checkitem_update, 0);
822 sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
823 checkitem_toggled, checkitem_update, 0);
824 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
825 checkitem_toggled, checkitem_update, 0);
826 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
827 checkitem_toggled, checkitem_update, 0);
828 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
829 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
830 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
831 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
832 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
833 checkitem_toggled, checkitem_update, 0);
834 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
835 checkitem_toggled, checkitem_update, 0);
836 }
839 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
840 {
841 gchar const* data[] = {
842 _("Default"), _("Default interface setup"),
843 _("Custom"), _("Set the custom task"),
844 _("Wide"), _("Setup for widescreen work"),
845 0, 0
846 };
848 GSList *group = 0;
849 int count = 0;
850 gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
851 for (gchar const **strs = data; strs[0]; strs += 2, count++)
852 {
853 GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
854 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
855 if ( count == active )
856 {
857 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
858 }
860 g_object_set_data( G_OBJECT(item), "view", view );
861 g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
862 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
863 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
865 gtk_widget_show( item );
866 gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
867 }
868 }
871 /** @brief Observer that updates the recent list's max document count */
872 class MaxRecentObserver : public Inkscape::Preferences::Observer {
873 public:
874 MaxRecentObserver(GtkWidget *recent_menu) :
875 Observer("/options/maxrecentdocuments/value"),
876 _rm(recent_menu)
877 {}
878 virtual void notify(Inkscape::Preferences::Entry const &e) {
879 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
880 // hack: the recent menu doesn't repopulate after changing the limit, so we force it
881 g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
882 }
883 private:
884 GtkWidget *_rm;
885 };
887 /** \brief This function turns XML into a menu
888 \param menus This is the XML that defines the menu
889 \param menu Menu to be added to
890 \param view The View that this menu is being built for
892 This function is realitively simple as it just goes through the XML
893 and parses the individual elements. In the case of a submenu, it
894 just calls itself recursively. Because it is only reasonable to have
895 a couple of submenus, it is unlikely this will go more than two or
896 three times.
898 In the case of an unrecognized verb, a menu item is made to identify
899 the verb that is missing, and display that. The menu item is also made
900 insensitive.
901 */
902 void
903 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
904 {
905 if (menus == NULL) return;
906 if (menu == NULL) return;
907 GSList *group = NULL;
909 for (Inkscape::XML::Node *menu_pntr = menus;
910 menu_pntr != NULL;
911 menu_pntr = menu_pntr->next()) {
912 if (!strcmp(menu_pntr->name(), "submenu")) {
913 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
914 GtkWidget *submenu = gtk_menu_new();
915 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
916 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
917 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
918 continue;
919 }
920 if (!strcmp(menu_pntr->name(), "verb")) {
921 gchar const *verb_name = menu_pntr->attribute("verb-id");
922 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
924 if (verb != NULL) {
925 if (menu_pntr->attribute("radio") != NULL) {
926 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
927 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
928 if (menu_pntr->attribute("default") != NULL) {
929 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
930 }
931 if (verb->get_code() != SP_VERB_NONE) {
932 SPAction *action = verb->get_action(view);
933 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
934 }
935 } else {
936 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
937 group = NULL;
938 }
939 } else {
940 gchar string[120];
941 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
942 string[119] = '\0'; /* may not be terminated */
943 GtkWidget *item = gtk_menu_item_new_with_label(string);
944 gtk_widget_set_sensitive(item, false);
945 gtk_widget_show(item);
946 gtk_menu_append(GTK_MENU(menu), item);
947 }
948 continue;
949 }
950 if (!strcmp(menu_pntr->name(), "separator")
951 // This was spelt wrong in the original version
952 // and so this is for backward compatibility. It can
953 // probably be dropped after the 0.44 release.
954 || !strcmp(menu_pntr->name(), "seperator")) {
955 GtkWidget *item = gtk_separator_menu_item_new();
956 gtk_widget_show(item);
957 gtk_menu_append(GTK_MENU(menu), item);
958 continue;
959 }
960 if (!strcmp(menu_pntr->name(), "template-list")) {
961 sp_menu_append_new_templates(menu, view);
962 continue;
963 }
964 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
965 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
967 // create recent files menu
968 int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
969 GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
970 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
971 // sort most recently used documents first to preserve previous behavior
972 gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
973 g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
975 // add filter to only open files added by Inkscape
976 GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
977 gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
978 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
980 gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
981 gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
983 GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
984 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
986 gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
987 // this will just sit and update the list's item count
988 static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
989 prefs->addObserver(*mro);
990 continue;
991 }
992 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
993 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
994 continue;
995 }
996 if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
997 addTaskMenuItems(GTK_MENU(menu), view);
998 continue;
999 }
1000 }
1001 }
1003 /** \brief Build the main tool bar
1004 \param view View to build the bar for
1006 Currently the main tool bar is built as a dynamic XML menu using
1007 \c sp_ui_build_dyn_menus. This function builds the bar, and then
1008 pass it to get items attached to it.
1009 */
1010 GtkWidget *
1011 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1012 {
1013 GtkWidget *mbar = gtk_menu_bar_new();
1015 #ifdef GDK_WINDOWING_QUARTZ
1016 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1017 #endif
1019 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1021 #ifdef GDK_WINDOWING_QUARTZ
1022 return NULL;
1023 #else
1024 return mbar;
1025 #endif
1026 }
1028 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1029 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1030 }
1032 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1033 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1034 sp_desktop_selection(desktop)->clear();
1035 }
1037 GtkWidget *
1038 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1039 {
1040 GtkWidget *m;
1041 SPDesktop *dt;
1043 dt = static_cast<SPDesktop*>(view);
1045 m = gtk_menu_new();
1047 /* Undo and Redo */
1048 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1049 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1051 /* Separator */
1052 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1054 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1055 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1056 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1058 /* Separator */
1059 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1061 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1062 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1064 /* Item menu */
1065 if (item) {
1066 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1067 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1068 }
1070 /* layer menu */
1071 SPGroup *group=NULL;
1072 if (item) {
1073 if (SP_IS_GROUP(item)) {
1074 group = SP_GROUP(item);
1075 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1076 group = SP_GROUP(SP_OBJECT_PARENT(item));
1077 }
1078 }
1080 if (( group && group != dt->currentLayer() ) ||
1081 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1082 /* Separator */
1083 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1084 }
1086 if ( group && group != dt->currentLayer() ) {
1087 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1088 gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1089 GtkWidget *w = gtk_menu_item_new_with_label(label);
1090 g_free(label);
1091 g_object_set_data(G_OBJECT(w), "group", group);
1092 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1093 gtk_widget_show(w);
1094 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1095 }
1097 if ( dt->currentLayer() != dt->currentRoot() ) {
1098 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1099 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1100 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1101 gtk_widget_show(w);
1102 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1104 }
1105 }
1107 return m;
1108 }
1110 /* Drag and Drop */
1111 void
1112 sp_ui_drag_data_received(GtkWidget *widget,
1113 GdkDragContext *drag_context,
1114 gint x, gint y,
1115 GtkSelectionData *data,
1116 guint info,
1117 guint /*event_time*/,
1118 gpointer /*user_data*/)
1119 {
1120 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1121 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1123 switch (info) {
1124 #if ENABLE_MAGIC_COLORS
1125 case APP_X_INKY_COLOR:
1126 {
1127 int destX = 0;
1128 int destY = 0;
1129 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1130 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1132 SPItem *item = desktop->getItemAtPoint( where, true );
1133 if ( item )
1134 {
1135 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1137 if ( data->length >= 8 ) {
1138 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1140 gchar c[64] = {0};
1141 // Careful about endian issues.
1142 guint16* dataVals = (guint16*)data->data;
1143 sp_svg_write_color( c, sizeof(c),
1144 SP_RGBA32_U_COMPOSE(
1145 0x0ff & (dataVals[0] >> 8),
1146 0x0ff & (dataVals[1] >> 8),
1147 0x0ff & (dataVals[2] >> 8),
1148 0xff // can't have transparency in the color itself
1149 //0x0ff & (data->data[3] >> 8),
1150 ));
1151 SPCSSAttr *css = sp_repr_css_attr_new();
1152 bool updatePerformed = false;
1154 if ( data->length > 14 ) {
1155 int flags = dataVals[4];
1157 // piggie-backed palette entry info
1158 int index = dataVals[5];
1159 Glib::ustring palName;
1160 for ( int i = 0; i < dataVals[6]; i++ ) {
1161 palName += (gunichar)dataVals[7+i];
1162 }
1164 // Now hook in a magic tag of some sort.
1165 if ( !palName.empty() && (flags & 1) ) {
1166 gchar* str = g_strdup_printf("%d|", index);
1167 palName.insert( 0, str );
1168 g_free(str);
1169 str = 0;
1171 SP_OBJECT(item)->setAttribute(
1172 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1173 palName.c_str(),
1174 false );
1175 item->updateRepr();
1177 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1178 updatePerformed = true;
1179 }
1180 }
1182 if ( !updatePerformed ) {
1183 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1184 }
1186 sp_desktop_apply_css_recursive( item, css, true );
1187 item->updateRepr();
1189 SPDocumentUndo::done( doc , SP_VERB_NONE,
1190 _("Drop color"));
1192 if ( srgbProf ) {
1193 cmsCloseProfile( srgbProf );
1194 }
1195 }
1196 }
1197 }
1198 break;
1199 #endif // ENABLE_MAGIC_COLORS
1201 case APP_X_COLOR:
1202 {
1203 int destX = 0;
1204 int destY = 0;
1205 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1206 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1207 Geom::Point const button_dt(desktop->w2d(where));
1208 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1210 if ( data->length == 8 ) {
1211 gchar colorspec[64] = {0};
1212 // Careful about endian issues.
1213 guint16* dataVals = (guint16*)data->data;
1214 sp_svg_write_color( colorspec, sizeof(colorspec),
1215 SP_RGBA32_U_COMPOSE(
1216 0x0ff & (dataVals[0] >> 8),
1217 0x0ff & (dataVals[1] >> 8),
1218 0x0ff & (dataVals[2] >> 8),
1219 0xff // can't have transparency in the color itself
1220 //0x0ff & (data->data[3] >> 8),
1221 ));
1223 SPItem *item = desktop->getItemAtPoint( where, true );
1225 bool consumed = false;
1226 if (desktop->event_context && desktop->event_context->get_drag()) {
1227 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1228 if (consumed) {
1229 DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1230 desktop->event_context->get_drag()->updateDraggers();
1231 }
1232 }
1234 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1235 // consumed = sp_text_context_drop_color(c, button_doc);
1236 // if (consumed) {
1237 // SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1238 // }
1239 //}
1241 if (!consumed && item) {
1242 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1243 if (fillnotstroke &&
1244 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1245 Path *livarot_path = Path_for_item(item, true, true);
1246 livarot_path->ConvertWithBackData(0.04);
1248 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1249 if (position) {
1250 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1251 Geom::Point delta = nearest - button_doc;
1252 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1253 delta = desktop->d2w(delta);
1254 double stroke_tolerance =
1255 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1256 desktop->current_zoom() *
1257 SP_OBJECT_STYLE (item)->stroke_width.computed *
1258 to_2geom(item->i2d_affine()).descrim() * 0.5
1259 : 0.0)
1260 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1262 if (Geom::L2 (delta) < stroke_tolerance) {
1263 fillnotstroke = false;
1264 }
1265 }
1266 delete livarot_path;
1267 }
1269 SPCSSAttr *css = sp_repr_css_attr_new();
1270 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1272 sp_desktop_apply_css_recursive( item, css, true );
1273 item->updateRepr();
1275 DocumentUndo::done( doc , SP_VERB_NONE,
1276 _("Drop color") );
1277 }
1278 }
1279 }
1280 break;
1282 case APP_OSWB_COLOR:
1283 {
1284 bool worked = false;
1285 Glib::ustring colorspec;
1286 if ( data->format == 8 ) {
1287 ege::PaintDef color;
1288 worked = color.fromMIMEData("application/x-oswb-color",
1289 reinterpret_cast<char*>(data->data),
1290 data->length,
1291 data->format);
1292 if ( worked ) {
1293 if ( color.getType() == ege::PaintDef::CLEAR ) {
1294 colorspec = ""; // TODO check if this is sufficient
1295 } else if ( color.getType() == ege::PaintDef::NONE ) {
1296 colorspec = "none";
1297 } else {
1298 unsigned int r = color.getR();
1299 unsigned int g = color.getG();
1300 unsigned int b = color.getB();
1302 SPGradient* matches = 0;
1303 const GSList *gradients = doc->getResourceList("gradient");
1304 for (const GSList *item = gradients; item; item = item->next) {
1305 SPGradient* grad = SP_GRADIENT(item->data);
1306 if ( color.descr == grad->getId() ) {
1307 if ( grad->hasStops() ) {
1308 matches = grad;
1309 break;
1310 }
1311 }
1312 }
1313 if (matches) {
1314 colorspec = "url(#";
1315 colorspec += matches->getId();
1316 colorspec += ")";
1317 } else {
1318 gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1319 colorspec = tmp;
1320 g_free(tmp);
1321 }
1322 }
1323 }
1324 }
1325 if ( worked ) {
1326 int destX = 0;
1327 int destY = 0;
1328 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1329 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1330 Geom::Point const button_dt(desktop->w2d(where));
1331 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1333 SPItem *item = desktop->getItemAtPoint( where, true );
1335 bool consumed = false;
1336 if (desktop->event_context && desktop->event_context->get_drag()) {
1337 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1338 if (consumed) {
1339 DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1340 desktop->event_context->get_drag()->updateDraggers();
1341 }
1342 }
1344 if (!consumed && item) {
1345 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1346 if (fillnotstroke &&
1347 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1348 Path *livarot_path = Path_for_item(item, true, true);
1349 livarot_path->ConvertWithBackData(0.04);
1351 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1352 if (position) {
1353 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1354 Geom::Point delta = nearest - button_doc;
1355 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1356 delta = desktop->d2w(delta);
1357 double stroke_tolerance =
1358 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1359 desktop->current_zoom() *
1360 SP_OBJECT_STYLE (item)->stroke_width.computed *
1361 to_2geom(item->i2d_affine()).descrim() * 0.5
1362 : 0.0)
1363 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1365 if (Geom::L2 (delta) < stroke_tolerance) {
1366 fillnotstroke = false;
1367 }
1368 }
1369 delete livarot_path;
1370 }
1372 SPCSSAttr *css = sp_repr_css_attr_new();
1373 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1375 sp_desktop_apply_css_recursive( item, css, true );
1376 item->updateRepr();
1378 DocumentUndo::done( doc , SP_VERB_NONE,
1379 _("Drop color") );
1380 }
1381 }
1382 }
1383 break;
1385 case SVG_DATA:
1386 case SVG_XML_DATA: {
1387 gchar *svgdata = (gchar *)data->data;
1389 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1391 if (rnewdoc == NULL) {
1392 sp_ui_error_dialog(_("Could not parse SVG data"));
1393 return;
1394 }
1396 Inkscape::XML::Node *repr = rnewdoc->root();
1397 gchar const *style = repr->attribute("style");
1399 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1400 newgroup->setAttribute("style", style);
1402 Inkscape::XML::Document * xml_doc = doc->getReprDoc();
1403 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1404 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1405 newgroup->appendChild(newchild);
1406 }
1408 Inkscape::GC::release(rnewdoc);
1410 // Add it to the current layer
1412 // Greg's edits to add intelligent positioning of svg drops
1413 SPObject *new_obj = NULL;
1414 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1416 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1417 selection->set(SP_ITEM(new_obj));
1419 // move to mouse pointer
1420 {
1421 sp_desktop_document(desktop)->ensureUpToDate();
1422 Geom::OptRect sel_bbox = selection->bounds();
1423 if (sel_bbox) {
1424 Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1425 sp_selection_move_relative(selection, m, false);
1426 }
1427 }
1429 Inkscape::GC::release(newgroup);
1430 DocumentUndo::done( doc, SP_VERB_NONE,
1431 _("Drop SVG") );
1432 break;
1433 }
1435 case URI_LIST: {
1436 gchar *uri = (gchar *)data->data;
1437 sp_ui_import_files(uri);
1438 break;
1439 }
1441 case PNG_DATA:
1442 case JPEG_DATA:
1443 case IMAGE_DATA: {
1444 const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1446 Inkscape::Extension::DB::InputList o;
1447 Inkscape::Extension::db.get_input_list(o);
1448 Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1449 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1450 ++i;
1451 }
1452 Inkscape::Extension::Extension *ext = *i;
1453 bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1454 ext->set_param_optiongroup("link", "embed");
1455 ext->set_gui(false);
1457 gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1458 g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1459 file_import(doc, filename, ext);
1460 g_free(filename);
1462 ext->set_param_optiongroup("link", save ? "embed" : "link");
1463 ext->set_gui(true);
1464 DocumentUndo::done( doc , SP_VERB_NONE,
1465 _("Drop bitmap image") );
1466 break;
1467 }
1468 }
1469 }
1471 #include "gradient-context.h"
1473 void sp_ui_drag_motion( GtkWidget */*widget*/,
1474 GdkDragContext */*drag_context*/,
1475 gint /*x*/, gint /*y*/,
1476 GtkSelectionData */*data*/,
1477 guint /*info*/,
1478 guint /*event_time*/,
1479 gpointer /*user_data*/)
1480 {
1481 // SPDocument *doc = SP_ACTIVE_DOCUMENT;
1482 // SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1485 // g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
1486 }
1488 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1489 GdkDragContext */*drag_context*/,
1490 guint /*event_time*/,
1491 gpointer /*user_data*/ )
1492 {
1493 // g_message("drag-n-drop leave at %d", event_time);
1494 }
1496 static void
1497 sp_ui_import_files(gchar *buffer)
1498 {
1499 GList *list = gnome_uri_list_extract_filenames(buffer);
1500 if (!list)
1501 return;
1502 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1503 g_list_foreach(list, (GFunc) g_free, NULL);
1504 g_list_free(list);
1505 }
1507 static void
1508 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1509 {
1510 if (filename) {
1511 if (strlen((char const *)filename) > 2)
1512 sp_ui_import_one_file((char const *)filename);
1513 }
1514 }
1516 static void
1517 sp_ui_import_one_file(char const *filename)
1518 {
1519 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1520 if (!doc) return;
1522 if (filename == NULL) return;
1524 // Pass off to common implementation
1525 // TODO might need to get the proper type of Inkscape::Extension::Extension
1526 file_import( doc, filename, NULL );
1527 }
1529 void
1530 sp_ui_error_dialog(gchar const *message)
1531 {
1532 GtkWidget *dlg;
1533 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1535 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1536 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1537 sp_transientize(dlg);
1538 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1539 gtk_dialog_run(GTK_DIALOG(dlg));
1540 gtk_widget_destroy(dlg);
1541 g_free(safeMsg);
1542 }
1544 bool
1545 sp_ui_overwrite_file(gchar const *filename)
1546 {
1547 bool return_value = FALSE;
1549 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1550 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1551 gchar* baseName = g_path_get_basename( filename );
1552 gchar* dirName = g_path_get_dirname( filename );
1553 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1554 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1555 GTK_MESSAGE_QUESTION,
1556 GTK_BUTTONS_NONE,
1557 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1558 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1559 baseName,
1560 dirName
1561 );
1562 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1563 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1564 _("Replace"), GTK_RESPONSE_YES,
1565 NULL );
1566 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1568 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1569 return_value = TRUE;
1570 } else {
1571 return_value = FALSE;
1572 }
1573 gtk_widget_destroy(dialog);
1574 g_free( baseName );
1575 g_free( dirName );
1576 } else {
1577 return_value = TRUE;
1578 }
1580 return return_value;
1581 }
1583 static void
1584 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1585 {
1586 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1587 }
1589 static void
1590 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1591 {
1592 void *child = GTK_BIN (data)->child;
1593 //child is either
1594 //- a GtkHBox, whose first child is a label displaying name if the menu
1595 //item has an accel key
1596 //- a GtkLabel if the menu has no accel key
1597 if (GTK_IS_LABEL(child)) {
1598 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1599 } else if (GTK_IS_HBOX(child)) {
1600 gtk_label_set_markup_with_mnemonic(
1601 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1602 name.c_str());
1603 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1604 //a menu item in yet another way...
1605 }
1607 void injectRenamedIcons()
1608 {
1609 Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
1611 std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed;
1612 renamed.push_back(std::make_pair("gtk-file", "document-x-generic"));
1613 renamed.push_back(std::make_pair("gtk-directory", "folder"));
1615 for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) {
1616 bool hasIcon = iconTheme->has_icon(it->first);
1617 bool hasSecondIcon = iconTheme->has_icon(it->second);
1619 if ( !hasIcon && hasSecondIcon ) {
1620 Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second);
1621 for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
1622 Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
1623 if ( pb ) {
1624 // install a private copy of the pixbuf to avoid pinning a theme
1625 Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
1626 Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
1627 }
1628 }
1629 }
1630 }
1631 }
1634 /*
1635 Local Variables:
1636 mode:c++
1637 c-file-style:"stroustrup"
1638 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1639 indent-tabs-mode:nil
1640 fill-column:99
1641 End:
1642 */
1643 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :