control-point-selection.cpp revision dedfdbe5d68088cf3db4be3b0eb52d4b2a2b20fa
/** @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 "desktop.h"
#include "preferences.h"
#include "ui/tool/control-point-selection.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/selectable-control-point.h"
#include "ui/tool/transform-handle-set.h"
namespace Inkscape {
namespace UI {
/**
* @class ControlPointSelection
* @brief 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));
false))));
}
{
clear();
delete _handles;
}
/** Add a control point to the selection. */
{
}
// hide event param and always return false
/*clist->push_back(
x->signal_grabbed.connect(
sigc::bind_return(
sigc::bind<0>(
sigc::mem_fun(*this, &ControlPointSelection::_selectionGrabbed),
x),
false)));
clist->push_back(
x->signal_dragged.connect(
sigc::mem_fun(*this, &ControlPointSelection::_selectionDragged)));
clist->push_back(
x->signal_ungrabbed.connect(
sigc::hide(
sigc::mem_fun(*this, &ControlPointSelection::_selectionUngrabbed))));
clist->push_back(
x->signal_clicked.connect(
sigc::hide(
sigc::bind<0>(
sigc::mem_fun(*this, &ControlPointSelection::_selectionClicked),
x))));*/
x->updateState();
_rot_radius.reset();
signal_point_changed.emit(x, true);
}
/** Remove a point from the selection. */
{
i->disconnect();
}
erased->updateState();
_rot_radius.reset();
}
{
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. */
{
}
// TODO preserving the rotation radius needs some rethinking...
}
/** Align control points on the specified axis. */
{
if (empty()) 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
}
// 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 */
{
if (!bound) {
} else {
}
}
return bound;
}
{
}
return bound;
}
{
_handles_visible = v;
_updateTransformHandles(false);
}
{
_handles->setVisible(false);
}
{
_updateTransformHandles(true);
}
{
} else {
}
}
void ControlPointSelection::_pointGrabbed()
{
_dragging = true;
}
{
}
}
void ControlPointSelection::_pointUngrabbed()
{
_dragging = false;
}
{
// clicking a selected node should toggle the transform handles between rotate and scale mode,
// if they are visible
if (held_shift(*event)) return false;
if (_handles_visible && p->selected()) {
return true;
}
return false;
}
{
if (_dragging) return;
_handles->setVisible(true);
_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;
}
/** 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;
if (!_rot_radius) {
double maxlen = 0;
for (unsigned i = 0; i < 4; ++i) {
}
}
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;
// TODO should the saved rotation center or the current center be used?
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);
}
{
// 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_Up:
case GDK_KP_Up:
case GDK_KP_8:
case GDK_Down:
case GDK_KP_Down:
case GDK_KP_2:
case GDK_Right:
case GDK_KP_Right:
case GDK_KP_6:
case GDK_Left:
case GDK_KP_Left:
case GDK_KP_4:
// rotates
case GDK_bracketleft:
case GDK_bracketright:
// scaling
case GDK_less:
case GDK_comma:
case GDK_greater:
case GDK_period:
// TODO: skewing
// flipping
// NOTE: H is horizontal flip, while Shift+H switches transform handle mode!
case GDK_h:
case GDK_H:
return true;
}
// any modifiers except shift should cause no action
return _keyboardFlip(Geom::X);
case GDK_v:
case GDK_V:
return _keyboardFlip(Geom::Y);
default: break;
}
break;
default: break;
}
return false;
}
} // 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:encoding=utf-8:textwidth=99 :