diff --git a/src/desktop.cpp b/src/desktop.cpp
index d159fbc78d236df8f3996087c59092ea28bfc74f..932d79ff2e7dadfe24d1c6e3cee85e5db55c0d79 100644 (file)
--- a/src/desktop.cpp
+++ b/src/desktop.cpp
* John Bintz <jcoswell@coswellproductions.org>
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
*
- * Copyright (C) 2006-2007 Johan Engelen
+ * Copyright (C) 2007 Jon A. Cruz
+ * Copyright (C) 2006-2008 Johan Engelen
* Copyright (C) 2006 John Bintz
* Copyright (C) 2004 MenTaLguY
* Copyright (C) 1999-2002 Lauris Kaplinski
#include <glibmm/i18n.h>
#include <sigc++/functors/mem_fun.h>
+#include <gtkmm.h>
#include "macros.h"
#include "inkscape-private.h"
#include "display/gnome-canvas-acetate.h"
#include "display/sodipodi-ctrlrect.h"
#include "display/sp-canvas-util.h"
+#include "display/canvas-temporary-item-list.h"
+#include "display/snap-indicator.h"
#include "libnr/nr-matrix-div.h"
#include "libnr/nr-rect-ops.h"
#include "ui/dialog/dialog-manager.h"
#include "xml/repr.h"
#include "message-context.h"
+#include "device-manager.h"
+#include "layer-fns.h"
#include "layer-manager.h"
#include "event-log.h"
+#include "display/canvas-grid.h"
+#include "widgets/desktop-widget.h"
+#include "box3d-context.h"
+
+#include "display/sp-canvas.h"
namespace Inkscape { namespace XML { class Node; }}
* \pre namedview != NULL.
* \pre canvas != NULL.
*/
-SPDesktop::SPDesktop()
-{
- _dlg_mgr = NULL;
- _widget = 0;
- namedview = NULL;
- selection = NULL;
- acetate = NULL;
- main = NULL;
- grid = NULL;
- guides = NULL;
- drawing = NULL;
- sketch = NULL;
- controls = NULL;
- event_context = 0;
- layer_manager = 0;
-
+SPDesktop::SPDesktop() :
+ _dlg_mgr( 0 ),
+ namedview( 0 ),
+ canvas( 0 ),
+ selection( 0 ),
+ event_context( 0 ),
+ layer_manager( 0 ),
+ event_log( 0 ),
+ temporary_item_list( 0 ),
+ snapindicator( 0 ),
+ acetate( 0 ),
+ main( 0 ),
+ gridgroup( 0 ),
+ guides( 0 ),
+ drawing( 0 ),
+ sketch( 0 ),
+ controls( 0 ),
+ tempgroup ( 0 ),
+ table( 0 ),
+ page( 0 ),
+ page_border( 0 ),
+ current( 0 ),
+ zooms_past( 0 ),
+ zooms_future( 0 ),
+ dkey( 0 ),
+ number( 0 ),
+ window_state(0),
+ interaction_disabled_counter( 0 ),
+ waiting_cursor( false ),
+ guides_active( false ),
+ gr_item( 0 ),
+ gr_point_type( 0 ),
+ gr_point_i( 0 ),
+ gr_fill_or_stroke( true ),
+ _layer_hierarchy( 0 ),
+ _reconstruction_old_layer_id( 0 ),
+ _display_mode(Inkscape::RENDERMODE_NORMAL),
+ _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
+ _widget( 0 ),
+ _inkscape( 0 ),
+ _guides_message_context( 0 ),
+ _active( false ),
+ _w2d(),
+ _d2w(),
+ _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
+ grids_visible( false )
+{
_d2w.set_identity();
_w2d.set_identity();
- _doc2dt = NR::Matrix(NR::scale(1, -1));
-
- guides_active = false;
-
- zooms_past = NULL;
- zooms_future = NULL;
- is_fullscreen = false;
-
- gr_item = NULL;
- gr_point_type = 0;
- gr_point_i = 0;
- gr_fill_or_stroke = true;
-
- _layer_hierarchy = NULL;
- _active = false;
-
- selection = Inkscape::GC::release (new Inkscape::Selection (this));
+ selection = Inkscape::GC::release( new Inkscape::Selection(this) );
}
void
SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
-
{
+ // Temporary workaround for link order issues:
+ Inkscape::DeviceManager::getManager().getDevices();
+
_guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
sp_document_ensure_up_to_date (document);
/* Setup Dialog Manager */
- _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
+ _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
dkey = sp_item_display_key_new (1);
setDisplayModeNormal();
}
- grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
+ gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
+ tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
/* Push select tool to the bottom of stack */
/** \todo
_reconstruction_finish_connection =
document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
_reconstruction_old_layer_id = NULL;
-
- _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
-
+
// ?
// sp_active_desktop_set (desktop);
_inkscape = INKSCAPE;
/* setup LayerManager */
// (Setting up after the connections are all in place, as it may use some of them)
layer_manager = new Inkscape::LayerManager( this );
+
+ showGrids(namedview->grids_visible, false);
+
+ temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
+ snapindicator = new Inkscape::Display::SnapIndicator ( this );
}
void SPDesktop::destroy()
{
+ if (snapindicator) {
+ delete snapindicator;
+ snapindicator = NULL;
+ }
+ if (temporary_item_list) {
+ delete temporary_item_list;
+ temporary_item_list = NULL;
+ }
+
+ namedview->hide(this);
+
_activate_connection.disconnect();
_deactivate_connection.disconnect();
_sel_modified_connection.disconnect();
if (_layer_hierarchy) {
delete _layer_hierarchy;
+// _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
+ }
+
+ if (layer_manager) {
+ delete layer_manager;
+ layer_manager = NULL;
}
if (_inkscape) {
//--------------------------------------------------------------------
/* Public methods */
-void SPDesktop::setDisplayModeNormal()
+
+/* These methods help for temporarily showing things on-canvas.
+ * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
+ * is when you want to prematurely remove the item from the canvas, by calling
+ * desktop->remove_temporary_canvasitem(tempitem).
+ */
+/** Note that lifetime is measured in milliseconds
+ * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
+ * delete the object for you and the reference will become invalid without you knowing it.
+ * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
+ * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
+ * because the object might be deleted already without you knowing it.
+ * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
+ */
+Inkscape::Display::TemporaryItem *
+SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
{
- SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
- canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
- displayMode = RENDERMODE_NORMAL;
- sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
- _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
+ if (move_to_bottom) {
+ sp_canvas_item_move_to_z(item, 0);
+ }
+
+ return temporary_item_list->add_item(item, lifetime);
}
-void SPDesktop::setDisplayModeOutline()
+/** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
+*/
+void
+SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
{
- SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
- canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
- displayMode = RENDERMODE_OUTLINE;
+ // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
+ if (tempitem && temporary_item_list) {
+ temporary_item_list->delete_item(tempitem);
+ }
+}
+
+void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
+ SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
+ canvas->rendermode = mode;
+ _display_mode = mode;
+ if (mode != Inkscape::RENDERMODE_OUTLINE) {
+ _saved_display_mode = _display_mode;
+ }
sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
_widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
}
-void SPDesktop::displayModeToggle()
-{
- if (displayMode == RENDERMODE_OUTLINE)
- setDisplayModeNormal();
- else
- setDisplayModeOutline();
+void SPDesktop::displayModeToggle() {
+ if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
+ _setDisplayMode(_saved_display_mode);
+ } else {
+ _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
+ }
}
/**
/**
* Sets the current layer of the desktop.
- *
+ *
* Make \a object the top layer.
*/
void SPDesktop::setCurrentLayer(SPObject *object) {
_layer_hierarchy->setBottom(object);
}
+void SPDesktop::toggleLayerSolo(SPObject *object) {
+ g_return_if_fail(SP_IS_GROUP(object));
+ g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
+
+ bool othersShowing = false;
+ std::vector<SPObject*> layers;
+ for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
+ layers.push_back(obj);
+ othersShowing |= !SP_ITEM(obj)->isHidden();
+ }
+ for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
+ layers.push_back(obj);
+ othersShowing |= !SP_ITEM(obj)->isHidden();
+ }
+
+
+ if ( SP_ITEM(object)->isHidden() ) {
+ SP_ITEM(object)->setHidden(false);
+ }
+
+ for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
+ SP_ITEM(*it)->setHidden(othersShowing);
+ }
+}
+
/**
* Return layer that contains \a object.
*/
selection->clear();
setDocument (theDocument);
+
+ /* update the rulers, connect the desktop widget's signal to the new namedview etc.
+ (this can probably be done in a better way) */
+ Gtk::Window *parent = this->getToplevel();
+ g_assert(parent != NULL);
+ SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
+ if (dtw) dtw->desktop = this;
+ sp_desktop_widget_update_namedview(dtw);
+
_namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
_document_replaced_signal.emit (this, theDocument);
}
}
/**
- * Set viewbox.
+ * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
*/
void
SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
}
- newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
+ newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
int clear = FALSE;
if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
@@ -662,13 +773,16 @@ SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double
clear = TRUE;
}
- /* Calculate top left corner */
+ /* Calculate top left corner (in document pixels) */
x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
/* Scroll */
sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
+ /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
+ sp_box3d_context_update_lines(event_context);
+
_widget->updateRulers();
_widget->updateScrollbars(expansion(_d2w));
_widget->updateZoom();
NR::Rect d(NR::Point(0, 0),
NR::Point(sp_document_width(doc()), sp_document_height(doc())));
- if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
+ if (d.isEmpty(1.0)) {
return;
}
void
SPDesktop::zoom_selection()
{
- NR::Rect const d = selection->bounds();
+ NR::Maybe<NR::Rect> const d = selection->bounds();
- if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
+ if ( !d || d->isEmpty(0.1) ) {
return;
}
- set_display_area(d, 10);
+ set_display_area(*d, 10);
}
/**
/* Note that the second condition here indicates that
** there are no items in the drawing.
*/
- if ( !d || d->dimensions()[NR::X] < 1.0 || d->dimensions()[NR::Y] < 1.0 ) {
+ if ( !d || d->isEmpty(1.0) ) {
return;
}
set_display_area(*d, 10);
}
+/**
+ * Scroll canvas by specific coordinate amount in svg coordinates.
+ */
+void
+SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
+{
+ double scale = expansion(_d2w);
+ scroll_world(dx*scale, dy*scale, is_scrolling);
+}
+
/**
* Scroll canvas by specific coordinate amount.
*/
sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
+ /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
+ sp_box3d_context_update_lines(event_context);
+
_widget->updateRulers();
_widget->updateScrollbars(expansion(_d2w));
}
return false;
}
+bool
+SPDesktop::is_iconified()
+{
+ return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
+}
+
+void
+SPDesktop::iconify()
+{
+ _widget->setIconified();
+}
+
+bool
+SPDesktop::is_maximized()
+{
+ return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
+}
+
+void
+SPDesktop::maximize()
+{
+ _widget->setMaximized();
+}
+
+bool
+SPDesktop::is_fullscreen()
+{
+ return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
+}
+
void
SPDesktop::fullscreen()
{
_widget->setTransient (p, transient_policy);
}
+Gtk::Window*
+SPDesktop::getToplevel( )
+{
+ return _widget->getWindow();
+}
+
void
SPDesktop::presentWindow()
{
return _widget->shutdown();
}
+bool SPDesktop::onDeleteUI (GdkEventAny*)
+{
+ if(shutdown())
+ return true;
+
+ destroyWidget();
+ return false;
+}
+
+/**
+ * onWindowStateEvent
+ *
+ * Called when the window changes its maximize/fullscreen/iconify/pinned state.
+ * Since GTK doesn't have a way to query this state information directly, we
+ * record it for the desktop here, and also possibly trigger a layout.
+ */
+bool
+SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
+{
+ // Record the desktop window's state
+ window_state = event->new_window_state;
+
+ // Layout may differ depending on full-screen mode or not
+ GdkWindowState changed = event->changed_mask;
+ if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
+ layoutWidget();
+ }
+
+ return false;
+}
+
void
SPDesktop::setToolboxFocusTo (gchar const *label)
{
_widget->setToolboxAdjustmentValue (id, val);
}
+void
+SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
+{
+ _widget->setToolboxSelectOneValue (id, val);
+}
+
bool
SPDesktop::isToolboxButtonActive (gchar const *id)
{
_widget->disableInteraction();
}
+void SPDesktop::setWaitingCursor()
+{
+ GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
+ gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
+ gdk_cursor_unref(waiting);
+ waiting_cursor = true;
+
+ // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
+ // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
+ // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
+ // after the call to setWaitingCursor as it was before
+ while( Gtk::Main::events_pending() )
+ Gtk::Main::iteration();
+}
+
+void SPDesktop::clearWaitingCursor()
+{
+ if (waiting_cursor)
+ sp_event_context_update_cursor(sp_desktop_event_context(this));
+}
+
+void SPDesktop::toggleColorProfAdjust()
+{
+ _widget->toggleColorProfAdjust();
+}
+
+void SPDesktop::toggleGrids()
+{
+ if (namedview->grids) {
+ if(gridgroup) {
+ showGrids(!grids_visible);
+ }
+ } else {
+ //there is no grid present at the moment. add a rectangular grid and make it visible
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
+ Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
+ showGrids(true);
+ }
+}
+
+void SPDesktop::showGrids(bool show, bool dirty_document)
+{
+ grids_visible = show;
+ sp_namedview_show_grids(namedview, grids_visible, dirty_document);
+ if (show) {
+ sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
+ } else {
+ sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
+ }
+}
+
+void SPDesktop::toggleSnapping()
+{
+ bool v = namedview->snap_manager.getSnapEnabledGlobally();
+ Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
+ sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
+}
+
//----------------------------------------------------------------------
// Callback implementations. The virtual ones are connected by the view.
}
void
-SPDesktop::onResized (double x, double y)
+SPDesktop::onResized (double /*x*/, double /*y*/)
{
// Nothing called here
}
/**
* Associate document with desktop.
*/
-/// \todo fixme: refactor SPDesktop::init to use setDocument
void
SPDesktop::setDocument (SPDocument *doc)
{
_commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
/// \todo fixme: This condition exists to make sure the code
- /// inside is called only once on initialization. But there
+ /// inside is NOT called on initialization, only on replacement. But there
/// are surely more safe methods to accomplish this.
+ // TODO since the comment had reversed logic, check the intent of this block of code:
if (drawing) {
- NRArenaItem *ai;
+ NRArenaItem *ai = 0;
namedview = sp_document_namedview (doc, NULL);
_modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
void
SPDesktop::_onSelectionModified
-(Inkscape::Selection *selection, guint flags, SPDesktop *dt)
+(Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
{
if (!dt->_widget) return;
dt->_widget->updateScrollbars (expansion(dt->_d2w));
* \todo fixme
*/
static gint
-_arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
+_arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
{
if (ai) {
SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
/// Callback
static void
-_layer_hierarchy_changed(SPObject *top, SPObject *bottom,
+_layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
SPDesktop *desktop)
{
desktop->_layer_changed_signal.emit (bottom);
((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
}
}
-
+
/* Show/hide page shadow */
if (nv->showpageshadow && nv->pageshadow) {
((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
SPNamedView &nv = *desktop->namedview;
- nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
+ //tell all grid snappers
+ for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
+ Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
+ grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
*nv.gridtoleranceunit,
px));
- nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
- *nv.gridtoleranceunit,
- px));
- nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
+ }
+
+ nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
*nv.guidetoleranceunit,
px));
- nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
+ nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
*nv.objecttoleranceunit,
px));
}