control-point-selection.cpp revision 31f986be550e507d7643c59ea08b957933561111
/**
* @file
* Node selection - implementation.
*/
/* Authors:
* Krzysztof KosiĆski <tweenk.pl@gmail.com>
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "ui/tool/selectable-control-point.h"
#include "desktop.h"
#include "preferences.h"
#include "ui/tool/control-point-selection.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/transform-handle-set.h"
#include <gdk/gdkkeysyms.h>
namespace Inkscape {
namespace UI {
/**
* @class ControlPointSelection
* Group of selected control points.
*
* Some operations can be performed on all selected points regardless of their type, therefore
* this class is also a Manipulator. It handles the transformations of points using
* the keyboard.
*
* The exposed interface is similar to that of an STL set. Internally, a hash map is used.
* @todo Correct iterators (that don't expose the connection list)
*/
/** @var ControlPointSelection::signal_update
* Fires when the display needs to be updated to reflect changes.
*/
/** @var ControlPointSelection::signal_point_changed
* Fires when a control point is added to or removed from the selection.
* The first param contains a pointer to the control point that changed sel. state.
* The second says whether the point is currently selected.
*/
/** @var ControlPointSelection::signal_commit
* Fires when a change that needs to be committed to XML happens.
*/
: Manipulator(d)
, _dragging(false)
, _handles_visible(true)
, _one_node_handles(false)
{
true));
}
{
clear();
delete _handles;
}
/** Add a control point to the selection. */
{
}
x->updateState();
_pointChanged(x, true);
}
/** Remove a point from the selection. */
{
erased->updateState();
_pointChanged(erased, false);
}
{
return 1;
}
{
}
/** Remove all points from the selection, making it empty. */
void ControlPointSelection::clear()
{
erase(i++);
}
/** Select all points that this selection can contain. */
void ControlPointSelection::selectAll()
{
insert(*i);
}
}
/** Select all points inside the given rectangle (in desktop coordinates). */
{
if (r.contains(**i))
insert(*i);
}
}
/** Unselect all selected points and select all unselected points. */
void ControlPointSelection::invertSelection()
{
else insert(*i);
}
}
{
match = *i;
}
}
// use >= to also deselect the origin node when it's the last one selected
match = *i;
}
}
}
if (match) {
}
}
/** Transform all selected control points by the given affine transformation. */
{
SelectableControlPoint *cur = *i;
}
// TODO preserving the rotation radius needs some rethinking...
}
/** Align control points on the specified axis. */
{
if (empty()) return;
}
if (!bound) { return; }
}
}
/** Equdistantly distribute control points by moving them in the specified dimension. */
{
if (empty()) return;
// this needs to be a multimap, otherwise it will fail when some points have the same coord
// first we insert all points into a multimap keyed by the aligned coord to sort them
// simultaneously we compute the extent of selection
}
if (!bound) { return; }
// now we iterate over the multimap and set aligned positions.
unsigned num = 0;
}
}
/** Get the bounds of the selection.
* @return Smallest rectangle containing the positions of all selected points,
* or nothing if the selection is empty */
{
return _bounds;
}
{
}
{
_handles_visible = v;
_updateTransformHandles(false);
}
{
_handles->setVisible(false);
}
{
_updateTransformHandles(true);
}
{
if (size() == 1) {
}
} else {
}
}
{
_dragging = true;
double maxdist = 0;
m.setIdentity();
_farthest_point = *i;
}
}
}
{
double fdist = Geom::distance(_original_positions[_grabbed_point], _original_positions[_farthest_point]);
// Sculpting
SelectableControlPoint *cur = (*i);
trans.setIdentity();
if (dist != 0.0) {
// The sculpting transformation is not affine, but it can be
// locally approximated by one. Here we compute the local
// affine approximation of the sculpting transformation near
// the currently transformed point. We then transform the point
// by this approximation. This gives us sensible behavior for node handles.
// NOTE: probably it would be better to transform the node handles,
// but ControlPointSelection is supposed to work for any
// SelectableControlPoints, not only Nodes. We could create a specialized
// NodeSelection class that inherits from this one and move sculpting there.
if (itrans.isSingular())
} else {
}
//cur->move(_original_positions[cur] + abs_delta * deltafrac);
}
} else {
SelectableControlPoint *cur = (*i);
}
}
}
void ControlPointSelection::_pointUngrabbed()
{
_last_trans.clear();
_dragging = false;
}
{
// clicking a selected node should toggle the transform handles between rotate and scale mode,
// if they are visible
return true;
}
return false;
}
{
_updateTransformHandles(false);
if (_bounds) {
}
}
{
}
void ControlPointSelection::_updateBounds()
{
SelectableControlPoint *cur = (*i);
if (!_bounds) {
} else {
}
}
}
{
if (_dragging) return;
_handles->setVisible(true);
SelectableControlPoint *p = *begin();
_handles->setVisible(true);
} else {
_handles->setVisible(false);
}
}
/** Moves the selected points along the supplied unit vector according to
* the modifier state of the supplied event. */
{
if (held_control(event)) return false;
} else {
}
} else {
}
return true;
}
/**
* Computes the distance to the farthest corner of the bounding box.
* Used to determine what it means to "rotate by one pixel".
*/
{
double maxlen = 0;
for (unsigned i = 0; i < 4; ++i) {
}
return maxlen;
}
/**
* Rotates the selected points in the given direction according to the modifier state
* from the supplied event.
* @param event Key event to take modifier state from
* @param dir Direction of rotation (math convention: 1 = counterclockwise, -1 = clockwise)
*/
{
if (empty()) return false;
// rotate around the mouseovered point, or the selection's rotation center
// if nothing is mouseovered
double radius;
if (scp) {
if (!_mouseover_rot_radius) {
}
} else {
if (!_rot_radius) {
}
radius = *_rot_radius;
}
double angle;
// Rotate by "one pixel". We interpret this as rotating by an angle that causes
// the topmost point of a circle circumscribed about the selection's bounding box
// to move on an arc 1 screen pixel long.
} else {
}
// translate to origin, rotate, translate back to original position
transform(m);
return true;
}
{
if (empty()) return false;
if (scp) {
} else {
}
double length_change;
// of the bounding box.
} else {
length_change *= dir;
}
transform(m);
return true;
}
{
if (empty()) return false;
if (d == Geom::X) {
} else {
}
transform(m);
return true;
}
{
_updateTransformHandles(true);
}
bool ControlPointSelection::event(Inkscape::UI::Tools::ToolBase * /*event_context*/, GdkEvent *event)
{
// implement generic event handling that should apply for all control point selections here;
// for example, keyboard moves and transformations. This way this functionality doesn't need
// to be duplicated in many places
// Later split out so that it can be reused in object selection
case GDK_KEY_PRESS:
// do not handle key events if the selection is empty
if (empty()) break;
// moves
case GDK_KEY_Up:
case GDK_KEY_KP_Up:
case GDK_KEY_KP_8:
case GDK_KEY_Down:
case GDK_KEY_KP_Down:
case GDK_KEY_KP_2:
case GDK_KEY_Right:
case GDK_KEY_KP_Right:
case GDK_KEY_KP_6:
case GDK_KEY_Left:
case GDK_KEY_KP_Left:
case GDK_KEY_KP_4:
// rotates
case GDK_KEY_bracketleft:
case GDK_KEY_bracketright:
// scaling
case GDK_KEY_less:
case GDK_KEY_comma:
case GDK_KEY_greater:
case GDK_KEY_period:
// TODO: skewing
// flipping
// NOTE: H is horizontal flip, while Shift+H switches transform handle mode!
case GDK_KEY_h:
case GDK_KEY_H:
return true;
}
// any modifiers except shift should cause no action
return _keyboardFlip(Geom::X);
case GDK_KEY_v:
case GDK_KEY_V:
return _keyboardFlip(Geom::Y);
default: break;
}
break;
default: break;
}
return false;
}
{
}
}
{
if (!(*i)->selected()) {
}
}
}
{
}
}
} // namespace UI
} // namespace Inkscape
/*
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 :