/* This file is part of the KDE project
   Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
   Copyright (C) 2005-2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>

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

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <tqregexp.h>

#include <kdebug.h>

#include "kspread_canvas.h"
#include "kspread_cell.h"
#include "kspread_doc.h"
#include "kspread_editors.h"
#include "kspread_sheet.h"
#include "kspread_view.h"
#include "kspread_util.h"

#include "selection.h"

// TODO Stefan: Substract points in selections
// TODO Stefan: KPart signal (kspread_events.h)

using namespace KSpread;

/***************************************************************************
  class Selection::Private
****************************************************************************/

class Selection::Private
{
public:
  Private(View *v)
  {
    view = v;
    sheet = 0;
    anchor = TQPoint(1,1);
    cursor = TQPoint(1,1);
    marker = TQPoint(1,1);

    colors.push_back(TQt::red);
    colors.push_back(TQt::blue);
    colors.push_back(TQt::magenta);
    colors.push_back(TQt::darkRed);
    colors.push_back(TQt::darkGreen);
    colors.push_back(TQt::darkMagenta);
    colors.push_back(TQt::darkCyan);
    colors.push_back(TQt::darkYellow);

    multipleSelection = false;

    activeElement = Iterator();
    activeSubRegionStart = 0;
    activeSubRegionLength = 0;
  }

  View*  view;
  Sheet* sheet;
  TQPoint anchor;
  TQPoint cursor;
  TQPoint marker;
  TQValueList<TQColor> colors;

  bool multipleSelection : 1;

  Selection::Iterator activeElement;
  uint activeSubRegionStart;
  uint activeSubRegionLength;
};

/***************************************************************************
  class Selection
****************************************************************************/
namespace KSpread {

Selection::Selection(View *view)
  : TQObject(view), Region(1,1)
{
  d = new Private(view);
  d->activeSubRegionStart = 0;
  d->activeSubRegionLength = 1;
}

Selection::Selection(const Selection& selection)
  : TQObject(selection.d->view), Region()
{
/*  kdDebug() << k_funcinfo << endl;*/
  d = new Private(selection.d->view);
  d->sheet = selection.d->sheet;
  d->activeSubRegionStart = 0;
  d->activeSubRegionLength = cells().count();
}

Selection::~Selection()
{
  delete d;
}

void Selection::initialize(const TQPoint& point, Sheet* sheet)
{
    if (!util_isPointValid(point))
        return;

    if (!d->view->activeSheet())
	    return;

  if (!sheet)
  {
    if (d->sheet)
    {
      sheet = d->sheet;
    }
    else
    {
      sheet = d->view->activeSheet();
    }
  }

  Region changedRegion(*this);
  changedRegion.add(extendToMergedAreas(TQRect(d->anchor,d->marker)));

  TQPoint topLeft(point);
  Cell* cell = d->view->activeSheet()->cellAt(point);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    topLeft = TQPoint(cell->column(), cell->row());
  }

  d->anchor = topLeft;
  d->cursor = point;
  d->marker = topLeft;

  fixSubRegionDimension(); // TODO remove this sanity check
  Iterator it = cells().begin() += d->activeSubRegionStart + d->activeSubRegionLength;
  if (it != insert(it, topLeft, sheet/*, true*/))
  {
    // if the point was inserted
    clearSubRegion();
  }
  Element* element = *(cells().begin() += d->activeSubRegionStart);
  // we end up with one element in the subregion
  d->activeSubRegionLength = 1;
  if (element && element->type() == Element::Point)
  {
    Point* point = static_cast<Point*>(element);
    point->setColor(d->colors[cells().size() % d->colors.size()]);
  }
  else if (element && element->type() == Element::Range)
  {
    Range* range = static_cast<Range*>(element);
    range->setColor(d->colors[cells().size() % d->colors.size()]);
  }

  d->activeElement = cells().begin();

  if (changedRegion == *this)
  {
    emit changed(Region(topLeft, sheet));
    return;
  }
  changedRegion.add(topLeft, sheet);

  emit changed(changedRegion);
}

void Selection::initialize(const TQRect& range, Sheet* sheet)
{
    if (!util_isRectValid(range) || ( range == TQRect(0,0,1,1) ))
        return;

  if (!sheet)
  {
    if (d->sheet)
    {
      sheet = d->sheet;
    }
    else
    {
      sheet = d->view->activeSheet();
    }
  }

  Region changedRegion(*this);
  changedRegion.add(extendToMergedAreas(TQRect(d->anchor,d->marker)));

  TQPoint topLeft(range.topLeft());
  Cell* cell = d->view->activeSheet()->cellAt(topLeft);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    topLeft = TQPoint(cell->column(), cell->row());
  }

  TQPoint bottomRight(range.bottomRight());
  cell = d->view->activeSheet()->cellAt(bottomRight);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    bottomRight = TQPoint(cell->column(), cell->row());
  }

  d->anchor = topLeft;
  d->cursor = bottomRight;
  d->marker = bottomRight;

  fixSubRegionDimension(); // TODO remove this sanity check
  Iterator it = cells().begin() += d->activeSubRegionStart + d->activeSubRegionLength;
  if (it != insert(it, TQRect(topLeft, bottomRight), sheet/*, true*/))
  {
    // if the range was inserted
    clearSubRegion();
  }

  Element* element = *(cells().begin() += d->activeSubRegionStart);
  // we end up with one element in the subregion
  d->activeSubRegionLength = 1;
  if (element && element->type() == Element::Point)
  {
    Point* point = static_cast<Point*>(element);
    point->setColor(d->colors[cells().size() % d->colors.size()]);
  }
  else if (element && element->type() == Element::Range)
  {
    Range* range = static_cast<Range*>(element);
    range->setColor(d->colors[cells().size() % d->colors.size()]);
  }

  d->activeElement = cells().begin();

  if (changedRegion == *this)
  {
    return;
  }
  changedRegion.add(TQRect(topLeft, bottomRight), sheet);

  emit changed(changedRegion);
}

void Selection::initialize(const Region& region, Sheet* sheet)
{
    if (!region.isValid())
        return;

  if (!sheet)
  {
    if (d->sheet)
    {
      sheet = d->sheet;
    }
    else
    {
      sheet = d->view->activeSheet();
    }
  }

  Region changedRegion(*this);
  changedRegion.add(extendToMergedAreas(TQRect(d->anchor,d->marker)));

  // TODO Stefan: handle subregion insertion
  // TODO Stefan: handle obscured cells correctly
  clear();
  Element* element = add(region);
  if (element && element->type() == Element::Point)
  {
    Point* point = static_cast<Point*>(element);
    point->setColor(d->colors[cells().size() % d->colors.size()]);
  }
  else if (element && element->type() == Element::Range)
  {
    Range* range = static_cast<Range*>(element);
    range->setColor(d->colors[cells().size() % d->colors.size()]);
  }

  TQPoint topLeft(cells().last()->rect().normalize().topLeft());
  Cell* cell = d->view->activeSheet()->cellAt(topLeft);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    topLeft = TQPoint(cell->column(), cell->row());
  }

  TQPoint bottomRight(cells().last()->rect().normalize().bottomRight());
  cell = d->view->activeSheet()->cellAt(bottomRight);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    bottomRight = TQPoint(cell->column(), cell->row());
  }

  d->anchor = topLeft;
  d->cursor = topLeft;
  d->marker = bottomRight;

  d->activeElement = --cells().end();
  d->activeSubRegionStart = 0;
  d->activeSubRegionLength = cells().count();

  if (changedRegion == *this)
  {
    return;
  }
  changedRegion.add( region );

  emit changed(changedRegion);
}

void Selection::update()
{
  emit changed(*this);
}

void Selection::update(const TQPoint& point)
{
  uint count = cells().count();

  if (cells().isEmpty())
  {
    add(point);
    d->activeSubRegionLength += cells().count() - count;
    return;
  }
  if (d->activeElement == cells().end())
  {
    // we're not empty, so this will not become again end()
    d->activeElement--;
  }

  Sheet* sheet = (*d->activeElement)->sheet();
  if (sheet != d->view->activeSheet())
  {
    extend(point);
    d->activeSubRegionLength += cells().count() - count;
    return;
  }

  TQPoint topLeft(point);
  Cell* cell = d->view->activeSheet()->cellAt(point);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    topLeft = TQPoint(cell->column(), cell->row());
  }

  if (topLeft == d->marker)
  {
    return;
  }

  TQRect area1 = (*d->activeElement)->rect().normalize();
  TQRect newRange = extendToMergedAreas(TQRect(d->anchor, topLeft));

  Element* oldElement = *d->activeElement;
  // returns iterator to the next element or end
  Iterator it = cells().remove(d->activeElement);
  delete oldElement;
  // returns iterator to the new element (before 'it') or 'it'
  d->activeElement = insert(it, newRange, sheet, d->multipleSelection);
  d->activeSubRegionLength += cells().count() - count;

  // The call to insert() above can just return the iterator which has been
  // passed in. This may be cells.end(), if the old active element was the
  // iterator to the list's end (!= last element). So attempts to dereference
  // it will fail.
  if (d->activeElement == cells().end())
  {
    d->activeElement--;
  }

  TQRect area2 = (*d->activeElement)->rect().normalize();
  Region changedRegion;

  bool newLeft   = area1.left() != area2.left();
  bool newTop    = area1.top() != area2.top();
  bool newRight  = area1.right() != area2.right();
  bool newBottom = area1.bottom() != area2.bottom();

  /* first, calculate some numbers that we'll use a few times */
  int farLeft = TQMIN(area1.left(), area2.left());
  int innerLeft = TQMAX(area1.left(), area2.left());

  int farTop = TQMIN(area1.top(), area2.top());
  int innerTop = TQMAX(area1.top(), area2.top());

  int farRight = TQMAX(area1.right(), area2.right());
  int innerRight = TQMIN(area1.right(), area2.right());

  int farBottom = TQMAX(area1.bottom(), area2.bottom());
  int innerBottom = TQMIN(area1.bottom(), area2.bottom());

  if (newLeft)
  {
    changedRegion.add(TQRect(TQPoint(farLeft, innerTop),
                      TQPoint(innerLeft-1, innerBottom)));
    if (newTop)
    {
      changedRegion.add(TQRect(TQPoint(farLeft, farTop),
                        TQPoint(innerLeft-1, innerTop-1)));
    }
    if (newBottom)
    {
      changedRegion.add(TQRect(TQPoint(farLeft, innerBottom+1),
                        TQPoint(innerLeft-1, farBottom)));
    }
  }

  if (newTop)
  {
    changedRegion.add(TQRect(TQPoint(innerLeft, farTop),
                      TQPoint(innerRight, innerTop-1)));
  }

  if (newRight)
  {
    changedRegion.add(TQRect(TQPoint(innerRight+1, innerTop),
                      TQPoint(farRight, innerBottom)));
    if (newTop)
    {
      changedRegion.add(TQRect(TQPoint(innerRight+1, farTop),
                        TQPoint(farRight, innerTop-1)));
    }
    if (newBottom)
    {
      changedRegion.add(TQRect(TQPoint(innerRight+1, innerBottom+1),
                        TQPoint(farRight, farBottom)));
    }
  }

  if (newBottom)
  {
    changedRegion.add(TQRect(TQPoint(innerLeft, innerBottom+1),
                      TQPoint(innerRight, farBottom)));
  }

  d->marker = topLeft;
  d->cursor = point;

  emit changed(changedRegion);
}

void Selection::extend(const TQPoint& point, Sheet* sheet)
{
    if (!util_isPointValid(point))
        return;

  if (isEmpty())
  {
    initialize(point, sheet);
    return;
  }
  if (d->activeElement == cells().end())
  {
    // we're not empty, so this will not become again end()
    d->activeElement--;
  }

  if (!sheet)
  {
    if (d->sheet)
    {
      sheet = d->sheet;
    }
    else
    {
      sheet = d->view->activeSheet();
    }
  }

  Region changedRegion = Region(extendToMergedAreas(TQRect(d->marker,d->marker)));

  TQPoint topLeft(point);
  Cell* cell = d->view->activeSheet()->cellAt(point);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    topLeft = TQPoint(cell->column(), cell->row());
  }

  uint count = cells().count();
  if (d->multipleSelection)
  {
    d->activeElement = insert(++d->activeElement, point, sheet, false);
  }
  else
  {
    eor(topLeft, sheet);
    d->activeElement = --cells().end();
  }
  d->anchor = (*d->activeElement)->rect().topLeft();
  d->cursor = (*d->activeElement)->rect().bottomRight();
  d->marker = d->cursor;

  d->activeSubRegionLength += cells().count() - count;

  changedRegion.add(topLeft, sheet);
  changedRegion.add(*this);

  emit changed(changedRegion);
}

void Selection::extend(const TQRect& range, Sheet* sheet)
{
    //See comment in Selection::initialize(const TQRect& range, Sheet* sheet)
    if (!util_isRectValid(range) || (range == TQRect(0,0,1,1)))
        return;

  if (isEmpty())
  {
    initialize(range, sheet);
    return;
  }
  if (d->activeElement == cells().end())
  {
    // we're not empty, so this will not become again end()
    d->activeElement--;
  }

  if (!sheet)
  {
    if (d->sheet)
    {
      sheet = d->sheet;
    }
    else
    {
      sheet = d->view->activeSheet();
    }
  }

  TQPoint topLeft(range.topLeft());
  Cell* cell = d->view->activeSheet()->cellAt(topLeft);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    topLeft = TQPoint(cell->column(), cell->row());
  }

  TQPoint bottomRight(range.bottomRight());
  cell = d->view->activeSheet()->cellAt(bottomRight);
  if (cell->isObscured() && cell->isPartOfMerged())
  {
    cell = cell->obscuringCells().first();
    bottomRight = TQPoint(cell->column(), cell->row());
  }

  d->anchor = topLeft;
  d->cursor = topLeft;
  d->marker = bottomRight;

  uint count = cells().count();
  Element* element;
  if (d->multipleSelection)
  {
    d->activeElement = insert(++d->activeElement, extendToMergedAreas(TQRect(topLeft, bottomRight)).normalize(), sheet, false);
    element = (d->activeElement == cells().end()) ? 0 : *d->activeElement;
  }
  else
  {
    element = add(extendToMergedAreas(TQRect(topLeft, bottomRight)).normalize(), sheet);
    d->activeElement = --cells().end();
  }
  if (element && element->type() == Element::Point)
  {
    Point* point = static_cast<Point*>(element);
    point->setColor(d->colors[cells().size() % d->colors.size()]);
  }
  else if (element && element->type() == Element::Range)
  {
    Range* range = static_cast<Range*>(element);
    range->setColor(d->colors[cells().size() % d->colors.size()]);
  }

  d->activeSubRegionLength += cells().count() - count;

  emit changed(*this);
}

void Selection::extend(const Region& region)
{
    if (!region.isValid())
        return;

  uint count = cells().count();
  ConstIterator end(region.constEnd());
  for (ConstIterator it = region.constBegin(); it != end; ++it)
  {
    Element *element = *it;
    if (!element) continue;
    if (element->type() == Element::Point)
    {
      Point* point = static_cast<Point*>(element);
      extend(point->pos(), element->sheet());
    }
    else
    {
      extend(element->rect(), element->sheet());
    }
  }

  d->activeSubRegionLength += cells().count() - count;

  emit changed(*this);
}

Selection::Element* Selection::eor(const TQPoint& point, Sheet* sheet)
{
  if (isSingular())
  {
    return Region::add(point, sheet);
  }
  return Region::eor(point, sheet);
}

const TQPoint& Selection::anchor() const
{
  return d->anchor;
}

const TQPoint& Selection::cursor() const
{
  return d->cursor;
}

const TQPoint& Selection::marker() const
{
  return d->marker;
}

bool Selection::isSingular() const
{
  return Region::isSingular();
}

TQRect Selection::selectionHandleArea() const
{
  int column, row;

  // complete rows/columns are selected, use the marker.
  if (isColumnOrRowSelected())
  {
    column = d->marker.x();
    row = d->marker.y();
  }
  else
  {
    column = lastRange().right();
    row = lastRange().bottom();
  }
  const Cell* cell = d->view->activeSheet()->cellAt(column, row);

  double xpos = d->view->activeSheet()->dblColumnPos( column );
  double ypos = d->view->activeSheet()->dblRowPos( row );
  double width = cell->dblWidth( column );
  double height = cell->dblHeight( row );

  TQPoint rightBottom( d->view->doc()->zoomItX( xpos + width ),
                      d->view->doc()->zoomItY( ypos + height ) );

  TQRect handle( ( rightBottom.x() - 2 ),
                  ( rightBottom.y() - 2 ),
                  ( 5 ),
                  ( 5 ) );
  return handle;
}

TQString Selection::name(Sheet* sheet) const
{
  return Region::name(sheet ? sheet : d->sheet);
}

void Selection::setSheet(Sheet* sheet)
{
  d->sheet = sheet;
}

Sheet* Selection::sheet() const
{
  return d->sheet;
}

void Selection::setActiveElement(const TQPoint& point)
{
  uint counter = 0;
  Iterator end = cells().end();
  for (Iterator it = cells().begin(); it != end; ++it)
  {
    TQRect range = (*it)->rect();
    if (range.topLeft() == point || range.bottomRight() == point)
    {
      d->anchor = range.topLeft();
      d->cursor = range.bottomRight();
      d->marker = range.bottomRight();
      d->activeElement = it;
      d->activeSubRegionStart = counter;
      d->activeSubRegionLength = 1;
      if (d->view->canvasWidget()->editor())
      {
        d->view->canvasWidget()->editor()->setCursorToRange(counter);
      }
    }
    counter++;
  }
}

void Selection::setActiveElement(uint pos)
{
  if (pos >= cells().count())
  {
    kdDebug() << "Selection::setActiveElement: position exceeds list" << endl;
    d->activeElement = cells().begin();
    return;
  }

  Iterator it = cells().begin() += pos;
  TQRect range = (*it)->rect();
  d->anchor = range.topLeft();
  d->cursor = range.bottomRight();
  d->marker = range.bottomRight();
  d->activeElement = it;
}

Region::Element* Selection::activeElement() const
{
  return (d->activeElement == cells().end()) ? 0 : *d->activeElement;
}

void Selection::clear()
{
  d->activeSubRegionStart = 0;
  d->activeSubRegionLength = 0;
  Region::clear();
  d->activeElement = cells().begin();
}

void Selection::clearSubRegion()
{
  if (isEmpty())
  {
    return;
  }
//   kdDebug() << *this << endl;
//   kdDebug() << d->activeSubRegionStart << endl;
//   kdDebug() << d->activeSubRegionLength << endl;

  Iterator it = cells().begin();
  Iterator end = it += d->activeSubRegionStart;
  end += d->activeSubRegionLength;
  while (it != end)
  {
/*    kdDebug() << (*it)->name() << endl;*/
    delete *it;
    it = cells().remove(it);
  }
  d->activeSubRegionLength = 0;
  d->activeElement = it;
/*  kdDebug() << "ENDE" << endl;*/
}

void Selection::fixSubRegionDimension()
{
  if (d->activeSubRegionStart > cells().count())
  {
    kdDebug() << "Selection::fixSubRegionDimension: start position exceeds list" << endl;
    d->activeSubRegionStart = 0;
    d->activeSubRegionLength = cells().count();
    return;
  }
  if (d->activeSubRegionStart + d->activeSubRegionLength > cells().count())
  {
    kdDebug() << "Selection::fixSubRegionDimension: length exceeds list" << endl;
    d->activeSubRegionLength = cells().count() - d->activeSubRegionStart;
    return;
  }
}

void Selection::setActiveSubRegion(uint start, uint length)
{
//   kdDebug() << k_funcinfo << endl;
  d->activeSubRegionStart = start;
  d->activeSubRegionLength = length;
  fixSubRegionDimension();
  d->activeElement = cells().begin() += d->activeSubRegionStart;
}

TQString Selection::activeSubRegionName() const
{
//   kdDebug() << k_funcinfo << endl;
//   kdDebug() << *this << endl;
//   kdDebug() << "start = " << d->activeSubRegionStart << ", len = " << d->activeSubRegionLength << endl;

  TQStringList names;
  Iterator it = cells().begin();
  it += d->activeSubRegionStart;
  Iterator end = it;
  end += d->activeSubRegionLength;
  while (it != end)
  {
    names += (*it++)->name(d->sheet);
  }
/*  kdDebug() << "ENDE" << endl;*/
  return names.isEmpty() ? "" : names.join(";");
}

void Selection::setMultipleSelection(bool state)
{
  d->multipleSelection = state;
}

const TQValueList<TQColor>& Selection::colors() const
{
  return d->colors;
}

TQRect Selection::lastRange(bool extend) const
{
  TQRect selection = TQRect(d->anchor, d->marker).normalize();
  return extend ? extendToMergedAreas(selection) : selection;
}

TQRect Selection::selection(bool extend) const
{
  TQRect selection = TQRect(d->anchor, d->marker).normalize();
  return extend ? extendToMergedAreas(selection) : selection;
}

TQRect Selection::extendToMergedAreas(TQRect area) const
{
  if (!d->view->activeSheet())
	  return area;

  area = area.normalize(); // TODO Stefan: avoid this
  const Cell *cell = d->view->activeSheet()->cellAt(area.left(), area.top());

  if( Region::Range(area).isColumn() || Region::Range(area).isRow() )
  {
    return area;
  }
  else if ( !(cell->isObscured() && cell->isPartOfMerged()) &&
              (cell->mergedXCells() + 1) >= area.width() &&
              (cell->mergedYCells() + 1) >= area.height())
  {
    /* if just a single cell is selected, we need to merge even when
    the obscuring isn't forced.  But only if this is the cell that
    is doing the obscuring -- we still want to be able to click on a cell
    that is being obscured.
    */
    area.setWidth(cell->mergedXCells() + 1);
    area.setHeight(cell->mergedYCells() + 1);
  }
  else
  {
    int top=area.top();
    int left=area.left();
    int bottom=area.bottom();
    int right=area.right();
    for ( int x = area.left(); x <= area.right(); x++ )
      for ( int y = area.top(); y <= area.bottom(); y++ )
    {
      cell = d->view->activeSheet()->cellAt( x, y );
      if( cell->doesMergeCells())
      {
        right=TQMAX(right,cell->mergedXCells()+x);
        bottom=TQMAX(bottom,cell->mergedYCells()+y);
      }
      else if ( cell->isObscured() && cell->isPartOfMerged() )
      {
        cell = cell->obscuringCells().first();
        left=TQMIN(left,cell->column());
        top=TQMIN(top,cell->row());
        bottom=TQMAX(bottom,cell->row() + cell->mergedYCells());
        right=TQMAX(right,cell->column() + cell->mergedXCells());
      }
    }

    area.setCoords(left,top,right,bottom);
  }
  return area;
}

Selection::Region::Point* Selection::createPoint(const TQPoint& point) const
{
  return new Point(point);
}

Selection::Region::Point* Selection::createPoint(const TQString& string) const
{
  return new Point(string);
}

Selection::Region::Point* Selection::createPoint(const Point& point) const
{
  return new Point(point);
}

Selection::Region::Range* Selection::createRange(const TQRect& rect) const
{
  return new Range(rect);
}

Selection::Region::Range* Selection::createRange(const TQString& string) const
{
  return new Range(string);
}

Selection::Region::Range* Selection::createRange(const Range& range) const
{
  return new Range(range);
}

/***************************************************************************
  class Point
****************************************************************************/

Selection::Point::Point(const TQPoint& point)
  : Region::Point(point),
    m_color(TQt::black),
    m_columnFixed(false),
    m_rowFixed(false)
{
}

Selection::Point::Point(const TQString& string)
  : Region::Point(string),
    m_color(TQt::black),
    m_columnFixed(false),
    m_rowFixed(false)
{
  if (!isValid())
  {
    return;
  }

  uint p = 0;
  // Fixed?
  if (string[p++] == '$')
  {
    m_columnFixed = true;
  }

  //search for the first character != text
  int result = string.find( TQRegExp("[^A-Za-z]+"), p );
  if (string[result] == '$')
  {
    m_rowFixed = true;
  }
}

/***************************************************************************
  class Range
****************************************************************************/

Selection::Range::Range(const TQRect& range)
  : Region::Range(range),
    m_color(TQt::black),
    m_leftFixed(false),
    m_rightFixed(false),
    m_topFixed(false),
    m_bottomFixed(false)
{
}

Selection::Range::Range(const TQString& string)
  : Region::Range(string),
    m_color(TQt::black),
    m_leftFixed(false),
    m_rightFixed(false),
    m_topFixed(false),
    m_bottomFixed(false)
{
  if (!isValid())
  {
    return;
  }

  int delimiterPos = string.find(':');
  if (delimiterPos == -1)
  {
    return;
  }

  Selection::Point ul(string.left(delimiterPos));
  Selection::Point lr(string.mid(delimiterPos + 1));

  if (!ul.isValid() || !lr.isValid())
  {
    return;
  }
  m_leftFixed = ul.columnFixed();
  m_rightFixed = lr.columnFixed();
  m_topFixed = ul.rowFixed();
  m_bottomFixed = lr.rowFixed();
}

} // namespace KSpread
#include "selection.moc"
