/*
 * Kivio - Visual Modelling and Flowcharting
 * Copyright (C) 2000-2001 theKompany.com & Dave Marotti
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/*
 * Implementing a connector involves deriving from this class.
 *
 * You need to implement the following routines:
 *   setStartPoint
 *   setEndPoint
 *   checkForCollision
 *   duplicate
 *   paint
 *   paintOutline
 *   saveXML
 *   loadXML
 *
 * Save and Load should call saveConnectors, loadConnectors, saveProperties, and
 * loadProperties.  These are helper functions which take care of the common
 * acts of saving/loading connectors and colors/line-styles, etc...
 *
 */

#include "kivio_1d_stencil.h"
#include "kivio_arrowhead.h"
#include "kivio_common.h"
#include "kivio_connector_point.h"
#include "kivio_custom_drag_data.h"
#include "kivio_fill_style.h"
#include "kivio_intra_stencil_data.h"
#include "kivio_layer.h"
#include "kivio_line_style.h"
#include "kivio_page.h"
#include "kivio_painter.h"
#include "kivio_point.h"
#include "kivio_stencil.h"
#include "kivio_stencil_spawner.h"
#include "kivio_stencil_spawner_info.h"
#include "kivio_stencil_spawner_set.h"
#include "kivio_text_style.h"
#include "kivio_connector_target.h"

#include <kdebug.h>
#include <math.h>
#include <KoZoomHandler.h>

#include <tqbitmap.h>
#include <tqpainter.h>

/**
 * Default constructor.
 *
 * This will allocate a new fill style object, a list for
 * connection points, and set some default values.
 */
Kivio1DStencil::Kivio1DStencil()
    : KivioStencil()
{
    m_pFillStyle = new KivioFillStyle();
    m_pLineStyle = new KivioLineStyle();
    m_pTextStyle = new KivioTextStyle();

    m_pConnectorPoints = new TQPtrList<KivioConnectorPoint>;
    m_pConnectorPoints->setAutoDelete(true);

    m_pStart = new KivioConnectorPoint(this, true);
    m_pStart->setPosition(72.0f, 18.0f, false);

    m_pEnd = new KivioConnectorPoint(this, true);
    m_pEnd->setPosition(0.0f, 18.0f, false);

    m_pLeft = new KivioConnectorPoint(this, false);
    m_pLeft->setPosition(36.0f, 36.0f, false);

    m_pRight = new KivioConnectorPoint(this, false);
    m_pRight->setPosition(36.0f, 0.0f, false);

    m_pTextConn = new KivioConnectorPoint(this, false);
    m_pTextConn->setPosition(36.0f, 18.0f, false);

    m_connectorWidth = 36.0f;
    m_needsWidth = true;
    m_needsText = false;

    m_pConnectorPoints->append( m_pStart );
    m_pConnectorPoints->append( m_pEnd );
    m_pConnectorPoints->append( m_pLeft );
    m_pConnectorPoints->append( m_pRight );
    m_pConnectorPoints->append( m_pTextConn );
}


/**
 * Destructor
 */
Kivio1DStencil::~Kivio1DStencil()
{
   delete m_pFillStyle;
   delete m_pLineStyle;
   delete m_pTextStyle;
   delete m_pConnectorPoints;
}

void Kivio1DStencil::setFGColor( TQColor c )
{
    m_pLineStyle->setColor(c);
}

TQColor Kivio1DStencil::fgColor()
{
    return m_pLineStyle->color();
}

void Kivio1DStencil::setLineWidth( double f )
{
    m_pLineStyle->setWidth(f);
}

double Kivio1DStencil::lineWidth()
{
    return m_pLineStyle->width();
}

void Kivio1DStencil::setLinePattern(int p)
{
    m_pLineStyle->setStyle(p);
}

int Kivio1DStencil::linePattern()
{
    return m_pLineStyle->style();
}

void Kivio1DStencil::setFillPattern(int p)
{
    m_pFillStyle->setBrushStyle(static_cast<TQt::BrushStyle>(p));
}

int Kivio1DStencil::fillPattern()
{
    return m_pFillStyle->brushStyle();
}

void Kivio1DStencil::setBGColor( TQColor c )
{
    m_pFillStyle->setColor(c);
}

TQColor Kivio1DStencil::bgColor()
{
    return m_pFillStyle->color();
}


/////////////////////////////////
// Position functions
/////////////////////////////////
void Kivio1DStencil::setX( double x )
{
  double dx = x - m_x;
  KivioConnectorPoint *p = m_pConnectorPoints->first();

  while( p )
  {
    p->disconnect();
    p->setX( p->x() + dx, false );
    p = m_pConnectorPoints->next();
  }

  m_x = x;
}

void Kivio1DStencil::setY( double y )
{
  double dy = y - m_y;
  KivioConnectorPoint *p = m_pConnectorPoints->first();

  while( p )
  {
    p->disconnect();
    p->setY( p->y() + dy, false );
    p = m_pConnectorPoints->next();
  }

  m_y = y;
}

void Kivio1DStencil::setPosition( double x, double y )
{
    double dx = x - m_x;
    double dy = y - m_y;

    m_x += dx;
    m_y += dy;

    KivioConnectorPoint *p = m_pConnectorPoints->first();
    while( p )
    {
        p->setPosition( p->x()+dx, p->y()+dy, false );
        p->disconnect();

        p = m_pConnectorPoints->next();
    }


    m_x = x;
    m_y = y;
}



/////////////////////////////
// Connection tool functions
/////////////////////////////
void Kivio1DStencil::setStartPoint( double x, double y )
{
   double oldX = m_pStart->x();
   double oldY = m_pStart->y();

   m_pStart->setPosition(x, y, false);
   m_pStart->disconnect();

   updateConnectorPoints(m_pStart, oldX, oldY);
}


void Kivio1DStencil::setEndPoint( double x, double y )
{
   double oldX = m_pEnd->x();
   double oldY = m_pEnd->y();

   m_pEnd->setPosition(x, y, false);
   m_pEnd->disconnect();

   updateConnectorPoints(m_pEnd, oldX, oldY);
}

void Kivio1DStencil::updateConnectorPoints( KivioConnectorPoint *p, double /*oldX*/, double /*oldY*/ )
{
   // If p is the start or end, we need to adjust the width connectors
   if( p == m_pStart || p == m_pEnd )
   {
      double vx = m_pStart->x() - m_pEnd->x();
      double vy = m_pStart->y() - m_pEnd->y();
      double len = sqrt( vx*vx + vy*vy );
      double midX = (m_pStart->x() + m_pEnd->x())/2.0f;
      double midY = (m_pStart->y() + m_pEnd->y())/2.0f;

      vx /= len;
      vy /= len;

      double d = m_connectorWidth/2.0f;

      m_pLeft->setPosition( midX + d*vy, midY + d*(-vx), false );
      m_pRight->setPosition( midX + d*(-vy), midY + d*vx, false );
   }

   updateGeometry();
}


void Kivio1DStencil::paint( KivioIntraStencilData * )
{
    /* Derived class must implement this */
}

void Kivio1DStencil::paintOutline( KivioIntraStencilData *pData )
{
    /* Derived class should implement this */
    paint( pData );
}

void Kivio1DStencil::paintConnectorTargets( KivioIntraStencilData * )
{
}

void Kivio1DStencil::paintSelectionHandles( KivioIntraStencilData *pData )
{
  KivioPainter *painter = pData->painter;
  double x1, y1;
  int flag;

  KoZoomHandler* zoomHandler = pData->zoomHandler;

  KivioConnectorPoint *p = m_pConnectorPoints->first();
  while( p )
  {
    // If we don't need width connectors and we are on a width connector,
    // ignore it.
    x1 = zoomHandler->zoomItX(p->x());
    y1 = zoomHandler->zoomItY(p->y());

    flag = (p->target()) ? KivioPainter::cpfConnected : 0;

    if( p==m_pTextConn )
    {
      if( m_needsText==true )
      {
        painter->drawHandle( x1, y1, 0 );
      }
    }
    else if( p==m_pLeft || p==m_pRight )
    {
      if( m_needsWidth==true )
      {
        painter->drawHandle( x1, y1, 0 );
      }
    }
    else if( p==m_pStart )
    {
      painter->drawHandle( x1, y1, KivioPainter::cpfStart | flag );
    }
    else if( p==m_pEnd )
    {
      painter->drawHandle( x1, y1, KivioPainter::cpfEnd | flag );
    }
    else
    {
      if( p->connectable() ) {
        painter->drawHandle( x1, y1, KivioPainter::cpfConnectable | flag );
      } else {
        painter->drawHandle( x1, y1, flag );
      }
    }

    p = m_pConnectorPoints->next();
  }
}



///////////////////////////////
// Collision detection
///////////////////////////////
KivioCollisionType Kivio1DStencil::checkForCollision( KoPoint *, double )
{
    /* Derived class must implement this */

    return kctNone;
}




/////////////////////////////////
// Custom dragging
/////////////////////////////////
/**
 * Custom drag the connector points.
 *
 * The default action of this function is to locate the point
 * in the connector list by the id and then drag it around.
 * Then attempt to snap it to another stencil.  Otherwise
 * disconnect it.
 */
void Kivio1DStencil::customDrag( KivioCustomDragData *pData )
{
  setCustomIDPoint(pData->id, KoPoint(pData->x, pData->y), pData->page);
}


/**
 * Sets the position and dimensions of this stencil based on its connection points.
 */
void Kivio1DStencil::updateGeometry()
{
    double minX, minY, maxX, maxY;

    minX = 1000000000000.0f;
    minY = minX;
    maxX = -100000000000.0f;
    maxY = maxX;


    KivioConnectorPoint *p;
    p = m_pConnectorPoints->first();
    while( p )
    {
        if( p->x() < minX )
            minX = p->x();
        if( p->x() > maxX )
            maxX = p->x();
        if( p->y() < minY )
            minY = p->y();
        if( p->y() > maxY )
            maxY = p->y();

        p = m_pConnectorPoints->next();
    }


    m_x = minX;
    m_y = minY;
    m_w = maxX - minX + 1.0f;
    m_h = maxY - minY + 1.0f;
}



// file i/o routines
bool Kivio1DStencil::loadXML( const TQDomElement &e )
{
   TQDomNode node;
   TQString name;

   node = e.firstChild();
   while( !node.isNull() )
   {
      name = node.nodeName();

      if( name == "KivioStencilProperties" )
      {
	 loadProperties(node.toElement() );
      }

      node = node.nextSibling();
   }

   updateGeometry();

   return true;
}


TQDomElement Kivio1DStencil::createRootElement( TQDomDocument &doc )
{
   TQDomElement e = doc.createElement("KivioPluginStencil");

   XmlWriteString( e, "id", m_pSpawner->info()->id() );
   XmlWriteString( e, "setId", m_pSpawner->set()->id() );

   return e;
}


TQDomElement Kivio1DStencil::saveXML( TQDomDocument &doc )
{
   TQDomElement e = createRootElement(doc);

   e.appendChild( saveProperties(doc) );

   return e;
}



KivioStencil *Kivio1DStencil::duplicate()
{
    /* Derived class must implement this function */
    return NULL;
}


bool Kivio1DStencil::boolAllTrue( bool *boolArray, int count )
{
    int i;

    for( i=0; i<count; i++ )
    {
        if( boolArray[i]==false )
            return false;
    }

    return true;
}

bool Kivio1DStencil::boolContainsFalse( bool *boolArray, int count )
{
    int i;

    for( i=0; i<count; i++ )
    {
        if( boolArray[i]==false )
            return true;
    }

    return false;
}

void Kivio1DStencil::searchForConnections( KivioPage *pPage )
{
  bool *done = new bool[ m_pConnectorPoints->count()];
  unsigned int i;

  for(i = 0; i < m_pConnectorPoints->count(); i++) {
    done[i] = false;
  }

  KivioConnectorPoint *p;
  i = 0;
  p = m_pConnectorPoints->first();

  while( p )
  {
    if( p->targetId() == -1 ) {
      done[i] = true;
    }

    i++;
    p = m_pConnectorPoints->next();
  }

  // No connections? BaiL!
  if( boolAllTrue( done, m_pConnectorPoints->count() ) )
  {
    delete [] done;
    return;
  }

  KivioLayer *pLayer = pPage->firstLayer();

  while( pLayer && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
  {
    KivioStencil *pStencil = pLayer->firstStencil();

    while( pStencil && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
    {
      // No connecting to ourself!
      if((pStencil != this))
      {

        // Iterate through all connectors attempting to connect it to the stencil.
        // If it connects, mark it as done
        i=0;
        p = m_pConnectorPoints->first();
        while( p )
        {
          if( !done[i] && p->targetId() != -1 )
          {
            if(pStencil->connectToTarget( p, p->targetId()))
            {
              done[i] = true;
            }
          }

          i++;
          p = m_pConnectorPoints->next();
        }

      }

      pStencil = pLayer->nextStencil();
    }

    pLayer = pPage->nextLayer();
  }

  delete [] done;
}

void Kivio1DStencil::searchForConnections( KivioPage *pPage, double threshold )
{
  bool *done = new bool[ m_pConnectorPoints->count()];
  int i;

  for( i=0; i<(int)m_pConnectorPoints->count(); i++ ) {
    done[i] = false;
  }

  KivioConnectorPoint *p;
  i = 0;
  p = m_pConnectorPoints->first();

  while( p )
  {
    if(p->target() != 0L) {
      done[i] = true;
    }

    i++;
    p = m_pConnectorPoints->next();
  }

  // No connections? BaiL!
  if( boolAllTrue( done, m_pConnectorPoints->count() ) )
  {
    delete [] done;
    return;
  }

  KivioLayer *pLayer = pPage->firstLayer();

  while( pLayer && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
  {
    KivioStencil *pStencil = pLayer->firstStencil();

    while( pStencil && ( boolContainsFalse(done, m_pConnectorPoints->count()) ) )
    {
      // No connecting to ourself!
      if( pStencil != this )
      {

        // Iterate through all connectors attempting to connect it to the stencil.
        // If it connects, mark it as done
        i = 0;
        p = m_pConnectorPoints->first();

        while( p )
        {
          if( !done[i] && p->target() == 0 )
          {
            if( pStencil->connectToTarget( p, threshold ) )
            {
                done[i] = true;
            }
          }

          i++;
          p = m_pConnectorPoints->next();
        }

      }

      pStencil = pLayer->nextStencil();
    }

    pLayer = pPage->nextLayer();
  }

  delete [] done;
}

//////////////////////
// resize handles
//////////////////////
int Kivio1DStencil::resizeHandlePositions()
{
    return (int)krhpNone;
}

TQDomElement Kivio1DStencil::saveConnectors( TQDomDocument &doc )
{
    TQDomElement eConns = doc.createElement("KivioConnectorList");

    KivioConnectorPoint *p;
    p = m_pConnectorPoints->first();
    while( p )
    {
        eConns.appendChild( p->saveXML(doc) );

        p = m_pConnectorPoints->next();
    }

    return eConns;
}

bool Kivio1DStencil::loadConnectors( const TQDomElement &e )
{
    m_pConnectorPoints->clear();

    KivioConnectorPoint *p;

    TQDomNode node = e.firstChild();
    TQDomElement e2;
    TQString name;

    while( !node.isNull() )
    {
        e2 = node.toElement();
        name = e2.nodeName();

        if( name == "KivioConnectorPoint" )
        {
            p = new KivioConnectorPoint();
            p->setStencil(this);
            p->loadXML( e2 );

            m_pConnectorPoints->append( p );
            p = NULL;
        }

        node = node.nextSibling();
    }

    // Set the pointers to the start,end,left,right points
    m_pStart = m_pConnectorPoints->first();
    m_pEnd = m_pConnectorPoints->next();
    m_pLeft = m_pConnectorPoints->next();
    m_pRight = m_pConnectorPoints->next();
    m_pTextConn = m_pConnectorPoints->next();

    // Hopefully this will help with backwards compatibility
    if( m_pStart == NULL ) {
       m_pStart = new KivioConnectorPoint(this, true);
    }
    if( m_pEnd == NULL ) {
       m_pEnd = new KivioConnectorPoint(this, true);
    }
    if( m_pLeft == NULL ) {
       m_pLeft = new KivioConnectorPoint(this, false);
    }
    if( m_pRight == NULL ) {
       m_pRight = new KivioConnectorPoint(this, false);
    }
    if( m_pTextConn == NULL ) {
       m_pTextConn = new KivioConnectorPoint(this, false);
    }


    return true;
}

TQDomElement Kivio1DStencil::saveProperties( TQDomDocument &doc )
{
    TQDomElement propE = doc.createElement("KivioStencilProperties");

    TQDomElement connE = doc.createElement("Kivio1DProperties");
    XmlWriteFloat( connE, "connectorWidth", m_connectorWidth );
    XmlWriteInt( connE, "needsWidth", m_needsWidth );
    propE.appendChild( connE );

    propE.appendChild( m_pLineStyle->saveXML( doc ) );

    propE.appendChild( m_pFillStyle->saveXML( doc ) );

    propE.appendChild( m_pTextStyle->saveXML( doc ) );

    propE.appendChild( saveConnectors(doc) );

    TQDomElement customE = doc.createElement("CustomData");
    if( saveCustom( customE, doc )==true )
    {
       propE.appendChild( customE );
    }

    return propE;
}

bool Kivio1DStencil::loadProperties( const TQDomElement &e )
{
    TQDomNode node;
    TQDomElement nodeE;
    TQString nodeName;

    node = e.firstChild();
    while( !node.isNull() )
    {
        nodeE = node.toElement();
        nodeName = node.nodeName();

        if( nodeName == "KivioFillStyle" )
        {
            m_pFillStyle->loadXML( nodeE );
        }
        else if( nodeName == "KivioLineStyle" )
        {
	   m_pLineStyle->loadXML( nodeE );
        }
	else if( nodeName == "KivioTextStyle" )
	{
	   m_pTextStyle->loadXML( nodeE );
	}
	else if( nodeName == "KivioConnectorList" )
	{
	   loadConnectors( nodeE );
	}
	else if( nodeName == "Kivio1DProperties" )
	{
	   m_needsWidth = (bool)XmlReadInt( nodeE, "needsWidth", (int)true );
	   m_connectorWidth = XmlReadFloat( nodeE, "connectorWidth", 36.0f );
	}
	else if( nodeName == "CustomData" )
	{
	   loadCustom( nodeE );
	}

        node = node.nextSibling();
    }

    return true;
}

bool Kivio1DStencil::loadCustom( const TQDomElement & )
{
   return true;
}

bool Kivio1DStencil::saveCustom( TQDomElement &, TQDomDocument & )
{
   return false;
}

void Kivio1DStencil::copyBasicInto( Kivio1DStencil *pStencil )
{
    KivioConnectorPoint *pSrc, *pTg;

    // Copy the spawner
    pStencil->setSpawner( m_pSpawner );

    // Copy the connector points
    pSrc = m_pConnectorPoints->first();
    pTg = pStencil->m_pConnectorPoints->first();
    while( pSrc && pTg )
    {
       pTg->setPosition( pSrc->x(), pSrc->y(), false );

       pSrc = m_pConnectorPoints->next();
       pTg = pStencil->m_pConnectorPoints->next();
    }

    // Copy the dimensions
    pStencil->m_x = m_x;
    pStencil->m_y = m_y;
    pStencil->m_w = m_w;
    pStencil->m_h = m_h;

    // Copy over the width info
    pStencil->m_connectorWidth = m_connectorWidth;
    pStencil->m_needsWidth = m_needsWidth;

    // Copy over the styles
    m_pFillStyle->copyInto( pStencil->m_pFillStyle );
    m_pLineStyle->copyInto( pStencil->m_pLineStyle );
    m_pTextStyle->copyInto( pStencil->m_pTextStyle );

    // Copy over the protection
    *(pStencil->m_pProtection) = *m_pProtection;
    *(pStencil->m_pCanProtect) = *m_pCanProtect;
}

void Kivio1DStencil::drawText( KivioIntraStencilData *pData )
{
  if(m_pTextStyle->text().isEmpty()) {
    return;
  }
  
  KoZoomHandler* zoomHandler = pData->zoomHandler;
  KivioPainter *painter = pData->painter;

  int _x, _y, _w, _h;

  _x = zoomHandler->zoomItX(m_pTextConn->x());
  _y = zoomHandler->zoomItY(m_pTextConn->y());
  _w = 10000000;
  _h = 10000000;

  TQFont f = m_pTextStyle->font();
  int tf = m_pTextStyle->hTextAlign() | m_pTextStyle->vTextAlign();

  f.setPointSizeFloat(f.pointSizeFloat() * (((float)zoomHandler->zoom()) / 100.0));
  painter->setFont(f);
  TQRect boundRect = painter->boundingRect( _x, _y, _w, _h, tf, m_pTextStyle->text() );
  TQPixmap pix(boundRect.width(), boundRect.height());
  pix.fill();
  TQPainter p(&pix);
  p.setPen(m_pTextStyle->color());
  p.setFont(f);
  p.drawText( 0, 0, boundRect.width(), boundRect.height(), tf, m_pTextStyle->text() );
  TQBitmap mask;
  mask = pix;
  pix.setMask(mask);
  painter->drawPixmap(_x, _y, pix);
}

bool Kivio1DStencil::connected()
{
  KivioConnectorPoint *p;
  p = m_pConnectorPoints->first();

  while( p )
  {
    if(p->target() != 0) {
      return true;
    }

    p = m_pConnectorPoints->next();
  }

  return false;
}

void Kivio1DStencil::disconnectFromTargets()
{
  KivioConnectorPoint *p;
  p = m_pConnectorPoints->first();

  while( p )
  {
    p->disconnect(true);
    p = m_pConnectorPoints->next();
  }
}

KivioLineStyle Kivio1DStencil::lineStyle()
{
  return *m_pLineStyle;
}

void Kivio1DStencil::setLineStyle(KivioLineStyle ls)
{
  ls.copyInto(m_pLineStyle);
}

void Kivio1DStencil::setCustomIDPoint(int customID, const KoPoint& point, KivioPage* page)
{
  double oldX, oldY;
  KivioConnectorPoint *p;

  // Locate the point specified by customID
  p = m_pConnectorPoints->at( customID - (kctCustom+1));

  if( !p )
  {
    kdDebug(43000) << "Kivio1DStencil::customDrag() - KivioConnectorPoint customID: " << (customID - (kctCustom+1)) << "  not found\n" << endl;
    return;
  }

  oldX = p->x();
  oldY = p->y();
  p->setPosition(point.x(),point.y(),true);

  if( p->connectable()==true )
  {
    // Attempt a snap....
    KivioLayer *pCurLayer = page->curLayer();
    KivioLayer *pLayer = page->firstLayer(); //page->curLayer();
    bool foundConnection = false;

    while( pLayer && !foundConnection )
    {
      // To be connected to, a layer must be visible and connectable
      if( pLayer!=pCurLayer )
      {
        if( pLayer->connectable()==false || pLayer->visible()==false )
        {
          pLayer = page->nextLayer();
          continue;
        }
      }

      // Tell the layer to search for a target
      if( pLayer->connectPointToTarget( p, 8.0f ) )
      {
        foundConnection = true;
      }

      pLayer = page->nextLayer();
    }

    if( foundConnection == false )
    {
      p->disconnect();
    }
  }

  // If it is a start/end point, then make a request to update the connectors (must be implemented by stencil developer)
  if( customID == kctCustom+1 || customID == kctCustom+2 )
  {
    // If it's the end connector, then update the text point
    if( p==m_pEnd && m_needsText==true )
    {
      m_pTextConn->setPosition( m_pTextConn->x() + (m_pEnd->x() - oldX),
                                m_pTextConn->y() + (m_pEnd->y() - oldY), false );
    }

    updateConnectorPoints(p, oldX, oldY);
  }
  // If it is one of the width handles, then fix the width and update the opposite point
  // only if the stencils 'needs width' connectors
  else if( (customID == kctCustom+3 || customID == kctCustom+4) && (m_needsWidth==true) )
  {
    double vx = m_pStart->x() - m_pEnd->x();
    double vy = m_pStart->y() - m_pEnd->y();
    double len = sqrt( vx*vx + vy*vy );
    double midX = (m_pStart->x() + m_pEnd->x())/2.0f;
    double midY = (m_pStart->y() + m_pEnd->y())/2.0f;

    vx /= len;
    vy /= len;

    double d = shortestDistance( m_pStart, m_pEnd, (customID==kctCustom+3) ? m_pLeft : m_pRight );

    m_pLeft->setPosition( midX + d*vy, midY + d*(-vx), false );
    m_pRight->setPosition( midX + d*(-vy), midY + d*vx, false );

    m_connectorWidth = d*2.0f;

    updateConnectorPoints(p, oldX, oldY);
    return;
  }
  // Text handle
  else if( customID == kctCustom+5 )
  {
    updateConnectorPoints(p, oldX, oldY);
  }
}

KoPoint Kivio1DStencil::customIDPoint(int customID)
{
  KivioConnectorPoint *p;

  // Locate the point specified by customID
  p = m_pConnectorPoints->at( customID - (kctCustom+1));

  return p->position();
}
