/*
* Editable view implementation
*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* MenTaLguY <mental@rydia.net>
* bulia byak <buliabyak@users.sf.net>
* Ralf Stephan <ralf@ark.in-berlin.de>
* John Bintz <jcoswell@coswellproductions.org>
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
*
* 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
* Copyright (C) 2000-2001 Ximian, Inc.
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ui/dialog/dialog-manager.h"
#include "ui/tools/box3d-tool.h"
#include "color.h"
#include "desktop-events.h"
#include "desktop.h"
#include "desktop-style.h"
#include "device-manager.h"
#include "display/canvas-arena.h"
#include "display/canvas-grid.h"
#include "display/canvas-temporary-item-list.h"
#include "display/drawing-group.h"
#include "display/gnome-canvas-acetate.h"
#include "display/snap-indicator.h"
#include "display/sodipodi-ctrlrect.h"
#include "display/sp-canvas-group.h"
#include "display/sp-canvas.h"
#include "display/sp-canvas-util.h"
#include "document.h"
#include "document-undo.h"
#include "event-log.h"
#include "helper/action-context.h"
#include "ui/interface.h"
#include "layer-fns.h"
#include "layer-manager.h"
#include "layer-model.h"
#include "macros.h"
#include "message-context.h"
#include "message-stack.h"
#include "preferences.h"
#include "resource-manager.h"
#include "ui/tools/select-tool.h"
#include "selection.h"
#include "sp-item-group.h"
#include "sp-item-group.h"
#include "sp-namedview.h"
#include "sp-root.h"
#include "sp-defs.h"
#include "ui/tool-factory.h"
#include "widgets/desktop-widget.h"
// TODO those includes are only for node tool quick zoom. Remove them after fixing it.
#include "ui/tools/node-tool.h"
#include "ui/tool/control-point-selection.h"
// Callback declarations
static gint _arena_handler (SPCanvasArena *arena, Inkscape::DrawingItem *ai, GdkEvent *event, SPDesktop *desktop);
event_context( NULL ),
layer_manager( NULL ),
snapindicator( NULL ),
page_border( NULL ),
_focusMode(false),
dkey( 0 ),
number( 0 ),
window_state(0),
waiting_cursor( false ),
showing_dialogs ( false ),
guides_active( false ),
gr_point_i( 0 ),
_reconstruction_old_layer_id(), // an id attribute is not allowed to be the empty string
_active( false ),
_w2d(),
_d2w(),
_image_render_observer(this, "/options/rendering/imageinoutlinemode"),
grids_visible( false )
{
_d2w.setIdentity();
_w2d.setIdentity();
}
void
SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
{
// Temporary workaround for link order issues:
_guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
/* XXX:
* ensureUpToDate() sends a 'modified' signal to the root element.
* This is reportedly required to prevent flickering after the document
* loads. However, many SPObjects write to their repr in response
* to this signal. This is apparently done to support live path effects,
* which rewrite their result paths after each modification of the base object.
* This causes the generation of an incomplete undo transaction,
* which causes problems down the line, including crashes in the
* Undo History dialog.
*
* For now, this is handled by disabling undo tracking during this call.
* A proper fix would involve modifying the way ensureUpToDate() works,
* so that the LPE results are not rewritten.
*/
/* Setup Dialog Manager */
/* Connect display key to layer model */
/* Connect document */
/* Setup Canvas */
/* Setup adminstrative layers */
/* This is the background the page sits on. */
SP_CANVAS_ARENA (drawing)->drawing.delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
// Start in outline mode
} else {
// Start in normal mode, default
}
// The order in which these canvas items are added determines the z-order. It's therefore
// important to add the tempgroup (which will contain the snapindicator) before adding the
// controls. Only this way one will be able to quickly (before the snap indicator has
// disappeared) reselect a node after snapping it. If the z-order is wrong however, this
// will not work (the snap indicator is on top of the node handler; is the snapindicator
// being selected? or does it intercept some of the events that should have gone to the
// node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
// Set the select tool as the active tool.
// display rect and zoom are now handled in sp_desktop_widget_realize()
/* the following sets the page shadow on the canvas
It was originally set to 5, which is really cheesy!
It now is an attribute in the document's namedview. If a value of
0 is used, then the constructor for a shadow is not initialized.
*/
}
/* Connect event for page resize */
_modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
dkey,
if (ai) {
}
/* Ugly hack */
activate_guides (true);
/* Ugly hack */
/* Set up notification of rebuilding the document, this allows
for saving object related settings in the document. */
// ?
// sp_active_desktop_set (desktop);
this
)
);
this
)
);
this
)
);
this
)
);
/* setup LayerManager */
// (Setting up after the connections are all in place, as it may use some of them)
}
{
_destroy_signal.emit(this);
if (snapindicator) {
delete snapindicator;
}
if (temporary_item_list) {
delete temporary_item_list;
}
if (selection) {
delete selection;
}
g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
if (event_context) {
event_context->finish();
delete event_context;
}
delete layers;
if (layer_manager) {
delete layer_manager;
}
if (drawing) {
}
delete _guides_message_context;
}
{
}
return event_context;
}
return selection;
}
return doc();
}
}
return acetate;
}
return main;
}
return gridgroup;
}
return guides;
}
return drawing;
}
return sketch;
}
return controls;
}
return tempgroup;
}
return messageStack();
}
return namedview;
}
//--------------------------------------------------------------------
/* Public methods */
/* 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.
*/
{
if (move_to_bottom) {
}
}
/** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
*/
void
{
// 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) {
}
}
}
}
// reload grayscale matrix from prefs
r, g, b, 0, 0,
r, g, b, 0, 0,
0, 0, 0, 1, 0 };
}
}
switch (_display_mode) {
case Inkscape::RENDERMODE_NORMAL:
break;
case Inkscape::RENDERMODE_NO_FILTERS:
break;
case Inkscape::RENDERMODE_OUTLINE:
break;
default:
}
}
switch (_display_color_mode) {
case Inkscape::COLORMODE_NORMAL:
break;
case Inkscape::COLORMODE_GRAYSCALE:
break;
// case Inkscape::COLORMODE_PRINT_COLORS_PREVIEW:
default:
}
}
// Pass-through LayerModel functions
{
return layers->currentRoot();
}
{
return layers->currentLayer();
}
{
}
{
}
{
}
{
}
{
}
{
}
/**
* True if desktop viewport intersects \a item's bbox.
*/
{
if (bbox) {
} else {
return false;
}
}
///
}
/**
* Set activate property of desktop; emit signal if changed.
*/
void
{
if (new_active != _active) {
if (new_active) {
} else {
}
}
}
/**
* Set activate status of current desktop's named view.
*/
void
{
}
/**
* Make desktop switch documents.
*/
void
{
/* unselect everything before switching documents */
/* update the rulers, connect the desktop widget's signal to the new namedview etc.
(this can probably be done in a better way) */
if (dtw) {
dtw->updateNamedview();
}
}
/**
* Replaces the currently active tool with a new one.
*/
{
if (old_tool) {
//g_message("Old tool: %s", old_tool->pref_observer->observed_path.c_str());
//g_message("New tool: %s", toolName.c_str());
delete old_tool;
} else {
return;
}
}
// Make sure no delayed snapping events are carried over after switching tools
// (this is only an additional safety measure against sloppy coding, because each
// tool should take care of this by itself)
}
/**
* Sets the coordinate status to a given point
*/
void
}
}
/**
* \see SPDocument::getItemFromListAtPointBottom()
*/
SPItem *SPDesktop::getItemFromListAtPointBottom(const std::vector<SPItem*> &list, Geom::Point const &p) const
{
}
/**
* \see SPDocument::getItemAtPoint()
*/
{
}
/**
* \see SPDocument::getGroupAtPoint()
*/
{
}
/**
* Returns the mouse point in document coordinates; if mouse is
* outside the canvas, returns the center of canvas viewpoint.
*/
{
{
return p;
} else {
}
}
/**
* Put current zoom data in history list.
*/
void
{
}
}
/**
* Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
*/
void
{
bool zoomChanged = false;
// save the zoom
if (log) {
// if we do a logged zoom, our zoom-forward list is invalidated, so delete it
}
// FIXME: This 2geom idiom doesn't allow us to declare dbox const
double newscale;
} else {
}
newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
// zoom changed - set new zoom factors
zoomChanged = true;
}
/* Calculate top left corner (in document pixels) */
// Scroll
/* 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);
if (SP_IS_BOX3D_CONTEXT(event_context)) {
}
_widget->updateRulers();
_widget->updateZoom();
if ( zoomChanged ) {
}
}
{
}
/**
* Return viewbox dimensions.
*/
{
/// @fixme hardcoded desktop transform
}
/**
* Revert back to previous zoom if possible.
*/
void
{
if (zooms_past.empty()) {
return;
}
// push current zoom into forward zooms list
// restore previous zoom
// remove the just-added zoom from the past zooms list
}
/**
* Set zoom to next in list.
*/
{
if (zooms_future.empty()) {
return;
}
// push current zoom into past zooms list
// restore next zoom
// remove the just-used zoom from the zooms_future list
}
/**
* Performs a quick zoom into what the user is working on.
*
* @param enable Whether we're going in or out of quick zoom.
*/
{
if (enable == _quick_zoom_enabled) {
return;
}
if (enable == true) {
bool zoomed = false;
// TODO This needs to migrate into the node tool, but currently the design
// of this method is sufficiently wrong to prevent this.
// do not zoom if a single cusp node is selected aand the bounds
// have zero area.
set_display_area(nodes, true);
zoomed = true;
}
}
}
if (!zoomed) {
set_display_area(*d, true);
zoomed = true;
}
}
if (!zoomed) {
zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
}
} else {
set_display_area(_quick_zoom_stored_area, false);
}
return;
}
/**
* Zoom to point with absolute zoom factor.
*/
void
{
// maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
// this check prevents "sliding" when trying to zoom in at maximum zoom;
/// \todo someone please fix calculations properly and remove this hack
if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
return;
0.0);
}
/**
* Apply the desktop's current style or the tool style to the object.
*/
void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text)
{
} else {
}
if (css_current) {
}
}
/**
* Zoom to center with absolute zoom factor.
*/
void
{
}
/**
* Zoom to point with relative zoom factor.
*/
void
{
}
}
}
}
}
/**
* Zoom to center with relative zoom factor.
*/
void
{
}
/**
* Set display area to origin and current document dimensions.
*/
void
{
if (d.minExtent() < 1.0) {
return;
}
set_display_area(d, 10);
}
/**
* Set display area to current document width.
*/
void
{
return;
}
set_display_area(d, 10);
}
/**
* Zoom to selection.
*/
void
{
if ( !d || d->minExtent() < 0.1 ) {
return;
}
set_display_area(*d, 10);
}
/**
* Tell widget to let zoom widget grab keyboard focus.
*/
void
{
}
/**
* Zoom to whole drawing.
*/
void
{
/* Note that the second condition here indicates that
** there are no items in the drawing.
*/
if ( !d || d->minExtent() < 0.1 ) {
return;
}
set_display_area(*d, 10);
}
/**
* Scroll canvas by specific coordinate amount in svg coordinates.
*/
void
{
}
/**
* Scroll canvas by specific coordinate amount.
*/
void
{
/* 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);
if (SP_IS_BOX3D_CONTEXT(event_context)) {
}
_widget->updateRulers();
}
bool
{
using Geom::X;
using Geom::Y;
gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
// autoscrolldistance is in screen pixels, but the display area is in document units
// FIXME: This 2geom idiom doesn't allow us to declare dbox const
else
x_to = p[X];
else
y_to = p[Y];
if (autoscrollspeed == 0)
if (autoscrollspeed != 0)
return true;
}
return false;
}
bool
{
return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
}
void
{
_widget->setIconified();
}
bool
{
return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
}
void
{
_widget->setMaximized();
}
bool
{
return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
}
void
{
_widget->setFullscreen();
}
/**
* Checks to see if the user is working in focused mode.
*
* @return the value of \c _focusMode.
*/
{
return _focusMode;
}
/**
* Changes whether the user is in focus mode or not.
*
* @param mode Which mode the view should be in.
*/
{
if (mode == _focusMode) { return; }
_focusMode = mode;
layoutWidget();
//sp_desktop_widget_layout(SPDesktopWidget);
return;
}
void
{
_widget->getGeometry (x, y, w, h);
}
void
{
_widget->setPosition (p);
}
void
{
}
void
{
}
{
}
void
{
}
{
}
bool
{
}
void
{
_widget->toggleRulers();
}
void
{
}
{
layoutWidget();
}
void
{
}
void
{
}
bool
{
}
{
if(shutdown())
return true;
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
{
// Record the desktop window's state
// Layout may differ depending on full-screen mode or not
layoutWidget();
}
return false;
}
void
{
}
void
{
}
void
{
}
bool
{
}
void
{
INKSCAPE.subselection_changed (this);
}
{
}
void
{
}
{
}
{
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
// GDK needs the flush for the cursor change to take effect
gdk_flush();
waiting_cursor = true;
}
if (waiting_cursor) {
this->event_context->sp_event_context_update_cursor();
}
}
{
}
{
}
{
return _widget->colorProfAdjustEnabled();
}
{
if(gridgroup) {
}
} else {
//there is no grid present at the moment. add a rectangular grid and make it visible
showGrids(true);
}
}
{
if (show) {
} else {
}
}
{
bool v = namedview->getSnapGlobal();
namedview->setSnapGlobal(!v);
}
//----------------------------------------------------------------------
// Callback implementations. The virtual ones are connected by the view.
void
{
// Nothing called here
}
/**
* Redraw callback; queues Gtk redraw; connected by View.
*/
void
{
if (main) {
}
}
void
{
}
/**
* Associate document with desktop.
*/
void
{
if (!doc) return;
if (this->doc()) {
}
// remove old EventLog if it exists (see also: bug #1071082)
if (event_log) {
delete event_log;
event_log = 0;
}
/* setup EventLog */
/// \todo fixme: This condition exists to make sure the code
/// 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) {
_modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
dkey,
if (ai) {
}
/* Ugly hack */
activate_guides (true);
/* Ugly hack */
}
}
void
{
if (_widget) {
}
}
void
{
}
/**
* Resized callback.
*/
void
{
}
void
{
}
void
{
}
void
{
}
static void
{
/** \todo
* only change the layer for single selections, or what?
* This seems reasonable -- for multiple selections there can be many
* different layers involved.
*/
if (item) {
}
}
}
/**
* Calls event handler of current event context.
* \param arena Unused
* \todo fixme
*/
static gint
_arena_handler (SPCanvasArena */*arena*/, Inkscape::DrawingItem *ai, GdkEvent *event, SPDesktop *desktop)
{
if (ai) {
} else {
}
}
static void
}
/// Callback
static void
}
/// Callback
static void
{
}
/// Called when document is starting to be rebuilt.
{
desktop->_reconstruction_old_layer_id = desktop->currentLayer()->getId() ? desktop->currentLayer()->getId() : "";
}
/// Called when document rebuild is finished.
{
g_debug("Desktop, finishing reconstruction\n");
SPObject * newLayer = desktop->namedview->document->getObjectById(desktop->_reconstruction_old_layer_id);
}
}
g_debug("Desktop, finishing reconstruction end\n");
}
/**
* Namedview_modified callback.
*/
{
if (flags & SP_OBJECT_MODIFIED_FLAG) {
if (nv->pagecheckerboard) {
} else {
}
if (nv->showborder) {
// show
// set color and shadow
if (nv->pageshadow) {
}
// place in the z-order stack
} else {
}
} else {
if (nv->pageshadow) {
}
}
} else {
}
// the background color is light, use black outline
SP_CANVAS_ARENA (desktop->drawing)->drawing.outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
} else { // use white outline
SP_CANVAS_ARENA (desktop->drawing)->drawing.outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
}
}
}
{
return _w2d;
}
{
return p * _w2d;
}
{
return p * _d2w;
}
{
return _doc2dt;
}
{
}
{
return p * _doc2dt;
}
{
return p * dt2doc();
}
void
{
return;
}
if (active == 0) {
// User has turned off this feature in preferences
return;
}
if (showing_dialogs) {
return;
}
/*
* Get each dialogs previous state from preferences and reopen on startup if needed, without grabbing focus (canvas retains focus).
* Map dialog manager's dialog IDs to dialog last visible state preference. FIXME: store this correspondence in dialogs themselves!
*/
for (std::map<Glib::ustring, Glib::ustring>::const_iterator iter = mapVerbPreference.begin(); iter != mapVerbPreference.end(); ++iter) {
if (visible) {
_dlg_mgr->showDialog(iter->first.c_str(), false); // without grabbing focus, we need focus to remain on the canvas
}
}
}
/*
* Pop event context from desktop's context stack. Never used.
*/
// void
// SPDesktop::pop_event_context (unsigned int key)
// {
// ToolBase *ec = NULL;
//
// if (event_context && event_context->key == key) {
// g_return_if_fail (event_context);
// g_return_if_fail (event_context->next);
// ec = event_context;
// sp_event_context_deactivate (ec);
// event_context = ec->next;
// sp_event_context_activate (event_context);
// _event_context_changed_signal.emit (this, ec);
// }
//
// ToolBase *ref = event_context;
// while (ref && ref->next && ref->next->key != key)
// ref = ref->next;
//
// if (ref && ref->next) {
// ec = ref->next;
// ref->next = ec->next;
// }
//
// if (ec) {
// sp_event_context_finish (ec);
// g_object_unref (G_OBJECT (ec));
// }
// }
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :