// --------------------------------------------------------------------
// Overlay for editing IpePath objects
// --------------------------------------------------------------------
/*

    This file is part of the extensible drawing editor Ipe.
    Copyright (C) 1993-2004  Otfried Cheong

    Ipe is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    As a special exception, you have permission to link Ipe with the
    CGAL library and distribute executables, as long as you follow the
    requirements of the Gnu General Public License in regard to all of
    the software in the executable aside from CGAL.

    Ipe is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
    License for more details.

    You should have received a copy of the GNU General Public License
    along with Ipe; if not, you can find it at
    "http://www.gnu.org/copyleft/gpl.html", or write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include "ipepath.h"

#include "ipeeditpath.h"
#include "ipecanvas.h"

#include <qevent.h>
#include <qpainter.h>

// --------------------------------------------------------------------

enum { ELineVtx,     // between two straight segments
       EBezierVtx,   // interior CP of EBezier or EQuad
       ESplineVtx,   // interior CP of ESpline
       EFrontVtx,    // front of path if it starts with line or spline
       ETailVtx,     // tail of path if it ends with line or spline
       EFrontDivVtx, // front of path otherwise
       ETailDivVtx,  // tail of path otherwise
       ESplineSplineVtx, // division between two spline segments
       ESplineLineVtx, // division between spline and line segments
       EDivVtx,      // division between segments if one is not spline or line
       ECenterVtx, ECurrentVtx, EOtherVtx  // styles for drawing only
};

bool IsArcOrBezier(const IpePathSegment &seg)
{
  return seg.Type() == IpePathSegment::EArc ||
    seg.Type() == IpePathSegment::EBezier ||
    seg.Type() == IpePathSegment::EQuad;
}

void DrawVertex(int type, QPainter *qPainter, const IpeVector &pos)
{
  int siz = 4;
  int x = int(pos.iX);
  int y = int(pos.iY);
  qPainter->setPen(Qt::NoPen);
  switch (type) {
  case EFrontVtx:
  case ETailVtx:
    siz = 6;
    qPainter->setBrush(Qt::magenta);
    qPainter->drawEllipse(x - siz, y-siz, siz+siz+1, siz+siz+1);
    break;
  case ELineVtx:
  case ESplineVtx:
  case EBezierVtx:
    qPainter->setBrush(Qt::magenta);
    qPainter->drawRect(x - siz, y-siz, siz+siz+1, siz+siz+1);
    break;
  case ESplineLineVtx:
  case ESplineSplineVtx:
  case EDivVtx:
  case EFrontDivVtx:
  case ETailDivVtx:
    siz = 6;
    qPainter->setBrush(Qt::red);
    qPainter->drawEllipse(x - siz, y-siz, siz+siz+1, siz+siz+1);
    break;
  case ECenterVtx:
    siz = 6;
    qPainter->setBrush(Qt::green);
    qPainter->drawEllipse(x - siz, y-siz, siz+siz+1, siz+siz+1);
    break;
  case ECurrentVtx:
    siz = 8;
    qPainter->setPen(Qt::red);
    qPainter->setBrush(Qt::NoBrush);
    qPainter->drawEllipse(x - 8, y - 8, 17, 17);
    break;
  case EOtherVtx:
    siz = 8;
    qPainter->setPen(Qt::red);
    qPainter->setBrush(Qt::NoBrush);
    qPainter->drawRect(x - 8, y - 8, 17, 17);
    break;
  }
}

// --------------------------------------------------------------------

class EditSubPath {
public:
  virtual ~EditSubPath() = 0;
  virtual void Draw(QPainter *qPainter, IpePainter &painter) const = 0;
  virtual double FindVertex(const IpeVector &pos, double bound) = 0;
  virtual IpeVector CurVertex() const = 0;
  virtual void Move(const IpeVector &v) = 0;
  virtual bool CanDelete() const = 0;
  //! Returns true if subpath is now gone.
  virtual bool Delete() = 0;
  virtual IpeSubPath *Take() = 0;
  virtual bool HasOther(int) const { return false; }
  virtual IpeVector Other(int) const { return IpeVector(); }
  virtual bool CanInsert(int) const { return false; }
  virtual void Insert(int, const IpeVector &) { /* nothing */ }
};

EditSubPath::~EditSubPath()
{
  // nothing
}

// --------------------------------------------------------------------

class EditEllipse : public EditSubPath {
public:
  EditEllipse(IpeEllipse *ell) : iEll(ell) { /* nothing */ }
  ~EditEllipse() { delete iEll; }
  virtual void Draw(QPainter *qPainter, IpePainter &painter) const;
  virtual double FindVertex(const IpeVector &pos, double bound);
  virtual IpeVector CurVertex() const { return iEll->Matrix().Translation(); }
  virtual void Move(const IpeVector &v);
  virtual bool CanDelete() const { return true; }
  virtual bool Delete() { return true; }
  virtual IpeSubPath *Take() { IpeSubPath *sp = iEll; iEll = 0; return sp; }
private:
  IpeEllipse *iEll;
};

void EditEllipse::Draw(QPainter *qPainter, IpePainter &painter) const
{
  qPainter->setPen(Qt::magenta);
  qPainter->setBrush(Qt::NoBrush);
  painter.NewPath();
  iEll->Draw(painter);
  painter.DrawPath();
  DrawVertex(ECenterVtx, qPainter,
	     painter.Matrix() * iEll->Matrix().Translation());
}

double EditEllipse::FindVertex(const IpeVector &pos, double /*bound*/)
{
  return (pos - iEll->Matrix().Translation()).Len();
}

void EditEllipse::Move(const IpeVector &v)
{
  iEll->SetMatrix(IpeMatrix(iEll->Matrix().Linear(), v));
}

// --------------------------------------------------------------------

class EditClosedSpline : public EditSubPath {
public:
  EditClosedSpline(IpeClosedSpline *csp)
    : iCSP(csp), iCur(0) { /* nothing */ }
  ~EditClosedSpline() { delete iCSP; }
  virtual void Draw(QPainter *qPainter, IpePainter &painter) const;
  virtual double FindVertex(const IpeVector &pos, double bound);
  virtual IpeVector CurVertex() const { return iCSP->iCP[iCur]; }
  virtual void Move(const IpeVector &v);
  virtual bool CanDelete() const { return true; }
  virtual bool Delete();
  virtual IpeSubPath *Take() { IpeSubPath *sp = iCSP; iCSP = 0; return sp; }
  virtual bool HasOther(int) const { return true; }
  virtual IpeVector Other(int other) const;
  virtual bool CanInsert(int) const { return true; }
  virtual void Insert(int other, const IpeVector &v);
private:
  IpeClosedSpline *iCSP;
  int iCur;
};

double EditClosedSpline::FindVertex(const IpeVector &pos, double bound)
{
  double d = bound;
  double d1;
  for (uint j = 0; j < iCSP->iCP.size(); ++j) {
    if ((d1 = (pos - iCSP->iCP[j]).Len()) < d) {
      d = d1;
      iCur = j;
    }
  }
  return d;
}

bool EditClosedSpline::Delete()
{
  if (iCSP->iCP.size() < 4)
    return true;

  iCSP->iCP.erase(iCSP->iCP.begin() + iCur);
  if (iCur > 0)
    iCur--;
  return false;
}

void EditClosedSpline::Draw(QPainter *qPainter, IpePainter &painter) const
{
  qPainter->setPen(Qt::magenta);
  painter.NewPath();
  iCSP->Draw(painter);
  QPen penSpline(Qt::green, 0, Qt::DashLine);
  qPainter->setPen(penSpline);
  painter.MoveTo(iCSP->iCP[0]);
  for (uint i = 1; i < iCSP->iCP.size(); ++i)
    painter.LineTo(iCSP->iCP[i]);
  painter.ClosePath();
  painter.DrawPath();
  for (uint i = 0; i < iCSP->iCP.size(); ++i)
    DrawVertex(ELineVtx, qPainter, painter.Matrix() * iCSP->iCP[i]);
}

void EditClosedSpline::Move(const IpeVector &v)
{
  iCSP->iCP[iCur] = v;
}

IpeVector EditClosedSpline::Other(int other) const
{
  int k = (iCur + other) % iCSP->iCP.size();
  return iCSP->iCP[k];
}

void EditClosedSpline::Insert(int other, const IpeVector &v)
{
  if (other < 0)
    iCSP->iCP.insert(iCSP->iCP.begin() + iCur, v);
  else
    iCSP->iCP.insert(iCSP->iCP.begin() + iCur + 1, v);
}

// --------------------------------------------------------------------

class EditSegmentSubPath : public EditSubPath {
public:
  EditSegmentSubPath(IpeSegmentSubPath *ssp);
  ~EditSegmentSubPath() { delete iSSP; }
  virtual void Draw(QPainter *qPainter, IpePainter &painter) const;
  virtual double FindVertex(const IpeVector &pos, double bound);
  int VertexType(int j, int k) const;
  virtual IpeVector CurVertex() const;
  virtual void Move(const IpeVector &v);
  virtual bool CanDelete() const;
  virtual bool Delete();
  virtual IpeSubPath *Take() { IpeSubPath *sp = iSSP; iSSP = 0; return sp; }
  virtual bool HasOther(int other) const;
  virtual IpeVector Other(int other) const;
  virtual bool CanInsert(int other) const;
  virtual void Insert(int other, const IpeVector &v);
private:
  IpeSegmentSubPath *iSSP;
  int iSeg;
  int iCur;
};

EditSegmentSubPath::EditSegmentSubPath(IpeSegmentSubPath *ssp)
  : iSSP(ssp), iSeg(0), iCur(0)
{
  // replace all B-spline segments with only 2 CPs by straight segments
  for (int i = 0; i < iSSP->NumSegments(); ++i) {
    IpePathSegment seg = iSSP->Segment(i);
    if (seg.Type() == IpePathSegment::ESpline && seg.NumCP() == 2)
      iSSP->Straighten(i);
  }
}

IpeVector EditSegmentSubPath::CurVertex() const
{
  if (iCur < 0)
    return iSSP->Segment(iSeg).Matrix().Translation();
  else
    return iSSP->Segment(iSeg).CP(iCur);
}

// Return type of CP(k) of segment j (both can be negative).
int EditSegmentSubPath::VertexType(int j, int k) const
{
  if (j < 0)
    j += iSSP->NumSegments();
  IpePathSegment seg = iSSP->Segment(j);
  if (k < 0)
    k += seg.NumCP();
  // Handle end cases
  if (j == 0 && k == 0)
    return IsArcOrBezier(seg) ? EFrontDivVtx : EFrontVtx;
  else if (j == iSSP->NumSegments() - 1 &&
	   k == iSSP->Segment(-1).NumCP() - 1)
    return IsArcOrBezier(seg) ? ETailDivVtx : ETailVtx;

  // Handle interior CPs
  if (0 < k && k < seg.NumCP() - 1)
    return IsArcOrBezier(seg) ? EBezierVtx : ESplineVtx;

  IpePathSegment::TType t1 = seg.Type();
  IpePathSegment::TType t2 =
    (k > 0) ? iSSP->Segment(j + 1).Type() : iSSP->Segment(j - 1).Type();

  if (t1 == IpePathSegment::ESegment) {
    if (t2 == IpePathSegment::ESegment)
      return ELineVtx;
    else if (t2 == IpePathSegment::ESpline)
      return ESplineLineVtx;
  } else if (t1 == IpePathSegment::ESpline) {
    if (t2 == IpePathSegment::ESegment)
      return ESplineLineVtx;
    else if (t2 == IpePathSegment::ESpline)
      return ESplineSplineVtx;
  }
  return EDivVtx;
}

bool EditSegmentSubPath::CanDelete() const
{
  if (iCur < 0)
    return true;
  switch (VertexType(iSeg, iCur)) {
  case ELineVtx:
  case ESplineVtx:
  case EBezierVtx:
  case EFrontVtx:
  case ETailVtx:
    return true;
  default:
    return false;
  }
}

bool EditSegmentSubPath::Delete()
{
  IpePathSegment seg = iSSP->Segment(iSeg);
  IpePathSegment::TType t = seg.Type();
  // vertex in the middle
  if (iCur < 0 || (0 < iCur && iCur + 1 < seg.NumCP())) {
    if (IsArcOrBezier(seg)) {
      iSSP->Straighten(iSeg);
      iCur = 0;
    } else {
      assert(t == IpePathSegment::ESpline);
      if (seg.NumCP() == 3)
	iSSP->Straighten(iSeg);
      else
	iSSP->DeleteCP(iSeg, iCur);
    }
    return false;
  } else if (iSeg == iSSP->NumSegments() - 1 && iCur > 0) {
    // Last vertex of chain
    if (t == IpePathSegment::ESegment)
      iSSP->DeleteSegment(-1);
    else if (t == IpePathSegment::ESpline)
      iSSP->DeleteCP(-1, -1);
    // in all other cases, we cannot delete
    if (iSSP->NumSegments() > 0) {
      // make last vertex again current
      iSeg = iSSP->NumSegments() - 1;
      iCur = iSSP->Segment(-1).NumCP() - 1;
      return false;
    } else
      return true;
  } else if (iSeg == 0 && iCur == 0) {
    // First vertex of chain
    if (t == IpePathSegment::ESegment)
      iSSP->DeleteSegment(0);
    else if (t == IpePathSegment::ESpline)
      iSSP->DeleteCP(0, 0);
    // make first vertex again current
    iSeg = 0;
    iCur = 0;
    return (iSSP->NumSegments() == 0);
  } else {
    // deleting joining vertex in the middle
    if (VertexType(iSeg, iCur) == ELineVtx) {
      // make segment trivial, so it can be deleted.
      iSSP->MoveCP(iSeg, 0, seg.CP(1));
      iSSP->DeleteSegment(iSeg);
      if (iSeg >= iSSP->NumSegments())
	--iSeg;
    }
  }
  return false;
}

bool EditSegmentSubPath::HasOther(int other) const
{
  if (iCur < 0)
    return false;
  switch (VertexType(iSeg, iCur)) {
  case ELineVtx:
  case ESplineVtx:
  case ESplineLineVtx:
  case ESplineSplineVtx:
    return true;
  case EFrontVtx:
    return iSSP->Closed() || (other > 0);
  case ETailVtx:
    return iSSP->Closed() || (other < 0);
  default:
    return false;
  }
}

IpeVector EditSegmentSubPath::Other(int other) const
{
  IpePathSegment seg = iSSP->Segment(iSeg);
  int k = iCur + other;
  if (0 <= k && k < seg.NumCP())
    return seg.CP(k);
  if (k < 0) {
    if (iSeg == 0)
      return iSSP->Segment(-1).Last();
    IpePathSegment seg1 = iSSP->Segment(iSeg - 1);
    return seg1.CP(seg1.NumCP() - 2);
  }
  if (iSeg == iSSP->NumSegments() - 1)
    return iSSP->Segment(0).CP(0);
  IpePathSegment seg1 = iSSP->Segment(iSeg + 1);
  return seg1.CP(1);
}

bool EditSegmentSubPath::CanInsert(int other) const
{
  if (iCur < 0)
    return false;
  switch (VertexType(iSeg, iCur)) {
  case ELineVtx:
  case ESplineVtx:
  case EFrontVtx:
  case ETailVtx:
  case ESplineSplineVtx:
  case ESplineLineVtx:
    return true;
  case EFrontDivVtx:
    return (other < 0);
  case ETailDivVtx:
    return (other > 0);
  case EDivVtx:
    if (other > 0)
      return !IsArcOrBezier(iSSP->Segment(iSeg));
    else
      return !IsArcOrBezier(iSSP->Segment(iSeg - 1));
  default:
    return false;
  }
}

void EditSegmentSubPath::Insert(int other, const IpeVector &v)
{
  // appending at the front
  if (iSeg == 0 && iCur == 0 && other < 0) {
    iSSP->InsertSegment(0);
    iSSP->MoveCP(0, 0, v);
    // ensure current vertex remains front vertex
    iSeg = 0;
    iCur = 0;
    return;
  }
  // appending at the back
  if (iSeg == iSSP->NumSegments() - 1 &&
      iCur == iSSP->Segment(-1).NumCP() - 1 && other > 0) {
    iSSP->AppendSegment(iSSP->Segment(-1).Last(), v);
    // ensure current vertex remains tail vertex
    iSeg = iSSP->NumSegments() - 1;
    iCur = iSSP->Segment(-1).NumCP() - 1;
    return;
  }
  // adjust iSeg to contain both points
  if (iCur == 0 && other < 0) {
    iSeg--;
    iCur = iSSP->Segment(iSeg).NumCP() - 1;
  } else if (iCur == iSSP->Segment(iSeg).NumCP() - 1 && other > 0) {
    iSeg++;
    iCur = 0;
  }
  IpePathSegment seg = iSSP->Segment(iSeg);
  if (seg.Type() == IpePathSegment::ESpline) {
    // insert into spline
    if (other > 0)
      iCur++;
    iSSP->InsertCP(iSeg, iCur, v);
    return;
  }
  // remaining case: seg is a segment
  assert(seg.Type() == IpePathSegment::ESegment);
  iSSP->InsertSegment(iSeg);
  iCur = 1;
  iSSP->MoveCP(iSeg, iCur, v);
}

/*! Never returns last vertex of a segment, unless it's the last
  vertex of the path. */
double EditSegmentSubPath::FindVertex(const IpeVector &pos, double bound)
{
  double d = bound;
  double d1;
  for (int j = 0; j < iSSP->NumSegments(); ++j) {
    IpePathSegment seg = iSSP->Segment(j);
    for (int k = 0; k < seg.NumCP() - 1; ++k) {
      if ((d1 = (pos - seg.CP(k)).Len()) < d) {
	d = d1;
	iSeg = j;
	iCur = k;
      }
    }
    if (seg.Type() == IpePathSegment::EArc) {
      if ((d1 = (pos - seg.Matrix().Translation()).Len()) < d) {
	d = d1;
	iSeg = j;
	iCur = -1;
      }
    }
  }
  if ((d1 = (pos - iSSP->Segment(-1).Last()).Len()) < d) {
    d = d1;
    iSeg = iSSP->NumSegments() - 1;
    iCur = iSSP->Segment(-1).NumCP() - 1;
  }
  return d;
}

void EditSegmentSubPath::Draw(QPainter *qPainter, IpePainter &painter) const
{
  qPainter->setPen(Qt::magenta);
  painter.NewPath();
  iSSP->Draw(painter);

  QPen penBezier(Qt::green, 0, Qt::DotLine);
  QPen penSpline(Qt::green, 0, Qt::DashLine);

  for (int j = 0; j < iSSP->NumSegments(); ++j) {
    IpePathSegment segm = iSSP->Segment(j);
    if (segm.Type() != IpePathSegment::EArc &&
	segm.Type() != IpePathSegment::ESegment) {
      if (segm.Type() == IpePathSegment::ESpline)
	qPainter->setPen(penSpline);
      else
	qPainter->setPen(penBezier);
      painter.MoveTo(segm.CP(0));
      for (int i = 1; i < segm.NumCP(); ++i)
	painter.LineTo(segm.CP(i));
    }
  }
  painter.DrawPath();

  IpeMatrix pm = painter.Matrix();

  for (int j = 0; j < iSSP->NumSegments(); ++j) {
    IpePathSegment segm = iSSP->Segment(j);
    for (int k = 0; k < segm.NumCP() - 1; ++k)
      DrawVertex(VertexType(j, k), qPainter, pm * segm.CP(k));
    if (segm.Type() == IpePathSegment::EArc)
      DrawVertex(ECenterVtx, qPainter, pm * segm.Matrix().Translation());
  }
  DrawVertex(VertexType(-1, -1), qPainter, pm * iSSP->Segment(-1).Last());
}

void EditSegmentSubPath::Move(const IpeVector &v)
{
  if (iCur < 0) {
    IpePathSegment seg = iSSP->Segment(iSeg);
    IpeMatrix m = seg.Matrix();
    IpeMatrix inv = m.Inverse();
    IpeVector v0 = inv * seg.CP(0);
    IpeVector v1 = inv * seg.CP(1);
    IpeLine l(0.5 * (v0 + v1), (v1 - v0).Normalized().Orthogonal());
    IpeVector c = l.Project(inv * v);
    double r = (v0 - c).Len();
    IpeMatrix m1 = m * IpeMatrix(IpeLinear(r, 0, 0, r), c);
    iSSP->SetMatrix(iSeg, m1);
  } else
    iSSP->MoveCP(iSeg, iCur, v);
}

// --------------------------------------------------------------------

/*! \class EditPath
  \brief Overlay for creating IpePath objects.
*/

//! Constructor starts selection.
IpeEditPath::IpeEditPath(IpeCanvas *canvas, IpePath *path,
			 IpePage *page, IpeOverlayServices* services,
			 IpePage::iterator it)
  : IpeOverlay(canvas), iPath(path), iPage(page), iIt(it),
    iServices(services)
{
  for (int i = 0; i < path->NumSubPaths(); ++i) {
    IpeSubPath *sp = path->SubPath(i)->Clone();
    EditSubPath *sp1 = 0;
    switch (sp->Type()) {
    case IpeSubPath::EEllipse:
      sp1 = new EditEllipse(sp->AsEllipse());
      break;
    case IpeSubPath::EClosedSpline:
      sp1 = new EditClosedSpline(sp->AsClosedSpline());
      break;
    case IpeSubPath::ESegments:
      sp1 = new EditSegmentSubPath(sp->AsSegs());
      break;
    }
    iSP.push_back(sp1);
  }
  iCur = 0;
  iOther = 1;
  iInv = iPath->Matrix().Inverse();
  iMoving = false;
  iCanvas->SetDoubleBuffer(true);
  iCanvas->SetDimmed(true);
  Explain();
}

//! Destructor.
IpeEditPath::~IpeEditPath()
{
  for (uint i = 0; i < iSP.size(); ++i)
    delete iSP[i];
}

//! Replace object, finish overlay.
void IpeEditPath::Finish()
{
  IpeAllAttributes attr;
  attr.iStroke = iPath->Stroke();
  attr.iFill = iPath->Fill();
  attr.iLineWidth = iPath->LineWidth();
  attr.iDashStyle = iPath->DashStyle();
  IpePath *obj = new IpePath(attr);
  obj->SetForwardArrow(iPath->ForwardArrow());
  obj->SetBackwardArrow(iPath->BackwardArrow());
  obj->SetStrokeStyle(iPath->StrokeStyle());
  obj->SetMatrix(iPath->Matrix());
  for (uint i = 0; i < iSP.size(); ++i)
    obj->AddSubPath(iSP[i]->Take());
  iServices->OvSvcAddUndoItem(iIt, iPage, *iIt,
				   QObject::tr("editing path object"));
  iIt->ReplaceObject(obj);
  iPage->SetEdited(true);
  iCanvas->FinishOverlay();
}

void IpeEditPath::Explain() const
{
  QString s;
  if (iSP[iCur]->CanInsert(iOther))
    s += QObject::tr("Left: Insert vtx | ");
  s += QObject::tr("Middle or Shift-Right: Move vtx | ");
  s += QObject::tr("Right: Select vtx | ");
  s += QObject::tr("O: Other insertion edge | ");
  if (iSP[iCur]->CanDelete())
    s += QObject::tr("Del: Del vtx | ");
  s += QObject::tr("Space: accept edit");
  iCanvas->Message(s);
}

//! Set current vertex to vertex closest to pos.
void IpeEditPath::FindVertex(const IpeVector &pos)
{
  IpeVector pos1 = iInv * pos;
  double d = (pos1 - iSP[iCur]->CurVertex()).Len();
  for (uint i = 0; i < iSP.size(); ++i) {
    double d1 = iSP[i]->FindVertex(pos1, d);
    if (d1 < d) {
      d = d1;
      iCur = i;
    }
  }
}

//! Destroy current subpath. Return true if this object has been deleted.
bool IpeEditPath::DestroySubPath()
{
  // destroyed last subpath
  if (iSP.size() == 1) {
    iServices->OvSvcAddUndoItem(new IpePage(*iPage),
				     QObject::tr("path object deletion"));
    iPage->erase(iIt);
    iPage->SetEdited(true);
    iCanvas->FinishOverlay();
    return true;
  } else {
    // destroy this subpath
    delete iSP[iCur];
    iSP.erase(iSP.begin() + iCur);
    iCur = 0;
    return false;
  }
}

void IpeEditPath::KeyPress(QKeyEvent *ev)
{
  if (ev->key() == Qt::Key_Delete && iSP[iCur]->CanDelete()) {
    ev->accept();
    if (iSP[iCur]->Delete() && DestroySubPath())
      return; // this object doesn't exist anymore, must not call its methods
    iCanvas->UpdateOverlay();
    Explain();
  } else if (ev->key() == Qt::Key_O) {
    iOther = -iOther;
    iCanvas->UpdateOverlay();
    Explain();
  } else if (ev->key() == Qt::Key_Space) {
    Finish();
    ev->accept();
  } else
    ev->ignore();
}

void IpeEditPath::Draw(QPaintEvent *, QPainter *qPainter) const
{
  IpeOverlayPainter painter(iCanvas->StyleSheet(), qPainter);
  painter.Transform(iCanvas->CanvasTfm());
  painter.Transform(iPath->Matrix());

  for (uint i = 0; i < iSP.size(); ++i) {
    EditSubPath *sp = iSP[i];
    sp->Draw(qPainter, painter);
  }
  DrawVertex(ECurrentVtx, qPainter,
	     painter.Matrix() * iSP[iCur]->CurVertex());
  if (iSP[iCur]->HasOther(iOther))
    DrawVertex(EOtherVtx, qPainter,
	       painter.Matrix() * iSP[iCur]->Other(iOther));
}

void IpeEditPath::MouseMove(QMouseEvent *)
{
  if (iMoving) {
    iSP[iCur]->Move(iInv * iCanvas->Pos());
    iCanvas->UpdateOverlay();
  }
}

void IpeEditPath::MouseRelease(QMouseEvent *ev)
{
  if (iMoving) {
    MouseMove(ev);
    iMoving = false;
    Explain();
  }
}

void IpeEditPath::MousePress(QMouseEvent *ev)
{
  if (ev->button() == QMouseEvent::LeftButton) {
    if (iSP[iCur]->CanInsert(iOther))
      iSP[iCur]->Insert(iOther, iInv * iCanvas->Pos());
  } else if (ev->button() == QMouseEvent::MidButton ||
	     (ev->button() == QMouseEvent::RightButton &&
	      ev->state() & QMouseEvent::ShiftButton)) {
    FindVertex(iCanvas->Pos());
    iMoving = true;
  } else if (ev->button() == QMouseEvent::RightButton)
    FindVertex(iCanvas->Pos());
  iCanvas->UpdateOverlay();
  Explain();
}

// --------------------------------------------------------------------
