multi-path-manipulator.cpp revision baaf66e2658cf2ae9c9a2413f3e7dcd20d2363dc
/**
* @file
* Multi path manipulator - implementation.
*/
/* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
* Abhishek Sharma
*
* Copyright (C) 2009 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include <boost/shared_ptr.hpp>
#include "node.h"
#include "desktop.h"
#include "desktop-handles.h"
#include "document.h"
#include "document-undo.h"
#include "live_effects/lpeobject.h"
#include "message-stack.h"
#include "preferences.h"
#include "sp-path.h"
#include "ui/tool/control-point-selection.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/multi-path-manipulator.h"
#include "ui/tool/path-manipulator.h"
#include "util/unordered-containers.h"
#include "verbs.h"
#include <gdk/gdkkeysyms.h>
namespace Inkscape {
namespace UI {
namespace {
struct hash_nodelist_iterator
{
}
};
/** Find pairs of selected endnodes suitable for joining. */
{
// find all endnodes in selection
if (!node) continue;
}
// Below we find the closest pairs. The algorithm is O(N^3).
// We can go down to O(N^2 log N) by using O(N^2) memory, by putting all pairs
// with their distances in a multimap (not worth it IMO).
}
}
}
}
}
/** After this function, first should be at the end of path and second at the beginnning.
* @returns True if the nodes are in the same subpath */
{
return true;
}
} else { // second is end
}
} else { // first is end
// do nothing
} else { // second is end
}
}
return false;
}
} // anonymous namespace
, _path_data(data)
{
}
{
}
/** Remove empty manipulators. */
void MultiPathManipulator::cleanup()
{
else ++i;
}
}
/**
* Change the set of items to edit.
*
* This method attempts to preserve as much of the state as possible.
*/
{
// iterate over currently edited items, modifying / removing them as necessary
// This item is no longer supposed to be edited - remove its manipulator
} else {
// if the shape record differs, replace the key only and modify other values
{
//hold->setOutlineColor(_getOutlineColor(sr_new.role));
}
}
++i;
}
}
// add newly selected items
ShapeRecord const &r = *i;
// always show outlines for clips and masks
}
}
void MultiPathManipulator::selectSubpaths()
{
if (_selection.empty()) {
} else {
}
}
{
if (empty()) return;
// 1. find last selected node
// 2. select the next node; if the last node or nothing is selected,
// select first node
bool anything_found = false;
bool anynode_found = false;
anynode_found = true;
if (k->selected()) {
last_i = i;
last_j = j;
last_k = k;
anything_found = true;
// when tabbing backwards, we want the first node
}
}
}
}
// NOTE: we should not assume the _selection contains only nodes
// in future it might also contain handles and other types of control points
// this is why we use a flag instead in the loop above, instead of calling
// selection.empty()
if (!anything_found) {
// select first / last node
// this should never fail because there must be at least 1 non-empty manipulator
if (anynode_found) {
if (dir == 1) {
} else {
}
}
return;
}
// three levels deep - w00t!
if (dir == 1) {
// here, last_k points to the node to be selected
++last_j;
++last_i;
}
}
}
} else {
}
--last_i;
}
--last_j;
}
--last_k;
}
_selection.clear();
}
{
}
{
if (_selection.empty()) return;
// When all selected nodes are already cusp, retract their handles
if (node) {
}
}
if (retract_handles) {
if (node) {
}
}
}
}
{
if (_selection.empty()) return;
if (type == SEGMENT_STRAIGHT) {
_done(_("Straighten segments"));
} else {
_done(_("Make segments curves"));
}
}
void MultiPathManipulator::insertNodes()
{
if (_selection.empty()) return;
_done(_("Add nodes"));
}
{
if (_selection.empty()) return;
_done(_("Add extremum nodes"));
}
void MultiPathManipulator::duplicateNodes()
{
if (_selection.empty()) return;
_done(_("Duplicate nodes"));
}
void MultiPathManipulator::joinNodes()
{
if (_selection.empty()) return;
// Node join has two parts. In the first one we join two subpaths by fusing endpoints
// into one. In the second we fuse nodes in each subpath.
if (mouseover_node) {
}
bool same_path = prepare_join(*i);
// When we encounter the mouseover node, we unset the iterator - it will be invalidated
if (i->first == preserve_pos) {
joined_pos = *i->first;
} else if (i->second == preserve_pos) {
joined_pos = *i->second;
} else {
}
// if the handles aren't degenerate, don't move them
}
}
if (same_path) {
} else {
}
}
// Second part replaces contiguous selections of nodes with single nodes
}
_doneWithCleanup(_("Join nodes"), true);
}
void MultiPathManipulator::breakNodes()
{
if (_selection.empty()) return;
_done(_("Break nodes"), true);
}
{
if (_selection.empty()) return;
_doneWithCleanup(_("Delete nodes"), true);
}
/** Join selected endpoints to create segments. */
void MultiPathManipulator::joinSegments()
{
if (_selection.empty()) return;
bool same_path = prepare_join(*i);
if (same_path) {
} else {
}
}
}
_doneWithCleanup("Join segments", true);
}
void MultiPathManipulator::deleteSegments()
{
if (_selection.empty()) return;
_doneWithCleanup("Delete segments", true);
}
{
if (_selection.empty()) return;
_selection.align(d);
if (d == Geom::X) {
_done("Align nodes to a horizontal line");
} else {
_done("Align nodes to a vertical line");
}
}
{
if (_selection.empty()) return;
_selection.distribute(d);
if (d == Geom::X) {
_done("Distrubute nodes horizontally");
} else {
_done("Distribute nodes vertically");
}
}
void MultiPathManipulator::reverseSubpaths()
{
if (_selection.empty()) {
_done("Reverse subpaths");
} else {
_done("Reverse selected subpaths");
}
}
{
if (_selection.empty()) return;
_done("Move nodes");
}
{
// always show outlines for clipping paths and masks
}
}
{
}
{
}
/**
* Set live outline update status.
* When set to true, outline will be updated continuously when dragging
* or transforming nodes. Otherwise it will only update when changes are committed
* to XML.
*/
{
_live_outline = set;
}
/**
* Set live object update status.
* When set to true, objects will be updated continuously when dragging
* or transforming nodes. Otherwise they will only update when changes are committed
* to XML.
*/
{
_live_objects = set;
}
{
//for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
// i->second->setOutlineColor(_getOutlineColor(i->first.role));
//}
}
void MultiPathManipulator::updateHandles()
{
}
{
}
// Single handle adjustments go here.
do {
if (!n) break;
int which = 0;
which = 1;
}
if (which != 0) break; // ambiguous
which = -1;
}
if (which == 0) break; // no handle chosen
bool handled = true;
switch (key) {
// single handle functions
// rotation
case GDK_KEY_bracketleft:
case GDK_KEY_braceleft:
break;
case GDK_KEY_bracketright:
case GDK_KEY_braceright:
break;
// adjust length
case GDK_KEY_period:
case GDK_KEY_greater:
break;
case GDK_KEY_comma:
case GDK_KEY_less:
break;
default:
handled = false;
break;
}
if (handled) return true;
} while(0);
}
case GDK_KEY_PRESS:
switch (key) {
case GDK_KEY_Insert:
case GDK_KEY_KP_Insert:
// Insert - insert nodes in the middle of selected segments
insertNodes();
return true;
case GDK_KEY_i:
case GDK_KEY_I:
// Shift+I - insert nodes (alternate keybinding for Mac keyboards
// that don't have the Insert key)
insertNodes();
return true;
}
break;
case GDK_KEY_d:
case GDK_KEY_D:
return true;
}
case GDK_KEY_j:
case GDK_KEY_J:
// Shift+J - join nodes
joinNodes();
return true;
}
// Alt+J - join segments
joinSegments();
return true;
}
break;
case GDK_KEY_b:
case GDK_KEY_B:
// Shift+B - break nodes
breakNodes();
return true;
}
break;
case GDK_KEY_Delete:
case GDK_KEY_KP_Delete:
case GDK_KEY_BackSpace:
// Alt+Delete - delete segments
} else {
// pass keep_shape = true when:
// a) del preserves shape, and control is not pressed
// b) ctrl+del preserves shape (del_preserves_shape is false), and control is pressed
// Hence xor
//spanish: si es trazado bspline (mode 2)
if(mode==2){
//spanish: ¿correcto?
deleteNodes(false);
else
deleteNodes(true);
}
else
// Delete any selected gradient nodes as well
}
return true;
case GDK_KEY_c:
case GDK_KEY_C:
// Shift+C - make nodes cusp
return true;
}
break;
case GDK_KEY_s:
case GDK_KEY_S:
// Shift+S - make nodes smooth
return true;
}
break;
case GDK_KEY_a:
case GDK_KEY_A:
// Shift+A - make nodes auto-smooth
return true;
}
break;
case GDK_KEY_y:
case GDK_KEY_Y:
// Shift+Y - make nodes symmetric
return true;
}
break;
case GDK_KEY_r:
case GDK_KEY_R:
// Shift+R - reverse subpaths
return true;
}
break;
case GDK_KEY_l:
case GDK_KEY_L:
// Shift+L - make segments linear
return true;
}
case GDK_KEY_u:
case GDK_KEY_U:
// Shift+U - make segments curves
return true;
}
default:
break;
}
break;
case GDK_MOTION_NOTIFY:
}
break;
default: break;
}
return false;
}
/** Commit changes to XML and add undo stack entry based on the action that was done. Invoked
* by sub-manipulators, for example TransformHandleSet and ControlPointSelection. */
{
switch(cps) {
case COMMIT_MOUSE_MOVE:
reason = _("Move nodes");
break;
case COMMIT_KEYBOARD_MOVE_X:
reason = _("Move nodes horizontally");
key = "node:move:x";
break;
case COMMIT_KEYBOARD_MOVE_Y:
reason = _("Move nodes vertically");
key = "node:move:y";
break;
case COMMIT_MOUSE_ROTATE:
reason = _("Rotate nodes");
break;
case COMMIT_KEYBOARD_ROTATE:
reason = _("Rotate nodes");
key = "node:rotate";
break;
reason = _("Scale nodes uniformly");
break;
case COMMIT_MOUSE_SCALE:
reason = _("Scale nodes");
break;
reason = _("Scale nodes uniformly");
key = "node:scale:uniform";
break;
case COMMIT_KEYBOARD_SCALE_X:
reason = _("Scale nodes horizontally");
key = "node:scale:x";
break;
case COMMIT_KEYBOARD_SCALE_Y:
reason = _("Scale nodes vertically");
key = "node:scale:y";
break;
case COMMIT_MOUSE_SKEW_X:
reason = _("Skew nodes horizontally");
key = "node:skew:x";
break;
case COMMIT_MOUSE_SKEW_Y:
reason = _("Skew nodes vertically");
key = "node:skew:y";
break;
case COMMIT_FLIP_X:
reason = _("Flip nodes horizontally");
break;
case COMMIT_FLIP_Y:
reason = _("Flip nodes vertically");
break;
default: return;
}
if (key) {
} else {
}
}
/** Commits changes to XML and adds undo stack entry. */
}
/** Commits changes to XML, adds undo stack entry and removes empty manipulators. */
cleanup();
}
/** Get an outline color based on the shape's role (normal, mask, LPE parameter, etc.). */
{
switch(role) {
case SHAPE_ROLE_CLIPPING_PATH:
case SHAPE_ROLE_MASK:
case SHAPE_ROLE_LPE_PARAM:
case SHAPE_ROLE_NORMAL:
default:
}
}
} // 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 :