591ea62531ed0add2689ab3aa119bf080f2c7059
1 #define __SP_DESKTOP_WIDGET_C__
3 /** \file
4 * Desktop widget implementation
5 */
6 /* Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2007 Johan Engelen
15 * Copyright (C) 2006 John Bintz
16 * Copyright (C) 2004 MenTaLguY
17 * Copyright (C) 1999-2002 Lauris Kaplinski
18 * Copyright (C) 2000-2001 Ximian, Inc.
19 *
20 * Released under GNU GPL, read the file 'COPYING' for more information
21 */
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <gtkmm/paned.h>
28 #include <gtk/gtk.h>
30 #include "box3d-context.h"
31 #include "color-profile-fns.h"
32 #include "conn-avoid-ref.h"
33 #include "desktop-events.h"
34 #include "desktop-handles.h"
35 #include "desktop-widget.h"
36 #include "display/canvas-arena.h"
37 #include "display/nr-arena.h"
38 #include "document.h"
39 #include "ege-color-prof-tracker.h"
40 #include "ege-select-one-action.h"
41 #include <extension/db.h>
42 #include "file.h"
43 #include "helper/units.h"
44 #include "inkscape-private.h"
45 #include "interface.h"
46 #include "macros.h"
47 #include "preferences.h"
48 #include "sp-image.h"
49 #include "sp-item.h"
50 #include "sp-namedview.h"
51 #include "ui/dialog/dialog-manager.h"
52 #include "ui/dialog/swatches.h"
53 #include "ui/icon-names.h"
54 #include "ui/widget/dock.h"
55 #include "ui/widget/layer-selector.h"
56 #include "ui/widget/selected-style.h"
57 #include "ui/uxmanager.h"
59 // We're in the "widgets" directory, so no need to explicitly prefix these:
60 #include "button.h"
61 #include "ruler.h"
62 #include "spinbutton-events.h"
63 #include "spw-utilities.h"
64 #include "toolbox.h"
65 #include "widget-sizes.h"
67 #if defined (SOLARIS) && (SOLARIS == 8)
68 #include "round.h"
69 using Inkscape::round;
70 #endif
73 using Inkscape::UI::UXManager;
74 using Inkscape::UI::ToolboxFactory;
76 #ifdef WITH_INKBOARD
77 #endif
81 enum {
82 ACTIVATE,
83 DEACTIVATE,
84 MODIFIED,
85 EVENT_CONTEXT_CHANGED,
86 LAST_SIGNAL
87 };
90 //---------------------------------------------------------------------
91 /* SPDesktopWidget */
93 static void sp_desktop_widget_class_init (SPDesktopWidgetClass *klass);
94 static void sp_desktop_widget_destroy (GtkObject *object);
96 static void sp_desktop_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
97 static void sp_desktop_widget_realize (GtkWidget *widget);
99 static gint sp_desktop_widget_event (GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw);
101 static void sp_dtw_color_profile_event(EgeColorProfTracker *widget, SPDesktopWidget *dtw);
102 static void cms_adjust_toggled( GtkWidget *button, gpointer data );
103 static void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled );
104 static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw);
106 static gdouble sp_dtw_zoom_value_to_display (gdouble value);
107 static gdouble sp_dtw_zoom_display_to_value (gdouble value);
108 static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data);
109 static bool sp_dtw_zoom_output (GtkSpinButton *spin, gpointer data);
110 static void sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data);
111 static void sp_dtw_zoom_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data);
112 static void sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor);
113 static void sp_dtw_zoom_50 (GtkMenuItem *item, gpointer data);
114 static void sp_dtw_zoom_100 (GtkMenuItem *item, gpointer data);
115 static void sp_dtw_zoom_200 (GtkMenuItem *item, gpointer data);
116 static void sp_dtw_zoom_page (GtkMenuItem *item, gpointer data);
117 static void sp_dtw_zoom_drawing (GtkMenuItem *item, gpointer data);
118 static void sp_dtw_zoom_selection (GtkMenuItem *item, gpointer data);
119 static void sp_dtw_sticky_zoom_toggled (GtkMenuItem *item, gpointer data);
121 SPViewWidgetClass *dtw_parent_class;
123 class CMSPrefWatcher {
124 public:
125 CMSPrefWatcher() :
126 _dpw(*this),
127 _spw(*this),
128 _tracker(ege_color_prof_tracker_new(0))
129 {
130 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
131 g_signal_connect( G_OBJECT(_tracker), "modified", G_CALLBACK(hook), this );
132 prefs->addObserver(_dpw);
133 prefs->addObserver(_spw);
134 }
135 virtual ~CMSPrefWatcher() {}
137 //virtual void notify(PrefValue &);
138 void add( SPDesktopWidget* dtw ) {
139 _widget_list.push_back(dtw);
140 }
141 void remove( SPDesktopWidget* dtw ) {
142 _widget_list.remove(dtw);
143 }
145 private:
146 static void hook(EgeColorProfTracker *tracker, gint a, gint b, CMSPrefWatcher *watcher);
148 class DisplayProfileWatcher : public Inkscape::Preferences::Observer {
149 public:
150 DisplayProfileWatcher(CMSPrefWatcher &pw) : Observer("/options/displayprofile"), _pw(pw) {}
151 virtual void notify(Inkscape::Preferences::Entry const &/*val*/) {
152 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
153 _pw._setCmsSensitive(!prefs->getString("/options/displayprofile/uri").empty());
154 _pw._refreshAll();
155 }
156 private:
157 CMSPrefWatcher &_pw;
158 };
160 DisplayProfileWatcher _dpw;
162 class SoftProofWatcher : public Inkscape::Preferences::Observer {
163 public:
164 SoftProofWatcher(CMSPrefWatcher &pw) : Observer("/options/softproof"), _pw(pw) {}
165 virtual void notify(Inkscape::Preferences::Entry const &) {
166 _pw._refreshAll();
167 }
168 private:
169 CMSPrefWatcher &_pw;
170 };
172 SoftProofWatcher _spw;
174 void _refreshAll();
175 void _setCmsSensitive(bool value);
177 std::list<SPDesktopWidget*> _widget_list;
178 EgeColorProfTracker *_tracker;
180 friend class DisplayProfileWatcher;
181 friend class SoftproofWatcher;
182 };
184 void CMSPrefWatcher::hook(EgeColorProfTracker */*tracker*/, gint screen, gint monitor, CMSPrefWatcher */*watcher*/)
185 {
186 #if ENABLE_LCMS
187 unsigned char* buf = 0;
188 guint len = 0;
190 ege_color_prof_tracker_get_profile_for( screen, monitor, reinterpret_cast<gpointer*>(&buf), &len );
191 Glib::ustring id = Inkscape::colorprofile_set_display_per( buf, len, screen, monitor );
192 #endif // ENABLE_LCMS
193 }
195 /// @todo Use conditional compilation in saner places. The whole PrefWatcher
196 /// object is unnecessary if ENABLE_LCMS is not defined.
197 void CMSPrefWatcher::_refreshAll()
198 {
199 #if ENABLE_LCMS
200 for ( std::list<SPDesktopWidget*>::iterator it = _widget_list.begin(); it != _widget_list.end(); ++it ) {
201 (*it)->requestCanvasUpdate();
202 }
203 #endif // ENABLE_LCMS
204 }
206 void CMSPrefWatcher::_setCmsSensitive(bool enabled)
207 {
208 #if ENABLE_LCMS
209 for ( std::list<SPDesktopWidget*>::iterator it = _widget_list.begin(); it != _widget_list.end(); ++it ) {
210 SPDesktopWidget *dtw = *it;
211 if ( GTK_WIDGET_SENSITIVE( dtw->cms_adjust ) != enabled ) {
212 cms_adjust_set_sensitive( dtw, enabled );
213 }
214 }
215 #else
216 (void) enabled;
217 #endif // ENABLE_LCMS
218 }
220 static CMSPrefWatcher* watcher = NULL;
222 void
223 SPDesktopWidget::setMessage (Inkscape::MessageType type, const gchar *message)
224 {
225 GtkLabel *sb=GTK_LABEL(this->select_status);
226 gtk_label_set_markup (sb, message ? message : "");
228 // make sure the important messages are displayed immediately!
229 if (type == Inkscape::IMMEDIATE_MESSAGE && GTK_WIDGET_DRAWABLE (GTK_WIDGET(sb))) {
230 gtk_widget_queue_draw(GTK_WIDGET(sb));
231 gdk_window_process_updates(GTK_WIDGET(sb)->window, TRUE);
232 }
234 gtk_tooltips_set_tip (this->tt, this->select_status_eventbox, gtk_label_get_text (sb) , NULL);
235 }
237 Geom::Point
238 SPDesktopWidget::window_get_pointer()
239 {
240 gint x,y;
241 gdk_window_get_pointer (GTK_WIDGET (canvas)->window, &x, &y, NULL);
242 return Geom::Point(x,y);
243 }
245 /**
246 * Registers SPDesktopWidget class and returns its type number.
247 */
248 GType SPDesktopWidget::getType(void)
249 {
250 static GtkType type = 0;
251 if (!type) {
252 GTypeInfo info = {
253 sizeof(SPDesktopWidgetClass),
254 0, // base_init
255 0, // base_finalize
256 (GClassInitFunc)sp_desktop_widget_class_init,
257 0, // class_finalize
258 0, // class_data
259 sizeof(SPDesktopWidget),
260 0, // n_preallocs
261 (GInstanceInitFunc)SPDesktopWidget::init,
262 0 // value_table
263 };
264 type = g_type_register_static(SP_TYPE_VIEW_WIDGET, "SPDesktopWidget", &info, static_cast<GTypeFlags>(0));
265 }
266 return type;
267 }
269 /**
270 * SPDesktopWidget vtable initialization
271 */
272 static void
273 sp_desktop_widget_class_init (SPDesktopWidgetClass *klass)
274 {
275 dtw_parent_class = (SPViewWidgetClass*)gtk_type_class (SP_TYPE_VIEW_WIDGET);
277 GtkObjectClass *object_class = (GtkObjectClass *) klass;
278 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
280 object_class->destroy = sp_desktop_widget_destroy;
282 widget_class->size_allocate = sp_desktop_widget_size_allocate;
283 widget_class->realize = sp_desktop_widget_realize;
284 }
286 /**
287 * Callback for SPDesktopWidget object initialization.
288 */
289 void SPDesktopWidget::init( SPDesktopWidget *dtw )
290 {
291 GtkWidget *widget;
292 GtkWidget *tbl;
293 GtkWidget *canvas_tbl;
295 GtkWidget *eventbox;
296 GtkStyle *style;
298 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
300 new (&dtw->modified_connection) sigc::connection();
302 widget = GTK_WIDGET (dtw);
304 dtw->window = 0;
305 dtw->desktop = NULL;
306 dtw->_interaction_disabled_counter = 0;
307 dtw->tt = gtk_tooltips_new ();
309 /* Main table */
310 dtw->vbox = gtk_vbox_new (FALSE, 0);
311 gtk_container_add( GTK_CONTAINER(dtw), GTK_WIDGET(dtw->vbox) );
313 dtw->statusbar = gtk_hbox_new (FALSE, 0);
314 //gtk_widget_set_usize (dtw->statusbar, -1, BOTTOM_BAR_HEIGHT);
315 gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->statusbar, FALSE, TRUE, 0);
317 {
318 using Inkscape::UI::Dialogs::SwatchesPanel;
320 dtw->panels = new SwatchesPanel("/embedded/swatches");
321 dtw->panels->setOrientation( Gtk::ANCHOR_SOUTH );
322 gtk_box_pack_end( GTK_BOX( dtw->vbox ), GTK_WIDGET(dtw->panels->gobj()), FALSE, TRUE, 0 );
323 }
325 dtw->hbox = gtk_hbox_new(FALSE, 0);
326 gtk_box_pack_end( GTK_BOX (dtw->vbox), dtw->hbox, TRUE, TRUE, 0 );
327 gtk_widget_show(dtw->hbox);
329 dtw->aux_toolbox = ToolboxFactory::createAuxToolbox();
330 gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->aux_toolbox, FALSE, TRUE, 0);
332 dtw->snap_toolbox = ToolboxFactory::createSnapToolbox();
333 ToolboxFactory::setOrientation( dtw->snap_toolbox, GTK_ORIENTATION_VERTICAL );
334 gtk_box_pack_end( GTK_BOX(dtw->hbox), dtw->snap_toolbox, FALSE, TRUE, 0 );
336 dtw->commands_toolbox = ToolboxFactory::createCommandsToolbox();
337 gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->commands_toolbox, FALSE, TRUE, 0);
339 dtw->tool_toolbox = ToolboxFactory::createToolToolbox();
340 ToolboxFactory::setOrientation( dtw->tool_toolbox, GTK_ORIENTATION_VERTICAL );
341 gtk_box_pack_start( GTK_BOX(dtw->hbox), dtw->tool_toolbox, FALSE, TRUE, 0 );
343 tbl = gtk_table_new (2, 3, FALSE);
344 gtk_box_pack_start( GTK_BOX(dtw->hbox), tbl, TRUE, TRUE, 1 );
346 canvas_tbl = gtk_table_new (3, 3, FALSE);
348 /* Horizontal ruler */
349 eventbox = gtk_event_box_new ();
350 dtw->hruler = sp_hruler_new ();
351 dtw->hruler_box = eventbox;
352 sp_ruler_set_metric (GTK_RULER (dtw->hruler), SP_PT);
353 gtk_tooltips_set_tip (dtw->tt, dtw->hruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL);
354 gtk_container_add (GTK_CONTAINER (eventbox), dtw->hruler);
355 gtk_table_attach (GTK_TABLE (canvas_tbl), eventbox, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), widget->style->xthickness, 0);
356 g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_hruler_event), dtw);
357 g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_hruler_event), dtw);
358 g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_hruler_event), dtw);
360 /* Vertical ruler */
361 eventbox = gtk_event_box_new ();
362 dtw->vruler = sp_vruler_new ();
363 dtw->vruler_box = eventbox;
364 sp_ruler_set_metric (GTK_RULER (dtw->vruler), SP_PT);
365 gtk_tooltips_set_tip (dtw->tt, dtw->vruler_box, gettext(sp_unit_get_plural (&sp_unit_get_by_id(SP_UNIT_PT))), NULL);
366 gtk_container_add (GTK_CONTAINER (eventbox), GTK_WIDGET (dtw->vruler));
367 gtk_table_attach (GTK_TABLE (canvas_tbl), eventbox, 0, 1, 1, 2, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_FILL), 0, widget->style->ythickness);
368 g_signal_connect (G_OBJECT (eventbox), "button_press_event", G_CALLBACK (sp_dt_vruler_event), dtw);
369 g_signal_connect (G_OBJECT (eventbox), "button_release_event", G_CALLBACK (sp_dt_vruler_event), dtw);
370 g_signal_connect (G_OBJECT (eventbox), "motion_notify_event", G_CALLBACK (sp_dt_vruler_event), dtw);
372 /* Horizontal scrollbar */
373 dtw->hadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
374 dtw->hscrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (dtw->hadj));
375 gtk_table_attach (GTK_TABLE (canvas_tbl), dtw->hscrollbar, 1, 2, 2, 3, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)(GTK_SHRINK), 0, 0);
377 /* Vertical scrollbar and the sticky zoom button */
378 dtw->vscrollbar_box = gtk_vbox_new (FALSE, 0);
379 dtw->sticky_zoom = sp_button_new_from_data ( Inkscape::ICON_SIZE_DECORATION,
380 SP_BUTTON_TYPE_TOGGLE,
381 NULL,
382 INKSCAPE_ICON_ZOOM_ORIGINAL,
383 _("Zoom drawing if window size changes"),
384 dtw->tt);
385 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dtw->sticky_zoom), prefs->getBool("/options/stickyzoom/value"));
386 gtk_box_pack_start (GTK_BOX (dtw->vscrollbar_box), dtw->sticky_zoom, FALSE, FALSE, 0);
387 g_signal_connect (G_OBJECT (dtw->sticky_zoom), "toggled", G_CALLBACK (sp_dtw_sticky_zoom_toggled), dtw);
388 dtw->vadj = (GtkAdjustment *) gtk_adjustment_new (0.0, -4000.0, 4000.0, 10.0, 100.0, 4.0);
389 dtw->vscrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (dtw->vadj));
390 gtk_box_pack_start (GTK_BOX (dtw->vscrollbar_box), dtw->vscrollbar, TRUE, TRUE, 0);
391 gtk_table_attach (GTK_TABLE (canvas_tbl), dtw->vscrollbar_box, 2, 3, 0, 2, (GtkAttachOptions)(GTK_SHRINK), (GtkAttachOptions)(GTK_FILL), 0, 0);
394 gchar const* tip = "";
395 Inkscape::Verb* verb = Inkscape::Verb::get( SP_VERB_VIEW_CMS_TOGGLE );
396 if ( verb ) {
397 SPAction *act = verb->get_action( dtw->viewwidget.view );
398 if ( act && act->tip ) {
399 tip = act->tip;
400 }
401 }
402 dtw->cms_adjust = sp_button_new_from_data( Inkscape::ICON_SIZE_DECORATION,
403 SP_BUTTON_TYPE_TOGGLE,
404 NULL,
405 INKSCAPE_ICON_COLOR_MANAGEMENT,
406 tip,
407 dtw->tt );
408 #if ENABLE_LCMS
409 {
410 Glib::ustring current = prefs->getString("/options/displayprofile/uri");
411 bool enabled = current.length() > 0;
412 cms_adjust_set_sensitive( dtw, enabled );
413 if ( enabled ) {
414 bool active = prefs->getBool("/options/displayprofile/enable");
415 if ( active ) {
416 sp_button_toggle_set_down( SP_BUTTON(dtw->cms_adjust), TRUE );
417 }
418 }
419 }
420 g_signal_connect_after( G_OBJECT(dtw->cms_adjust), "clicked", G_CALLBACK(cms_adjust_toggled), dtw );
421 #else
422 cms_adjust_set_sensitive(dtw, FALSE);
423 #endif // ENABLE_LCMS
424 gtk_table_attach( GTK_TABLE(canvas_tbl), dtw->cms_adjust, 2, 3, 2, 3, (GtkAttachOptions)(GTK_SHRINK), (GtkAttachOptions)(GTK_SHRINK), 0, 0);
425 {
426 if (!watcher) {
427 watcher = new CMSPrefWatcher();
428 }
429 watcher->add(dtw);
430 }
432 /* Canvas */
433 dtw->canvas = SP_CANVAS (sp_canvas_new_aa ());
434 #if ENABLE_LCMS
435 dtw->canvas->enable_cms_display_adj = prefs->getBool("/options/displayprofile/enable");
436 #endif // ENABLE_LCMS
437 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dtw->canvas), GTK_CAN_FOCUS);
438 style = gtk_style_copy (GTK_WIDGET (dtw->canvas)->style);
439 style->bg[GTK_STATE_NORMAL] = style->white;
440 gtk_widget_set_style (GTK_WIDGET (dtw->canvas), style);
441 if ( prefs->getBool("/options/useextinput/value", true) )
442 gtk_widget_set_extension_events(GTK_WIDGET (dtw->canvas) , GDK_EXTENSION_EVENTS_ALL); //set extension events for tablets, unless disabled in preferences
443 g_signal_connect (G_OBJECT (dtw->canvas), "event", G_CALLBACK (sp_desktop_widget_event), dtw);
444 gtk_table_attach (GTK_TABLE (canvas_tbl), GTK_WIDGET(dtw->canvas), 1, 2, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), 0, 0);
446 /* Dock */
447 bool create_dock =
448 prefs->getIntLimited("/options/dialogtype/value", Inkscape::UI::Dialog::FLOATING, 0, 1) ==
449 Inkscape::UI::Dialog::DOCK;
451 if (create_dock) {
452 dtw->dock = new Inkscape::UI::Widget::Dock();
454 Gtk::HPaned *paned = new Gtk::HPaned();
455 paned->pack1(*Glib::wrap(canvas_tbl));
456 paned->pack2(dtw->dock->getWidget(), Gtk::FILL);
458 /* Prevent the paned from catching F6 and F8 by unsetting the default callbacks */
459 if (GtkPanedClass *paned_class = GTK_PANED_CLASS (G_OBJECT_GET_CLASS (paned->gobj()))) {
460 paned_class->cycle_child_focus = NULL;
461 paned_class->cycle_handle_focus = NULL;
462 }
464 gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (paned->gobj()), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
465 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
467 } else {
468 gtk_table_attach (GTK_TABLE (tbl), GTK_WIDGET (canvas_tbl), 1, 2, 1, 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL),
469 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
470 }
472 dtw->selected_style = new Inkscape::UI::Widget::SelectedStyle(true);
473 GtkHBox *ss_ = dtw->selected_style->gobj();
474 gtk_box_pack_start (GTK_BOX (dtw->statusbar), GTK_WIDGET(ss_), FALSE, FALSE, 0);
475 gtk_box_pack_start (GTK_BOX (dtw->statusbar), gtk_vseparator_new(), FALSE, FALSE, 0);
477 // connect scrollbar signals
478 g_signal_connect (G_OBJECT (dtw->hadj), "value-changed", G_CALLBACK (sp_desktop_widget_adjustment_value_changed), dtw);
479 g_signal_connect (G_OBJECT (dtw->vadj), "value-changed", G_CALLBACK (sp_desktop_widget_adjustment_value_changed), dtw);
481 GtkWidget *statusbar_tail=gtk_statusbar_new();
482 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(statusbar_tail), TRUE);
483 gtk_box_pack_end (GTK_BOX (dtw->statusbar), statusbar_tail, FALSE, FALSE, 0);
485 // zoom status spinbutton
486 dtw->zoom_status = gtk_spin_button_new_with_range (log(SP_DESKTOP_ZOOM_MIN)/log(2), log(SP_DESKTOP_ZOOM_MAX)/log(2), 0.1);
487 gtk_tooltips_set_tip (dtw->tt, dtw->zoom_status, _("Zoom"), NULL);
488 gtk_widget_set_size_request (dtw->zoom_status, STATUS_ZOOM_WIDTH, -1);
489 gtk_entry_set_width_chars (GTK_ENTRY (dtw->zoom_status), 6);
490 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dtw->zoom_status), FALSE);
491 gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dtw->zoom_status), GTK_UPDATE_ALWAYS);
492 g_signal_connect (G_OBJECT (dtw->zoom_status), "input", G_CALLBACK (sp_dtw_zoom_input), dtw);
493 g_signal_connect (G_OBJECT (dtw->zoom_status), "output", G_CALLBACK (sp_dtw_zoom_output), dtw);
494 gtk_object_set_data (GTK_OBJECT (dtw->zoom_status), "dtw", dtw->canvas);
495 gtk_signal_connect (GTK_OBJECT (dtw->zoom_status), "focus-in-event", GTK_SIGNAL_FUNC (spinbutton_focus_in), dtw->zoom_status);
496 gtk_signal_connect (GTK_OBJECT (dtw->zoom_status), "key-press-event", GTK_SIGNAL_FUNC (spinbutton_keypress), dtw->zoom_status);
497 dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
498 dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
500 // cursor coordinates
501 dtw->coord_status = gtk_table_new (5, 2, FALSE);
502 gtk_table_set_row_spacings(GTK_TABLE(dtw->coord_status), 0);
503 gtk_table_set_col_spacings(GTK_TABLE(dtw->coord_status), 2);
504 gtk_table_attach(GTK_TABLE(dtw->coord_status), gtk_vseparator_new(), 0,1, 0,2, GTK_FILL, GTK_FILL, 0, 0);
505 eventbox = gtk_event_box_new ();
506 gtk_container_add (GTK_CONTAINER (eventbox), dtw->coord_status);
507 gtk_tooltips_set_tip (dtw->tt, eventbox, _("Cursor coordinates"), NULL);
508 GtkWidget *label_x = gtk_label_new(_("X:"));
509 gtk_misc_set_alignment (GTK_MISC(label_x), 0.0, 0.5);
510 gtk_table_attach(GTK_TABLE(dtw->coord_status), label_x, 1,2, 0,1, GTK_FILL, GTK_FILL, 0, 0);
511 GtkWidget *label_y = gtk_label_new(_("Y:"));
512 gtk_misc_set_alignment (GTK_MISC(label_y), 0.0, 0.5);
513 gtk_table_attach(GTK_TABLE(dtw->coord_status), label_y, 1,2, 1,2, GTK_FILL, GTK_FILL, 0, 0);
514 dtw->coord_status_x = gtk_label_new(NULL);
515 gtk_label_set_markup( GTK_LABEL(dtw->coord_status_x), "<tt> 0.00 </tt>" );
516 gtk_misc_set_alignment (GTK_MISC(dtw->coord_status_x), 1.0, 0.5);
517 dtw->coord_status_y = gtk_label_new(NULL);
518 gtk_label_set_markup( GTK_LABEL(dtw->coord_status_y), "<tt> 0.00 </tt>" );
519 gtk_misc_set_alignment (GTK_MISC(dtw->coord_status_y), 1.0, 0.5);
520 gtk_table_attach(GTK_TABLE(dtw->coord_status), dtw->coord_status_x, 2,3, 0,1, GTK_FILL, GTK_FILL, 0, 0);
521 gtk_table_attach(GTK_TABLE(dtw->coord_status), dtw->coord_status_y, 2,3, 1,2, GTK_FILL, GTK_FILL, 0, 0);
522 gtk_table_attach(GTK_TABLE(dtw->coord_status), gtk_label_new(_("Z:")), 3,4, 0,2, GTK_FILL, GTK_FILL, 0, 0);
523 gtk_table_attach(GTK_TABLE(dtw->coord_status), dtw->zoom_status, 4,5, 0,2, GTK_FILL, GTK_FILL, 0, 0);
524 sp_set_font_size_smaller (dtw->coord_status);
525 gtk_box_pack_end (GTK_BOX (statusbar_tail), eventbox, FALSE, FALSE, 1);
527 dtw->layer_selector = new Inkscape::Widgets::LayerSelector(NULL);
528 // FIXME: need to unreference on container destruction to avoid leak
529 dtw->layer_selector->reference();
530 //dtw->layer_selector->set_size_request(-1, SP_ICON_SIZE_BUTTON);
531 gtk_box_pack_start(GTK_BOX(dtw->statusbar), GTK_WIDGET(dtw->layer_selector->gobj()), FALSE, FALSE, 1);
533 dtw->_tracker = ege_color_prof_tracker_new(GTK_WIDGET(dtw->layer_selector->gobj()));
534 #if ENABLE_LCMS
535 bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display");
536 if ( fromDisplay ) {
537 Glib::ustring id = Inkscape::colorprofile_get_display_id( 0, 0 );
539 bool enabled = false;
540 if ( dtw->canvas->cms_key ) {
541 *(dtw->canvas->cms_key) = id;
542 enabled = !dtw->canvas->cms_key->empty();
543 }
544 cms_adjust_set_sensitive( dtw, enabled );
545 }
546 #endif // ENABLE_LCMS
547 g_signal_connect( G_OBJECT(dtw->_tracker), "changed", G_CALLBACK(sp_dtw_color_profile_event), dtw );
549 dtw->select_status_eventbox = gtk_event_box_new ();
550 dtw->select_status = gtk_label_new (NULL);
551 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 6
552 gtk_label_set_ellipsize (GTK_LABEL(dtw->select_status), PANGO_ELLIPSIZE_END);
553 #endif
554 gtk_misc_set_alignment (GTK_MISC (dtw->select_status), 0.0, 0.5);
555 gtk_widget_set_size_request (dtw->select_status, 1, -1);
556 // display the initial welcome message in the statusbar
557 gtk_label_set_markup (GTK_LABEL (dtw->select_status), _("<b>Welcome to Inkscape!</b> Use shape or freehand tools to create objects; use selector (arrow) to move or transform them."));
558 // space label 2 pixels from left edge
559 gtk_container_add (GTK_CONTAINER (dtw->select_status_eventbox), dtw->select_status);
560 gtk_box_pack_start (GTK_BOX (dtw->statusbar), gtk_hbox_new(FALSE, 0), FALSE, FALSE, 2);
561 gtk_box_pack_start (GTK_BOX (dtw->statusbar), dtw->select_status_eventbox, TRUE, TRUE, 0);
563 gtk_widget_show_all (dtw->vbox);
565 gtk_widget_grab_focus (GTK_WIDGET(dtw->canvas));
566 }
568 /**
569 * Called before SPDesktopWidget destruction.
570 */
571 static void
572 sp_desktop_widget_destroy (GtkObject *object)
573 {
574 SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (object);
576 UXManager::getInstance()->delTrack(dtw);
578 if (dtw->desktop) {
579 if ( watcher ) {
580 watcher->remove(dtw);
581 }
582 g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK(sp_dtw_zoom_input), dtw);
583 g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK(sp_dtw_zoom_output), dtw);
584 gtk_signal_disconnect_by_data (GTK_OBJECT (dtw->zoom_status), dtw->zoom_status);
585 g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
586 g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
587 g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas), (gpointer) G_CALLBACK (sp_desktop_widget_event), dtw);
589 dtw->layer_selector->unreference();
590 inkscape_remove_desktop (dtw->desktop); // clears selection too
591 dtw->modified_connection.disconnect();
592 dtw->desktop->destroy();
593 Inkscape::GC::release (dtw->desktop);
594 dtw->desktop = NULL;
595 }
597 dtw->modified_connection.~connection();
599 if (GTK_OBJECT_CLASS (dtw_parent_class)->destroy) {
600 (* GTK_OBJECT_CLASS (dtw_parent_class)->destroy) (object);
601 }
602 }
605 /**
606 * Set the title in the desktop-window (if desktop has an own window).
607 *
608 * The title has form file name: desktop number - Inkscape.
609 * The desktop number is only shown if it's 2 or higher,
610 */
611 void
612 SPDesktopWidget::updateTitle(gchar const* uri)
613 {
614 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
616 if (window) {
617 gchar const *fname = ( TRUE
618 ? uri
619 : g_basename(uri) );
620 GString *name = g_string_new ("");
621 if (this->desktop->number > 1) {
622 if (this->desktop->getMode() == Inkscape::RENDERMODE_OUTLINE) {
623 g_string_printf (name, _("%s: %d (outline) - Inkscape"), fname, this->desktop->number);
624 } else if (this->desktop->getMode() == Inkscape::RENDERMODE_NO_FILTERS) {
625 g_string_printf (name, _("%s: %d (no filters) - Inkscape"), fname, this->desktop->number);
626 } else if (this->desktop->getMode() == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW) {
627 g_string_printf (name, _("%s: %d (print colors preview) - Inkscape"), fname, this->desktop->number);
628 } else {
629 g_string_printf (name, _("%s: %d - Inkscape"), fname, this->desktop->number);
630 }
631 } else {
632 if (this->desktop->getMode() == Inkscape::RENDERMODE_OUTLINE) {
633 g_string_printf (name, _("%s (outline) - Inkscape"), fname);
634 } else if (this->desktop->getMode() == Inkscape::RENDERMODE_NO_FILTERS) {
635 g_string_printf (name, _("%s (no filters) - Inkscape"), fname);
636 } else if (this->desktop->getMode() == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW) {
637 g_string_printf (name, _("%s (print colors preview) - Inkscape"), fname);
638 } else {
639 g_string_printf (name, _("%s - Inkscape"), fname);
640 }
641 }
642 window->set_title (name->str);
643 g_string_free (name, TRUE);
644 }
645 }
647 Inkscape::UI::Widget::Dock*
648 SPDesktopWidget::getDock()
649 {
650 return dock;
651 }
653 /**
654 * Callback to allocate space for desktop widget.
655 */
656 static void
657 sp_desktop_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
658 {
659 SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (widget);
661 if ((allocation->x == widget->allocation.x) &&
662 (allocation->y == widget->allocation.y) &&
663 (allocation->width == widget->allocation.width) &&
664 (allocation->height == widget->allocation.height)) {
665 if (GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate)
666 GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate (widget, allocation);
667 return;
668 }
670 if (GTK_WIDGET_REALIZED (widget)) {
671 Geom::Rect const area = dtw->desktop->get_display_area();
672 double zoom = dtw->desktop->current_zoom();
674 if (GTK_WIDGET_CLASS(dtw_parent_class)->size_allocate) {
675 GTK_WIDGET_CLASS(dtw_parent_class)->size_allocate (widget, allocation);
676 }
678 if (SP_BUTTON_IS_DOWN(dtw->sticky_zoom)) {
679 /* Find new visible area */
680 Geom::Rect newarea = dtw->desktop->get_display_area();
681 /* Calculate adjusted zoom */
682 double oldshortside = MIN( area.width(), area.height());
683 double newshortside = MIN(newarea.width(), newarea.height());
684 zoom *= newshortside / oldshortside;
685 }
686 dtw->desktop->zoom_absolute(area.midpoint()[Geom::X], area.midpoint()[Geom::Y], zoom);
688 } else {
689 if (GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate) {
690 GTK_WIDGET_CLASS (dtw_parent_class)->size_allocate (widget, allocation);
691 }
692 // this->size_allocate (widget, allocation);
693 }
694 }
696 /**
697 * Callback to realize desktop widget.
698 */
699 static void
700 sp_desktop_widget_realize (GtkWidget *widget)
701 {
703 SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (widget);
705 if (GTK_WIDGET_CLASS (dtw_parent_class)->realize)
706 (* GTK_WIDGET_CLASS (dtw_parent_class)->realize) (widget);
708 NRRect d;
709 d.x0 = 0.0;
710 d.y0 = 0.0;
711 d.x1 = (dtw->desktop->doc())->getWidth ();
712 d.y1 = (dtw->desktop->doc())->getHeight ();
714 if ((fabs (d.x1 - d.x0) < 1.0) || (fabs (d.y1 - d.y0) < 1.0)) return;
716 dtw->desktop->set_display_area (d.x0, d.y0, d.x1, d.y1, 10);
718 dtw->updateNamedview();
719 }
721 /* This is just to provide access to common functionality from sp_desktop_widget_realize() above
722 as well as from SPDesktop::change_document() */
723 void SPDesktopWidget::updateNamedview()
724 {
725 // Listen on namedview modification
726 // originally (prior to the sigc++ conversion) the signal was simply
727 // connected twice rather than disconnecting the first connection
728 modified_connection.disconnect();
730 modified_connection = desktop->namedview->connectModified(sigc::mem_fun(*this, &SPDesktopWidget::namedviewModified));
731 namedviewModified(desktop->namedview, SP_OBJECT_MODIFIED_FLAG);
733 updateTitle(SP_DOCUMENT_NAME (desktop->doc()));
734 }
736 /**
737 * Callback to handle desktop widget event.
738 */
739 static gint
740 sp_desktop_widget_event (GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
741 {
742 if (event->type == GDK_BUTTON_PRESS) {
743 // defocus any spinbuttons
744 gtk_widget_grab_focus (GTK_WIDGET(dtw->canvas));
745 }
747 if ((event->type == GDK_BUTTON_PRESS) && (event->button.button == 3)) {
748 if (event->button.state & GDK_SHIFT_MASK) {
749 sp_canvas_arena_set_sticky (SP_CANVAS_ARENA (dtw->desktop->drawing), TRUE);
750 } else {
751 sp_canvas_arena_set_sticky (SP_CANVAS_ARENA (dtw->desktop->drawing), FALSE);
752 }
753 }
755 if (GTK_WIDGET_CLASS (dtw_parent_class)->event) {
756 return (* GTK_WIDGET_CLASS (dtw_parent_class)->event) (widget, event);
757 } else {
758 // The keypress events need to be passed to desktop handler explicitly,
759 // because otherwise the event contexts only receive keypresses when the mouse cursor
760 // is over the canvas. This redirection is only done for keypresses and only if there's no
761 // current item on the canvas, because item events and all mouse events are caught
762 // and passed on by the canvas acetate (I think). --bb
763 if (event->type == GDK_KEY_PRESS && !dtw->canvas->current_item) {
764 return sp_desktop_root_handler (NULL, event, dtw->desktop);
765 }
766 }
768 return FALSE;
769 }
771 void sp_dtw_color_profile_event(EgeColorProfTracker */*tracker*/, SPDesktopWidget *dtw)
772 {
773 #if ENABLE_LCMS
774 // Handle profile changes
775 GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET(dtw));
776 gint screenNum = gdk_screen_get_number(screen);
777 gint monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_toplevel(GTK_WIDGET(dtw))->window);
778 Glib::ustring id = Inkscape::colorprofile_get_display_id( screenNum, monitor );
779 bool enabled = false;
780 if ( dtw->canvas->cms_key ) {
781 *(dtw->canvas->cms_key) = id;
782 dtw->requestCanvasUpdate();
783 enabled = !dtw->canvas->cms_key->empty();
784 }
785 cms_adjust_set_sensitive( dtw, enabled );
786 #endif // ENABLE_LCMS
787 }
789 void cms_adjust_toggled( GtkWidget */*button*/, gpointer data )
790 {
791 #if ENABLE_LCMS
792 SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(data);
794 bool down = SP_BUTTON_IS_DOWN(dtw->cms_adjust);
795 if ( down != dtw->canvas->enable_cms_display_adj ) {
796 dtw->canvas->enable_cms_display_adj = down;
797 dtw->requestCanvasUpdate();
798 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
799 prefs->setBool("/options/displayprofile/enable", down);
800 if (down) {
801 dtw->setMessage (Inkscape::NORMAL_MESSAGE, _("Color-managed display is <b>enabled</b> in this window"));
802 } else {
803 dtw->setMessage (Inkscape::NORMAL_MESSAGE, _("Color-managed display is <b>disabled</b> in this window"));
804 }
805 }
806 #endif // ENABLE_LCMS
807 }
809 void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled )
810 {
811 Inkscape::Verb* verb = Inkscape::Verb::get( SP_VERB_VIEW_CMS_TOGGLE );
812 if ( verb ) {
813 SPAction *act = verb->get_action( dtw->viewwidget.view );
814 if ( act ) {
815 sp_action_set_sensitive( act, enabled );
816 }
817 }
818 gtk_widget_set_sensitive( dtw->cms_adjust, enabled );
819 }
821 void
822 sp_dtw_desktop_activate (SPDesktopWidget */*dtw*/)
823 {
824 /* update active desktop indicator */
825 }
827 void
828 sp_dtw_desktop_deactivate (SPDesktopWidget */*dtw*/)
829 {
830 /* update inactive desktop indicator */
831 }
833 /**
834 * Shuts down the desktop object for the view being closed. It checks
835 * to see if the document has been edited, and if so prompts the user
836 * to save, discard, or cancel. Returns TRUE if the shutdown operation
837 * is cancelled or if the save is cancelled or fails, FALSE otherwise.
838 */
839 bool
840 SPDesktopWidget::shutdown()
841 {
842 g_assert(desktop != NULL);
844 if (inkscape_is_sole_desktop_for_document(*desktop)) {
845 SPDocument *doc = desktop->doc();
846 if (doc->isModifiedSinceSave()) {
847 GtkWidget *dialog;
849 /** \todo
850 * FIXME !!! obviously this will have problems if the document
851 * name contains markup characters
852 */
853 dialog = gtk_message_dialog_new_with_markup(
854 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this))),
855 GTK_DIALOG_DESTROY_WITH_PARENT,
856 GTK_MESSAGE_WARNING,
857 GTK_BUTTONS_NONE,
858 _("<span weight=\"bold\" size=\"larger\">Save changes to document \"%s\" before closing?</span>\n\n"
859 "If you close without saving, your changes will be discarded."),
860 SP_DOCUMENT_NAME(doc));
861 // fix for bug 1767940:
862 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(GTK_MESSAGE_DIALOG(dialog)->label), GTK_CAN_FOCUS);
864 GtkWidget *close_button;
865 close_button = gtk_button_new_with_mnemonic(_("Close _without saving"));
866 gtk_widget_show(close_button);
867 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), close_button, GTK_RESPONSE_NO);
869 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
870 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
871 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
873 gint response;
874 response = gtk_dialog_run(GTK_DIALOG(dialog));
875 gtk_widget_destroy(dialog);
877 switch (response) {
878 case GTK_RESPONSE_YES:
879 {
880 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
882 doc->doRef();
883 sp_namedview_document_from_window(desktop);
884 if (sp_file_save_document(*window, doc)) {
885 doc->doUnref();
886 } else { // save dialog cancelled or save failed
887 doc->doUnref();
888 return TRUE;
889 }
891 break;
892 }
893 case GTK_RESPONSE_NO:
894 break;
895 default: // cancel pressed, or dialog was closed
896 return TRUE;
897 break;
898 }
899 }
900 /* Code to check data loss */
901 bool allow_data_loss = FALSE;
902 while (sp_document_repr_root(doc)->attribute("inkscape:dataloss") != NULL && allow_data_loss == FALSE) {
903 GtkWidget *dialog;
905 /** \todo
906 * FIXME !!! obviously this will have problems if the document
907 * name contains markup characters
908 */
909 dialog = gtk_message_dialog_new_with_markup(
910 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this))),
911 GTK_DIALOG_DESTROY_WITH_PARENT,
912 GTK_MESSAGE_WARNING,
913 GTK_BUTTONS_NONE,
914 _("<span weight=\"bold\" size=\"larger\">The file \"%s\" was saved with a format (%s) that may cause data loss!</span>\n\n"
915 "Do you want to save this file as Inkscape SVG?"),
916 SP_DOCUMENT_NAME(doc)? SP_DOCUMENT_NAME(doc) : "Unnamed",
917 SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE);
918 // fix for bug 1767940:
919 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(GTK_MESSAGE_DIALOG(dialog)->label), GTK_CAN_FOCUS);
921 GtkWidget *close_button;
922 close_button = gtk_button_new_with_mnemonic(_("Close _without saving"));
923 gtk_widget_show(close_button);
924 GtkWidget *save_button;
925 save_button = gtk_button_new_with_mnemonic(_("_Save as SVG"));
926 gtk_widget_show(save_button);
927 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), close_button, GTK_RESPONSE_NO);
929 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
930 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), save_button, GTK_RESPONSE_YES);
931 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
933 gint response;
934 response = gtk_dialog_run(GTK_DIALOG(dialog));
935 gtk_widget_destroy(dialog);
937 switch (response) {
938 case GTK_RESPONSE_YES:
939 {
940 doc->doRef();
942 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
944 if (sp_file_save_dialog(*window, doc, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG)) {
945 doc->doUnref();
946 } else { // save dialog cancelled or save failed
947 doc->doUnref();
948 return TRUE;
949 }
951 break;
952 }
953 case GTK_RESPONSE_NO:
954 allow_data_loss = TRUE;
955 break;
956 default: // cancel pressed, or dialog was closed
957 return TRUE;
958 break;
959 }
960 }
961 }
963 /* Save window geometry to prefs for use as a default.
964 * Use depends on setting of "options.savewindowgeometry".
965 * But we save the info here regardless of the setting.
966 */
967 {
968 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
969 bool maxed = desktop->is_maximized();
970 bool full = desktop->is_fullscreen();
971 prefs->setBool("/desktop/geometry/fullscreen", full);
972 prefs->setBool("/desktop/geometry/maximized", maxed);
973 gint w, h, x, y;
974 desktop->getWindowGeometry(x, y, w, h);
975 // Don't save geom for maximized windows. It
976 // just tells you the current maximized size, which is not
977 // as useful as whatever value it had previously.
978 if (!maxed && !full) {
979 prefs->setInt("/desktop/geometry/width", w);
980 prefs->setInt("/desktop/geometry/height", h);
981 prefs->setInt("/desktop/geometry/x", x);
982 prefs->setInt("/desktop/geometry/y", y);
983 }
984 }
986 return FALSE;
987 }
989 /**
990 * \pre this->desktop->main != 0
991 */
992 void
993 SPDesktopWidget::requestCanvasUpdate() {
994 gtk_widget_queue_draw (GTK_WIDGET (SP_CANVAS_ITEM (this->desktop->main)->canvas));
995 }
997 void
998 SPDesktopWidget::requestCanvasUpdateAndWait() {
999 requestCanvasUpdate();
1001 while (gtk_events_pending())
1002 gtk_main_iteration_do(FALSE);
1004 }
1006 void
1007 SPDesktopWidget::enableInteraction()
1008 {
1009 g_return_if_fail(_interaction_disabled_counter > 0);
1011 _interaction_disabled_counter--;
1013 if (_interaction_disabled_counter == 0) {
1014 gtk_widget_set_sensitive(GTK_WIDGET(this), TRUE);
1015 }
1016 }
1018 void
1019 SPDesktopWidget::disableInteraction()
1020 {
1021 if (_interaction_disabled_counter == 0) {
1022 gtk_widget_set_sensitive(GTK_WIDGET(this), FALSE);
1023 }
1025 _interaction_disabled_counter++;
1026 }
1028 void
1029 SPDesktopWidget::setCoordinateStatus(Geom::Point p)
1030 {
1031 gchar *cstr;
1032 cstr = g_strdup_printf("<tt>%7.2f </tt>", dt2r * p[Geom::X]);
1033 gtk_label_set_markup( GTK_LABEL(this->coord_status_x), cstr );
1034 g_free(cstr);
1036 cstr = g_strdup_printf("<tt>%7.2f </tt>", dt2r * p[Geom::Y]);
1037 gtk_label_set_markup( GTK_LABEL(this->coord_status_y), cstr );
1038 g_free(cstr);
1039 }
1041 void
1042 SPDesktopWidget::letZoomGrabFocus()
1043 {
1044 if (zoom_status)
1045 gtk_widget_grab_focus (zoom_status);
1046 }
1048 void
1049 SPDesktopWidget::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1050 {
1051 gboolean vis = GTK_WIDGET_VISIBLE (this);
1052 (void)vis; // TODO figure out why it is here but not used.
1054 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
1056 if (window)
1057 {
1058 window->get_size (w, h);
1059 window->get_position (x, y);
1060 }
1061 }
1063 void
1064 SPDesktopWidget::setWindowPosition (Geom::Point p)
1065 {
1066 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
1068 if (window)
1069 {
1070 window->move (gint(round(p[Geom::X])), gint(round(p[Geom::Y])));
1071 }
1072 }
1074 void
1075 SPDesktopWidget::setWindowSize (gint w, gint h)
1076 {
1077 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
1079 if (window)
1080 {
1081 window->set_default_size (w, h);
1082 window->reshow_with_initial_size ();
1083 }
1084 }
1086 /**
1087 * \note transientizing does not work on windows; when you minimize a document
1088 * and then open it back, only its transient emerges and you cannot access
1089 * the document window. The document window must be restored by rightclicking
1090 * the taskbar button and pressing "Restore"
1091 */
1092 void
1093 SPDesktopWidget::setWindowTransient (void *p, int transient_policy)
1094 {
1095 Gtk::Window *window = (Gtk::Window*)gtk_object_get_data (GTK_OBJECT(this), "window");
1096 if (window)
1097 {
1098 GtkWindow *w = (GtkWindow *) window->gobj();
1099 gtk_window_set_transient_for (GTK_WINDOW(p), w);
1101 /*
1102 * This enables "aggressive" transientization,
1103 * i.e. dialogs always emerging on top when you switch documents. Note
1104 * however that this breaks "click to raise" policy of a window
1105 * manager because the switched-to document will be raised at once
1106 * (so that its transients also could raise)
1107 */
1108 if (transient_policy == 2)
1109 // without this, a transient window not always emerges on top
1110 gtk_window_present (w);
1111 }
1112 }
1114 void
1115 SPDesktopWidget::presentWindow()
1116 {
1117 GtkWindow *w =GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this)));
1118 if (w)
1119 gtk_window_present (w);
1120 }
1122 bool
1123 SPDesktopWidget::warnDialog (gchar* text)
1124 {
1125 GtkWindow *w =GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(this)));
1126 if (w)
1127 {
1128 GtkWidget *dialog = gtk_message_dialog_new(
1129 w,
1130 GTK_DIALOG_DESTROY_WITH_PARENT,
1131 GTK_MESSAGE_WARNING,
1132 GTK_BUTTONS_YES_NO,
1133 "%s", text);
1134 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
1135 gtk_widget_destroy(dialog);
1136 if (response == GTK_RESPONSE_YES)
1137 return true;
1138 }
1139 return false;
1140 }
1142 void
1143 sp_desktop_widget_iconify(SPDesktopWidget *dtw)
1144 {
1145 GtkWindow *topw = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dtw->canvas)));
1146 if (GTK_IS_WINDOW(topw)) {
1147 if (dtw->desktop->is_iconified()) {
1148 gtk_window_deiconify(topw);
1149 } else {
1150 gtk_window_iconify(topw);
1151 }
1152 }
1153 }
1155 void
1156 sp_desktop_widget_maximize(SPDesktopWidget *dtw)
1157 {
1158 GtkWindow *topw = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dtw->canvas)));
1159 if (GTK_IS_WINDOW(topw)) {
1160 if (dtw->desktop->is_maximized()) {
1161 gtk_window_unmaximize(topw);
1162 } else {
1163 // Save geometry to prefs before maximizing so that
1164 // something useful is stored there, because GTK doesn't maintain
1165 // a separate non-maximized size.
1166 if (!dtw->desktop->is_iconified() && !dtw->desktop->is_fullscreen())
1167 {
1168 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1169 gint w, h, x, y;
1170 dtw->getWindowGeometry(x, y, w, h);
1171 prefs->setInt("/desktop/geometry/width", w);
1172 prefs->setInt("/desktop/geometry/height", h);
1173 prefs->setInt("/desktop/geometry/x", x);
1174 prefs->setInt("/desktop/geometry/y", y);
1175 }
1176 gtk_window_maximize(topw);
1177 }
1178 }
1179 }
1181 void
1182 sp_desktop_widget_fullscreen(SPDesktopWidget *dtw)
1183 {
1184 #ifdef HAVE_GTK_WINDOW_FULLSCREEN
1185 GtkWindow *topw = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dtw->canvas)));
1186 if (GTK_IS_WINDOW(topw)) {
1187 if (dtw->desktop->is_fullscreen()) {
1188 gtk_window_unfullscreen(topw);
1189 // widget layout is triggered by the resulting window_state_event
1190 } else {
1191 // Save geometry to prefs before maximizing so that
1192 // something useful is stored there, because GTK doesn't maintain
1193 // a separate non-maximized size.
1194 if (!dtw->desktop->is_iconified() && !dtw->desktop->is_maximized())
1195 {
1196 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1197 gint w, h, x, y;
1198 dtw->getWindowGeometry(x, y, w, h);
1199 prefs->setInt("/desktop/geometry/width", w);
1200 prefs->setInt("/desktop/geometry/height", h);
1201 prefs->setInt("/desktop/geometry/x", x);
1202 prefs->setInt("/desktop/geometry/y", y);
1203 }
1204 gtk_window_fullscreen(topw);
1205 // widget layout is triggered by the resulting window_state_event
1206 }
1207 }
1208 #endif /* HAVE_GTK_WINDOW_FULLSCREEN */
1209 }
1211 /**
1212 * Hide whatever the user does not want to see in the window
1213 */
1214 void SPDesktopWidget::layoutWidgets()
1215 {
1216 SPDesktopWidget *dtw = this;
1217 Glib::ustring pref_root;
1218 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1220 if (dtw->desktop->is_focusMode()) {
1221 pref_root = "/focus/";
1222 } else if (dtw->desktop->is_fullscreen()) {
1223 pref_root = "/fullscreen/";
1224 } else {
1225 pref_root = "/window/";
1226 }
1228 #ifndef GDK_WINDOWING_QUARTZ
1229 if (!prefs->getBool(pref_root + "menu/state", true)) {
1230 gtk_widget_hide_all (dtw->menubar);
1231 } else {
1232 gtk_widget_show_all (dtw->menubar);
1233 }
1234 #endif
1236 if (!prefs->getBool(pref_root + "commands/state", true)) {
1237 gtk_widget_hide_all (dtw->commands_toolbox);
1238 } else {
1239 gtk_widget_show_all (dtw->commands_toolbox);
1240 }
1242 if (!prefs->getBool(pref_root + "snaptoolbox/state", true)) {
1243 gtk_widget_hide_all (dtw->snap_toolbox);
1244 } else {
1245 gtk_widget_show_all (dtw->snap_toolbox);
1246 }
1248 if (!prefs->getBool(pref_root + "toppanel/state", true)) {
1249 gtk_widget_hide_all (dtw->aux_toolbox);
1250 } else {
1251 // we cannot just show_all because that will show all tools' panels;
1252 // this is a function from toolbox.cpp that shows only the current tool's panel
1253 ToolboxFactory::showAuxToolbox(dtw->aux_toolbox);
1254 }
1256 if (!prefs->getBool(pref_root + "toolbox/state", true)) {
1257 gtk_widget_hide_all (dtw->tool_toolbox);
1258 } else {
1259 gtk_widget_show_all (dtw->tool_toolbox);
1260 }
1262 if (!prefs->getBool(pref_root + "statusbar/state", true)) {
1263 gtk_widget_hide_all (dtw->statusbar);
1264 } else {
1265 gtk_widget_show_all (dtw->statusbar);
1266 }
1268 if (!prefs->getBool(pref_root + "panels/state", true)) {
1269 gtk_widget_hide_all( GTK_WIDGET(dtw->panels->gobj()) );
1270 } else {
1271 gtk_widget_show_all( GTK_WIDGET(dtw->panels->gobj()) );
1272 }
1274 if (!prefs->getBool(pref_root + "scrollbars/state", true)) {
1275 gtk_widget_hide_all (dtw->hscrollbar);
1276 gtk_widget_hide_all (dtw->vscrollbar_box);
1277 gtk_widget_hide_all( dtw->cms_adjust );
1278 } else {
1279 gtk_widget_show_all (dtw->hscrollbar);
1280 gtk_widget_show_all (dtw->vscrollbar_box);
1281 gtk_widget_show_all( dtw->cms_adjust );
1282 }
1284 if (!prefs->getBool(pref_root + "rulers/state", true)) {
1285 gtk_widget_hide_all (dtw->hruler);
1286 gtk_widget_hide_all (dtw->vruler);
1287 } else {
1288 gtk_widget_show_all (dtw->hruler);
1289 gtk_widget_show_all (dtw->vruler);
1290 }
1291 }
1293 void
1294 SPDesktopWidget::setToolboxFocusTo (const gchar* label)
1295 {
1296 gpointer hb = sp_search_by_data_recursive(aux_toolbox, (gpointer) label);
1297 if (hb && GTK_IS_WIDGET(hb))
1298 {
1299 gtk_widget_grab_focus(GTK_WIDGET(hb));
1300 }
1301 }
1303 void
1304 SPDesktopWidget::setToolboxAdjustmentValue (gchar const *id, double value)
1305 {
1306 GtkAdjustment *a = NULL;
1307 gpointer hb = sp_search_by_data_recursive (aux_toolbox, (gpointer) id);
1308 if (hb && GTK_IS_WIDGET(hb)) {
1309 if (GTK_IS_SPIN_BUTTON(hb))
1310 a = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(hb));
1311 else if (GTK_IS_RANGE(hb))
1312 a = gtk_range_get_adjustment (GTK_RANGE(hb));
1313 }
1315 if (a)
1316 gtk_adjustment_set_value (a, value);
1317 else
1318 g_warning ("Could not find GtkAdjustment for %s\n", id);
1319 }
1321 void
1322 SPDesktopWidget::setToolboxSelectOneValue (gchar const *id, int value)
1323 {
1324 gpointer hb = sp_search_by_data_recursive(aux_toolbox, (gpointer) id);
1325 if (hb) {
1326 ege_select_one_action_set_active((EgeSelectOneAction*) hb, value);
1327 }
1328 }
1331 bool
1332 SPDesktopWidget::isToolboxButtonActive (const gchar* id)
1333 {
1334 bool isActive = false;
1335 gpointer thing = sp_search_by_data_recursive(aux_toolbox, (gpointer) id);
1336 if ( !thing ) {
1337 //g_message( "Unable to locate item for {%s}", id );
1338 } else if ( GTK_IS_TOGGLE_BUTTON(thing) ) {
1339 GtkToggleButton *b = GTK_TOGGLE_BUTTON(thing);
1340 isActive = gtk_toggle_button_get_active( b ) != 0;
1341 } else if ( GTK_IS_TOGGLE_ACTION(thing) ) {
1342 GtkToggleAction* act = GTK_TOGGLE_ACTION(thing);
1343 isActive = gtk_toggle_action_get_active( act ) != 0;
1344 } else {
1345 //g_message( "Item for {%s} is of an unsupported type", id );
1346 }
1348 return isActive;
1349 }
1351 void SPDesktopWidget::setToolboxPosition(Glib::ustring const& id, GtkPositionType pos)
1352 {
1353 // Note - later on these won't be individual member variables.
1354 GtkWidget* toolbox = 0;
1355 if (id == "ToolToolbar") {
1356 toolbox = tool_toolbox;
1357 } else if (id == "AuxToolbar") {
1358 toolbox = aux_toolbox;
1359 } else if (id == "CommandsToolbar") {
1360 toolbox = commands_toolbox;
1361 } else if (id == "SnapToolbar") {
1362 toolbox = snap_toolbox;
1363 }
1366 if (toolbox) {
1367 switch(pos) {
1368 case GTK_POS_TOP:
1369 case GTK_POS_BOTTOM:
1370 if ( gtk_widget_is_ancestor(toolbox, hbox) ) {
1371 gtk_widget_reparent( toolbox, vbox );
1372 gtk_box_set_child_packing(GTK_BOX(vbox), toolbox, FALSE, TRUE, 0, GTK_PACK_START);
1373 }
1374 ToolboxFactory::setOrientation(toolbox, GTK_ORIENTATION_HORIZONTAL);
1375 break;
1376 case GTK_POS_LEFT:
1377 case GTK_POS_RIGHT:
1378 if ( !gtk_widget_is_ancestor(toolbox, hbox) ) {
1379 gtk_widget_reparent( toolbox, hbox );
1380 gtk_box_set_child_packing(GTK_BOX(hbox), toolbox, FALSE, TRUE, 0, GTK_PACK_START);
1381 if (pos == GTK_POS_LEFT) {
1382 gtk_box_reorder_child( GTK_BOX(hbox), toolbox, 0 );
1383 }
1384 }
1385 ToolboxFactory::setOrientation(toolbox, GTK_ORIENTATION_VERTICAL);
1386 break;
1387 }
1388 }
1389 }
1392 SPViewWidget *sp_desktop_widget_new( SPNamedView *namedview )
1393 {
1394 SPDesktopWidget* dtw = SPDesktopWidget::createInstance(namedview);
1396 return SP_VIEW_WIDGET(dtw);
1397 }
1399 SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview)
1400 {
1401 SPDesktopWidget *dtw = (SPDesktopWidget*)g_object_new(SP_TYPE_DESKTOP_WIDGET, NULL);
1403 dtw->dt2r = 1.0 / namedview->doc_units->unittobase;
1405 dtw->ruler_origin = Geom::Point(0,0); //namedview->gridorigin; Why was the grid origin used here?
1407 dtw->desktop = new SPDesktop();
1408 dtw->stub = new SPDesktopWidget::WidgetStub (dtw);
1409 dtw->desktop->init (namedview, dtw->canvas, dtw->stub);
1410 inkscape_add_desktop (dtw->desktop);
1412 // Add the shape geometry to libavoid for autorouting connectors.
1413 // This needs desktop set for its spacing preferences.
1414 init_avoided_shape_geometry(dtw->desktop);
1416 dtw->selected_style->setDesktop(dtw->desktop);
1418 /* Once desktop is set, we can update rulers */
1419 sp_desktop_widget_update_rulers (dtw);
1421 sp_view_widget_set_view (SP_VIEW_WIDGET (dtw), dtw->desktop);
1423 /* Listen on namedview modification */
1424 dtw->modified_connection = namedview->connectModified(sigc::mem_fun(*dtw, &SPDesktopWidget::namedviewModified));
1426 dtw->layer_selector->setDesktop(dtw->desktop);
1428 dtw->menubar = sp_ui_main_menubar (dtw->desktop);
1429 #ifndef GDK_WINDOWING_QUARTZ
1430 gtk_widget_show_all (dtw->menubar);
1431 gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0);
1432 #endif
1434 dtw->layoutWidgets();
1436 std::vector<GtkWidget *> toolboxes;
1437 toolboxes.push_back(dtw->tool_toolbox);
1438 toolboxes.push_back(dtw->aux_toolbox);
1439 toolboxes.push_back(dtw->commands_toolbox);
1440 toolboxes.push_back(dtw->snap_toolbox);
1442 dtw->panels->setDesktop( dtw->desktop );
1444 UXManager::getInstance()->addTrack(dtw);
1445 UXManager::getInstance()->connectToDesktop( toolboxes, dtw->desktop );
1447 return dtw;
1448 }
1450 void
1451 SPDesktopWidget::viewSetPosition (Geom::Point p)
1452 {
1453 Geom::Point const origin = ( p - ruler_origin );
1455 /// \todo fixme:
1456 GTK_RULER(hruler)->position = origin[Geom::X];
1457 gtk_ruler_draw_pos (GTK_RULER (hruler));
1458 GTK_RULER(vruler)->position = origin[Geom::Y];
1459 gtk_ruler_draw_pos (GTK_RULER (vruler));
1460 }
1462 void
1463 sp_desktop_widget_update_rulers (SPDesktopWidget *dtw)
1464 {
1465 sp_desktop_widget_update_hruler(dtw);
1466 sp_desktop_widget_update_vruler(dtw);
1467 }
1469 void
1470 sp_desktop_widget_update_hruler (SPDesktopWidget *dtw)
1471 {
1472 /* The viewbox (in integers) must exactly match the size of SPCanvasbuf's pixel buffer.
1473 * This is important because the former is being used for drawing the ruler, whereas
1474 * the latter is used for drawing e.g. the grids and guides. Only when the viewbox
1475 * coincides with the pixel buffer, everything will line up nicely.
1476 */
1477 NR::IRect viewbox = dtw->canvas->getViewboxIntegers();
1479 double const scale = dtw->desktop->current_zoom();
1480 double s = viewbox.min()[Geom::X] / scale - dtw->ruler_origin[Geom::X];
1481 double e = viewbox.max()[Geom::X] / scale - dtw->ruler_origin[Geom::X];
1482 gtk_ruler_set_range(GTK_RULER(dtw->hruler), s, e, GTK_RULER(dtw->hruler)->position, (e - s));
1483 }
1485 void
1486 sp_desktop_widget_update_vruler (SPDesktopWidget *dtw)
1487 {
1488 /* The viewbox (in integers) must exactly match the size of SPCanvasbuf's pixel buffer.
1489 * This is important because the former is being used for drawing the ruler, whereas
1490 * the latter is used for drawing e.g. the grids and guides. Only when the viewbox
1491 * coincides with the pixel buffer, everything will line up nicely.
1492 */
1493 NR::IRect viewbox = dtw->canvas->getViewboxIntegers();
1495 double const scale = dtw->desktop->current_zoom();
1496 double s = viewbox.min()[Geom::Y] / -scale - dtw->ruler_origin[Geom::Y];
1497 double e = viewbox.max()[Geom::Y] / -scale - dtw->ruler_origin[Geom::Y];
1498 gtk_ruler_set_range(GTK_RULER(dtw->vruler), s, e, GTK_RULER(dtw->vruler)->position, (e - s));
1499 }
1502 void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags)
1503 {
1504 SPNamedView *nv=SP_NAMEDVIEW(obj);
1506 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1507 this->dt2r = 1.0 / nv->doc_units->unittobase;
1508 this->ruler_origin = Geom::Point(0,0); //nv->gridorigin; Why was the grid origin used here?
1510 sp_ruler_set_metric(GTK_RULER (this->vruler), nv->getDefaultMetric());
1511 sp_ruler_set_metric(GTK_RULER (this->hruler), nv->getDefaultMetric());
1513 gtk_tooltips_set_tip(this->tt, this->hruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL);
1514 gtk_tooltips_set_tip(this->tt, this->vruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL);
1516 sp_desktop_widget_update_rulers(this);
1517 ToolboxFactory::updateSnapToolbox(this->desktop, 0, this->snap_toolbox);
1518 }
1519 }
1521 static void
1522 sp_desktop_widget_adjustment_value_changed (GtkAdjustment */*adj*/, SPDesktopWidget *dtw)
1523 {
1524 if (dtw->update)
1525 return;
1527 dtw->update = 1;
1529 sp_canvas_scroll_to (dtw->canvas, dtw->hadj->value, dtw->vadj->value, FALSE);
1530 sp_desktop_widget_update_rulers (dtw);
1532 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1533 sp_box3d_context_update_lines(dtw->desktop->event_context);
1535 dtw->update = 0;
1536 }
1538 /* we make the desktop window with focus active, signal is connected in interface.c */
1539 bool SPDesktopWidget::onFocusInEvent(GdkEventFocus*)
1540 {
1541 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1542 if (prefs->getBool("/options/bitmapautoreload/value", true)) {
1543 GSList const *imageList = (desktop->doc())->get_resource_list("image");
1544 for (GSList const *p = imageList; p; p = p->next) {
1545 SPImage* image = SP_IMAGE(p->data);
1546 sp_image_refresh_if_outdated( image );
1547 }
1548 }
1550 inkscape_activate_desktop (desktop);
1552 return false;
1553 }
1555 static gdouble
1556 sp_dtw_zoom_value_to_display (gdouble value)
1557 {
1558 return floor (10 * (pow (2, value) * 100.0 + 0.05)) / 10;
1559 }
1561 static gdouble
1562 sp_dtw_zoom_display_to_value (gdouble value)
1563 {
1564 return log (value / 100.0) / log (2);
1565 }
1567 static gint
1568 sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/)
1569 {
1570 gdouble new_scrolled = gtk_spin_button_get_value (spin);
1571 const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin));
1572 gdouble new_typed = atof (b);
1574 if (sp_dtw_zoom_value_to_display (new_scrolled) == new_typed) { // the new value is set by scrolling
1575 *new_val = new_scrolled;
1576 } else { // the new value is typed in
1577 *new_val = sp_dtw_zoom_display_to_value (new_typed);
1578 }
1580 return TRUE;
1581 }
1583 static bool
1584 sp_dtw_zoom_output (GtkSpinButton *spin, gpointer /*data*/)
1585 {
1586 gchar b[64];
1587 double val = sp_dtw_zoom_value_to_display (gtk_spin_button_get_value (spin));
1588 if (val < 10) {
1589 g_snprintf (b, 64, "%4.1f%%", val);
1590 } else {
1591 g_snprintf (b, 64, "%4.0f%%", val);
1592 }
1593 gtk_entry_set_text (GTK_ENTRY (spin), b);
1594 return TRUE;
1595 }
1597 static void
1598 sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data)
1599 {
1600 double const zoom_factor = pow (2, gtk_spin_button_get_value (spin));
1602 SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (data);
1603 SPDesktop *desktop = dtw->desktop;
1605 Geom::Rect const d = desktop->get_display_area();
1606 g_signal_handler_block (spin, dtw->zoom_update);
1607 desktop->zoom_absolute (d.midpoint()[Geom::X], d.midpoint()[Geom::Y], zoom_factor);
1608 g_signal_handler_unblock (spin, dtw->zoom_update);
1610 spinbutton_defocus (GTK_OBJECT (spin));
1611 }
1613 static void
1614 sp_dtw_zoom_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data)
1615 {
1616 GList *children, *iter;
1617 GtkWidget *item;
1618 SPDesktop *dt = SP_DESKTOP_WIDGET (data)->desktop;
1620 children = gtk_container_get_children (GTK_CONTAINER (menu));
1621 for ( iter = children ; iter ; iter = g_list_next (iter)) {
1622 gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (iter->data));
1623 }
1624 g_list_free (children);
1626 item = gtk_menu_item_new_with_label ("200%");
1627 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_200), dt);
1628 gtk_widget_show (item);
1629 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1630 item = gtk_menu_item_new_with_label ("100%");
1631 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_100), dt);
1632 gtk_widget_show (item);
1633 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1634 item = gtk_menu_item_new_with_label ("50%");
1635 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_50), dt);
1636 gtk_widget_show (item);
1637 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1639 item = gtk_separator_menu_item_new ();
1640 gtk_widget_show (item);
1641 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1643 item = gtk_menu_item_new_with_label (_("Page"));
1644 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_page), dt);
1645 gtk_widget_show (item);
1646 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1647 item = gtk_menu_item_new_with_label (_("Drawing"));
1648 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_drawing), dt);
1649 gtk_widget_show (item);
1650 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1651 item = gtk_menu_item_new_with_label (_("Selection"));
1652 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_zoom_selection), dt);
1653 gtk_widget_show (item);
1654 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1655 }
1657 static void
1658 sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor)
1659 {
1660 Geom::Rect const d = dt->get_display_area();
1661 dt->zoom_absolute(d.midpoint()[Geom::X], d.midpoint()[Geom::Y], factor);
1662 }
1664 static void
1665 sp_dtw_zoom_50 (GtkMenuItem */*item*/, gpointer data)
1666 {
1667 sp_dtw_zoom_menu_handler (static_cast<SPDesktop*>(data), 0.5);
1668 }
1670 static void
1671 sp_dtw_zoom_100 (GtkMenuItem */*item*/, gpointer data)
1672 {
1673 sp_dtw_zoom_menu_handler (static_cast<SPDesktop*>(data), 1.0);
1674 }
1676 static void
1677 sp_dtw_zoom_200 (GtkMenuItem */*item*/, gpointer data)
1678 {
1679 sp_dtw_zoom_menu_handler (static_cast<SPDesktop*>(data), 2.0);
1680 }
1682 static void
1683 sp_dtw_zoom_page (GtkMenuItem */*item*/, gpointer data)
1684 {
1685 static_cast<SPDesktop*>(data)->zoom_page();
1686 }
1688 static void
1689 sp_dtw_zoom_drawing (GtkMenuItem */*item*/, gpointer data)
1690 {
1691 static_cast<SPDesktop*>(data)->zoom_drawing();
1692 }
1694 static void
1695 sp_dtw_zoom_selection (GtkMenuItem */*item*/, gpointer data)
1696 {
1697 static_cast<SPDesktop*>(data)->zoom_selection();
1698 }
1700 static void
1701 sp_dtw_sticky_zoom_toggled (GtkMenuItem *, gpointer data)
1702 {
1703 SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(data);
1704 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1705 prefs->setBool("/options/stickyzoom/value", SP_BUTTON_IS_DOWN(dtw->sticky_zoom));
1706 }
1709 void
1710 sp_desktop_widget_update_zoom (SPDesktopWidget *dtw)
1711 {
1712 g_signal_handlers_block_by_func (G_OBJECT (dtw->zoom_status), (gpointer)G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
1713 gtk_spin_button_set_value (GTK_SPIN_BUTTON (dtw->zoom_status), log(dtw->desktop->current_zoom()) / log(2));
1714 gtk_widget_queue_draw(GTK_WIDGET(dtw->zoom_status));
1715 if (GTK_WIDGET(dtw->zoom_status)->window)
1716 gdk_window_process_updates(GTK_WIDGET(dtw->zoom_status)->window, TRUE);
1717 g_signal_handlers_unblock_by_func (G_OBJECT (dtw->zoom_status), (gpointer)G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
1718 }
1720 void
1721 sp_desktop_widget_toggle_rulers (SPDesktopWidget *dtw)
1722 {
1723 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1724 if (GTK_WIDGET_VISIBLE (dtw->hruler)) {
1725 gtk_widget_hide_all (dtw->hruler);
1726 gtk_widget_hide_all (dtw->vruler);
1727 prefs->setBool(dtw->desktop->is_fullscreen() ? "/fullscreen/rulers/state" : "/window/rulers/state", false);
1728 } else {
1729 gtk_widget_show_all (dtw->hruler);
1730 gtk_widget_show_all (dtw->vruler);
1731 prefs->setBool(dtw->desktop->is_fullscreen() ? "/fullscreen/rulers/state" : "/window/rulers/state", true);
1732 }
1733 }
1735 void
1736 sp_desktop_widget_toggle_scrollbars (SPDesktopWidget *dtw)
1737 {
1738 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1739 if (GTK_WIDGET_VISIBLE (dtw->hscrollbar)) {
1740 gtk_widget_hide_all (dtw->hscrollbar);
1741 gtk_widget_hide_all (dtw->vscrollbar_box);
1742 gtk_widget_hide_all( dtw->cms_adjust );
1743 prefs->setBool(dtw->desktop->is_fullscreen() ? "/fullscreen/scrollbars/state" : "/window/scrollbars/state", false);
1744 } else {
1745 gtk_widget_show_all (dtw->hscrollbar);
1746 gtk_widget_show_all (dtw->vscrollbar_box);
1747 gtk_widget_show_all( dtw->cms_adjust );
1748 prefs->setBool(dtw->desktop->is_fullscreen() ? "/fullscreen/scrollbars/state" : "/window/scrollbars/state", true);
1749 }
1750 }
1752 void sp_desktop_widget_toggle_color_prof_adj( SPDesktopWidget *dtw )
1753 {
1755 if ( GTK_WIDGET_SENSITIVE( dtw->cms_adjust ) ) {
1756 if ( SP_BUTTON_IS_DOWN(dtw->cms_adjust) ) {
1757 sp_button_toggle_set_down( SP_BUTTON(dtw->cms_adjust), FALSE );
1758 } else {
1759 sp_button_toggle_set_down( SP_BUTTON(dtw->cms_adjust), TRUE );
1760 }
1761 }
1762 }
1764 /* Unused
1765 void
1766 sp_spw_toggle_menubar (SPDesktopWidget *dtw, bool is_fullscreen)
1767 {
1768 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1769 if (GTK_WIDGET_VISIBLE (dtw->menubar)) {
1770 gtk_widget_hide_all (dtw->menubar);
1771 prefs->setBool(is_fullscreen ? "/fullscreen/menu/state" : "/window/menu/state", false);
1772 } else {
1773 gtk_widget_show_all (dtw->menubar);
1774 prefs->setBool(is_fullscreen ? "/fullscreen/menu/state" : "/window/menu/state", true);
1775 }
1776 }
1777 */
1779 static void
1780 set_adjustment (GtkAdjustment *adj, double l, double u, double ps, double si, double pi)
1781 {
1782 if ((l != adj->lower) ||
1783 (u != adj->upper) ||
1784 (ps != adj->page_size) ||
1785 (si != adj->step_increment) ||
1786 (pi != adj->page_increment)) {
1787 adj->lower = l;
1788 adj->upper = u;
1789 adj->page_size = ps;
1790 adj->step_increment = si;
1791 adj->page_increment = pi;
1792 gtk_adjustment_changed (adj);
1793 }
1794 }
1796 void
1797 sp_desktop_widget_update_scrollbars (SPDesktopWidget *dtw, double scale)
1798 {
1799 if (!dtw) return;
1800 if (dtw->update) return;
1801 dtw->update = 1;
1803 /* The desktop region we always show unconditionally */
1804 SPDocument *doc = dtw->desktop->doc();
1805 Geom::Rect darea ( Geom::Point(-doc->getWidth(), -doc->getHeight()),
1806 Geom::Point(2 * doc->getWidth(), 2 * doc->getHeight()) );
1807 SPObject* root = doc->root;
1808 SPItem* item = SP_ITEM(root);
1809 Geom::OptRect deskarea = Geom::unify(darea, item->getBboxDesktop());
1811 /* Canvas region we always show unconditionally */
1812 Geom::Rect carea( Geom::Point(deskarea->min()[Geom::X] * scale - 64, deskarea->max()[Geom::Y] * -scale - 64),
1813 Geom::Point(deskarea->max()[Geom::X] * scale + 64, deskarea->min()[Geom::Y] * -scale + 64) );
1815 Geom::Rect viewbox = dtw->canvas->getViewbox();
1817 /* Viewbox is always included into scrollable region */
1818 carea = Geom::unify(carea, viewbox);
1820 set_adjustment(dtw->hadj, carea.min()[Geom::X], carea.max()[Geom::X],
1821 viewbox.dimensions()[Geom::X],
1822 0.1 * viewbox.dimensions()[Geom::X],
1823 viewbox.dimensions()[Geom::X]);
1824 gtk_adjustment_set_value(dtw->hadj, viewbox.min()[Geom::X]);
1826 set_adjustment(dtw->vadj, carea.min()[Geom::Y], carea.max()[Geom::Y],
1827 viewbox.dimensions()[Geom::Y],
1828 0.1 * viewbox.dimensions()[Geom::Y],
1829 viewbox.dimensions()[Geom::Y]);
1830 gtk_adjustment_set_value(dtw->vadj, viewbox.min()[Geom::Y]);
1832 dtw->update = 0;
1833 }
1836 /*
1837 Local Variables:
1838 mode:c++
1839 c-file-style:"stroustrup"
1840 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1841 indent-tabs-mode:nil
1842 fill-column:99
1843 End:
1844 */
1845 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :