desktop.cpp revision 5cacbbec22b42a0480f396eb94bcfb5f746ac68a
/*
* 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 "box3d-context.h"
#include "color.h"
#include "desktop-events.h"
#include "desktop.h"
#include "desktop-handles.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 "event-log.h"
#include "inkscape-private.h"
#include "layer-fns.h"
#include "layer-manager.h"
#include "macros.h"
#include "message-context.h"
#include "message-stack.h"
#include "object-hierarchy.h"
#include "preferences.h"
#include "resource-manager.h"
#include "select-context.h"
#include "selection.h"
#include "sp-item-group.h"
#include "sp-item-group.h"
#include "sp-namedview.h"
#include "sp-root.h"
#include "ui/dialog/dialog-manager.h"
#include "widgets/desktop-widget.h"
// TODO those includes are only for node tool quick zoom. Remove them after fixing it.
#include "ui/tool/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);
_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 ),
_focusMode(false),
dkey( 0 ),
number( 0 ),
window_state(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(), // an id attribute is not allowed to be the empty string
_widget( 0 ),
_inkscape( 0 ),
_guides_message_context( 0 ),
_active( false ),
_w2d(),
_d2w(),
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()));
/* Kill flicker */
/* Setup Dialog Manager */
/* Connect document */
/* Setup Canvas */
/* Setup adminstrative layers */
SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
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)
/* Push select tool to the bottom of stack */
/** \todo
* FIXME: this is the only call to this. Everything else seems to just
* call "set" instead of "push". Can we assume that there is only one
* context ever?
*/
// 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);
while (event_context) {
}
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;
}
if (_inkscape) {
}
if (drawing) {
}
delete _guides_message_context;
}
{
}
//--------------------------------------------------------------------
/* 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) {
}
}
}
}
void SPDesktop::displayModeToggle() {
switch (_display_mode) {
case Inkscape::RENDERMODE_NORMAL:
break;
case Inkscape::RENDERMODE_NO_FILTERS:
break;
case Inkscape::RENDERMODE_OUTLINE:
break;
default:
}
}
void SPDesktop::displayColorModeToggle() {
switch (_display_color_mode) {
case Inkscape::COLORMODE_NORMAL:
break;
case Inkscape::COLORMODE_GRAYSCALE:
break;
// case Inkscape::COLORMODE_PRINT_COLORS_PREVIEW:
default:
}
}
/**
* Returns current root (=bottom) layer.
*/
{
}
/**
* Returns current top layer.
*/
{
}
/**
* Sets the current layer of the desktop.
*
* Make \a object the top layer.
*/
g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
// printf("Set Layer to ID: %s\n", object->getId());
}
g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
bool othersShowing = false;
for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
}
for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
}
}
}
}
/**
* Return layer that contains \a object.
*/
}
return object;
}
/**
* True if object is a layer.
*/
return ( SP_IS_GROUP(object)
}
/**
* 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();
}
/**
* Make desktop switch event contexts.
*/
void
{
while (event_context) {
ec = event_context;
// we have to keep event_context valid during destruction - otherwise writing
// destructors is next to impossible
}
// The event_context will be null. This means that it will be impossible
// to process any event invoked by the lines below. See for example bug
// LP #622350. Cutting and undoing again in the node tool resets the event
// context to the node tool. In this bug the line bellow invokes GDK_LEAVE_NOTIFY
// events which cannot be handled and must be discarded.
event_context = ec;
// Now the event_context has been set again and we can process all events again
}
/**
* Push event context onto desktop's context stack.
*/
void
{
ref = event_context;
}
event_context = ec;
}
/**
* Sets the coordinate status to a given point
*/
void
}
/**
* \see SPDocument::getItemFromListAtPointBottom()
*/
{
}
/**
* \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) */
_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);
zoomed = true;
}
} 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) */
_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.
*/
bool SPDesktop::is_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
{
}
SPDesktop::getToplevel( )
{
}
void
{
}
{
}
bool
{
}
void
{
_widget->toggleRulers();
}
void
{
}
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
{
}
void SPDesktop::disableInteraction()
{
}
void SPDesktop::setWaitingCursor()
{
// GDK needs the flush for the cursor change to take effect
gdk_flush();
waiting_cursor = true;
}
void SPDesktop::clearWaitingCursor()
{
if (waiting_cursor)
}
void SPDesktop::toggleColorProfAdjust()
{
}
void SPDesktop::toggleGrids()
{
if(gridgroup) {
}
} else {
//there is no grid present at the moment. add a rectangular grid and make it visible
showGrids(true);
}
}
{
if (show) {
} else {
}
}
void SPDesktop::toggleSnapGlobal()
{
bool v = namedview->getSnapGlobal();
namedview->setSnapGlobal(!v);
}
//----------------------------------------------------------------------
// Callback implementations. The virtual ones are connected by the view.
void
SPDesktop::onPositionSet (double x, double y)
{
}
void
{
// Nothing called here
}
/**
* Redraw callback; queues Gtk redraw; connected by View.
*/
void
{
if (main) {
}
}
void
{
}
/**
* Associate document with desktop.
*/
void
{
}
if (_layer_hierarchy) {
delete _layer_hierarchy;
}
/* 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.
{
// printf("Desktop, starting reconstruction\n");
desktop->_reconstruction_old_layer_id = desktop->currentLayer()->getId() ? desktop->currentLayer()->getId() : "";
/*
GSList const * selection_objs = desktop->selection->list();
for (; selection_objs != NULL; selection_objs = selection_objs->next) {
}
*/
// printf("Desktop, starting reconstruction end\n");
}
/// Called when document rebuild is finished.
{
// printf("Desktop, finishing reconstruction\n");
SPObject * newLayer = desktop->namedview->document->getObjectById(desktop->_reconstruction_old_layer_id);
}
// printf("Desktop, finishing reconstruction end\n");
}
}
/**
* Namedview_modified callback.
*/
{
if (flags & SP_OBJECT_MODIFIED_FLAG) {
} 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();
}
/*
* Pop event context from desktop's context stack. Never used.
*/
// void
// SPDesktop::pop_event_context (unsigned int key)
// {
// SPEventContext *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);
// }
//
// SPEventContext *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 :