/* This file is part of the KDE project Copyright (C) 2005-2006 Stefan Nikolaus 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 */ #include #include #include #include "kspread_cell.h" #include "kspread_doc.h" #include "kspread_global.h" #include "kspread_map.h" #include "kspread_sheet.h" #include "kspread_util.h" #include "kspread_view.h" #include "region.h" namespace KSpread { class Region::Private { public: Private() { view = 0; } View* view; TQValueList cells; }; /*************************************************************************** class Region ****************************************************************************/ Region::Region() { d = new Private(); } Region::Region(View* view, const TQString& string, Sheet* sheet) { d = new Private(); d->view = view; if (string.isEmpty()) { return; } TQStringList substrings = TQStringList::split(';', string); TQStringList::ConstIterator end = substrings.constEnd(); for (TQStringList::ConstIterator it = substrings.constBegin(); it != end; ++it) { TQString sRegion = *it; if (!sheet) { sheet = filterSheetName(sRegion); } int delimiterPos = sRegion.find(':'); if (delimiterPos > -1) { // range Point ul(sRegion.left(delimiterPos)); Point lr(sRegion.mid(delimiterPos + 1)); if (ul.isValid() && lr.isValid()) { Range* range = createRange(sRegion); range->setSheet(sheet); d->cells.append(range); } else if (ul.isValid()) { Point* point = createPoint(sRegion.left(delimiterPos)); point->setSheet(sheet); d->cells.append(point); } else // lr.isValid() { Point* point = createPoint(sRegion.right(delimiterPos + 1)); point->setSheet(sheet); d->cells.append(point); } } else { // single cell Point* point = createPoint(sRegion); point->setSheet(sheet); d->cells.append(point); } } } Region::Region(const TQRect& rect, Sheet* sheet) { d = new Private(); if (rect.isNull()) { kdError(36001) << "Region::Region(const TQRect&): TQRect is empty!" << endl; return; } add(rect, sheet); } Region::Region(const TQPoint& point, Sheet* sheet) { d = new Private(); if (point.isNull()) { kdError(36001) << "Region::Region(const TQPoint&): TQPoint is empty!" << endl; return; } add(point, sheet); } Region::Region(const Region& list) { d = new Private(); d->view = list.d->view; ConstIterator end(list.d->cells.constEnd()); for (ConstIterator it = list.d->cells.constBegin(); it != end; ++it) { Element *element = *it; if (element->type() == Element::Point) { Point* point = static_cast(element); d->cells.append(createPoint(*point)); } else { Range* range = static_cast(element); d->cells.append(createRange(*range)); } } } Region::Region(int x, int y, Sheet* sheet) { d = new Private(); if (x<1 || y<1) { kdError(36001) << "Region::Region(int x, int y): Coordinates are invalid!" << endl; return; } add(TQPoint(x,y), sheet); } Region::Region(int x, int y, int width, int height, Sheet* sheet) { d = new Private(); if (x<1 || y<1 || width<1 || height<1) { kdError(36001) << "Region::Region(int x, int y, int width, int height): Dimensions are invalid!" << endl; return; } add(TQRect(x,y,width,height), sheet); } Region::~Region() { d->cells.clear(); delete d; } View* Region::view() const { Q_ASSERT(d->view); return d->view; } void Region::setView(View* view) { d->view = view; } bool Region::isValid() const { ConstIterator end = d->cells.constEnd(); for (ConstIterator it = d->cells.constBegin(); it != end; ++it) { if (!(*it)->isValid()) { return false; } } return true; } bool Region::isSingular() const { if (d->cells.isEmpty() || d->cells.count() > 1 || (*d->cells.constBegin())->type() != Element::Point) { return false; } return true; } bool Region::isContiguous() const { if (d->cells.count() != 1 || !isValid()) { return false; } return true; } TQString Region::name(Sheet* originSheet) const { TQStringList names; ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; names += element->name(originSheet); } return names.isEmpty() ? "" : names.join(";"); } Region::Element* Region::add(const TQPoint& point, Sheet* sheet) { // kdDebug() << k_funcinfo << endl; if (point.x() < 1 || point.y() < 1) { return 0; } Iterator it = insert(d->cells.end(), point, sheet, false); return (it == d->cells.end()) ? 0 : *it; } Region::Element* Region::add(const TQRect& range, Sheet* sheet) { if (range.normalize().width() == 0 || range.normalize().height() == 0) { return 0; } if (range.size() == TQSize(1,1)) { return add(range.topLeft(), sheet); } Iterator it = insert(d->cells.end(), range, sheet, false); return (it == d->cells.end()) ? 0 : *it; } Region::Element* Region::add(const Region& region) { ConstIterator endOfList(region.d->cells.constEnd()); for (ConstIterator it = region.d->cells.constBegin(); it != endOfList; ++it) { add((*it)->rect(), (*it)->sheet()); } return d->cells.isEmpty() ? 0 : d->cells.last(); } void Region::sub(const TQPoint& point) { // TODO Stefan: Improve! Iterator endOfList(d->cells.end()); for (Iterator it = d->cells.begin(); it != endOfList; ++it) { Element *element = *it; if (element->rect() == TQRect(point,point)) { delete element; d->cells.remove(element); break; } } } void Region::sub(const TQRect& range) { // TODO Stefan: Improve! Iterator endOfList(d->cells.end()); for (Iterator it = d->cells.begin(); it != endOfList; ++it) { Element *element = *it; if (element->rect().normalize() == range.normalize()) { delete element; d->cells.remove(element); break; } } } void Region::sub(const Region& region) { ConstIterator endOfList(region.constEnd()); for (ConstIterator it = region.constBegin(); it != endOfList; ++it) { Element *element = *it; if (element->type() == Element::Point) { Point* point = static_cast(element); sub(point->pos()); } else { sub(element->rect()); } } } Region::Element* Region::eor(const TQPoint& point, Sheet* sheet) { bool containsPoint = false; Iterator it = cells().begin(); Iterator endOfList = cells().end(); while (it != endOfList) { if (!(*it)->contains(point)) { ++it; continue; } containsPoint = true; int x = point.x(); int y = point.y(); TQRect fullRange = (*it)->rect().normalize(); delete *it; it = cells().remove(it); // top range int left = fullRange.left(); int top = fullRange.top(); int width = fullRange.width(); int height = y - top; if (height > 0) { insert(it, TQRect(left, top, width, height), sheet); } // left range left = fullRange.left(); top = y; width = TQMAX(0, x - left); height = 1; if (width > 0) { insert(it, TQRect(left, top, width, height), sheet); } // right range left = TQMIN(x+1, fullRange.right()); top = y; width = TQMAX(0, fullRange.right() - x); height = 1; if (width > 0) { insert(it, TQRect(left, top, width, height), sheet); } // bottom range left = fullRange.left(); top = y+1; width = fullRange.width(); height = TQMAX(0, fullRange.bottom() - y); if (height > 0) { insert(it, TQRect(left, top, width, height), sheet); } return *it; } if (!containsPoint) { return add(point, sheet); } return 0; } Region::Iterator Region::insert(Region::Iterator pos, const TQPoint& point, Sheet* sheet, bool multi) { if (point.x() < 1 || point.y() < 1) { return pos; } bool containsPoint = false; // bool adjacentPoint = false; // TQRect neighbour; // we don't have to check for occurences? if (multi) { Point* rpoint = createPoint(point); rpoint->setSheet(sheet); return d->cells.insert(pos, rpoint); } ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; if (sheet && sheet != element->sheet()) { continue; } if (element->contains(point)) { containsPoint = true; break; } /* else { neighbour = element->rect().normalize(); neighbour.setTopLeft(neighbour.topLeft() - TQPoint(1,1)); neighbour.setBottomRight(neighbour.bottomRight() + TQPoint(1,1)); if (neighbour.contains(point)) { adjacentPoint = true; // TODO Stefan: Implement! break; } }*/ } if ( !containsPoint ) { Point* rpoint = createPoint(point); rpoint->setSheet(sheet); return d->cells.insert(pos, rpoint); } return pos; } Region::Iterator Region::insert(Region::Iterator pos, const TQRect& range, Sheet* sheet, bool multi) { if (range.size() == TQSize(1,1)) { return insert(pos, range.topLeft(), sheet); } if (multi) { Range* rrange = createRange(range); rrange->setSheet(sheet); return d->cells.insert(pos, rrange); } bool containsRange = false; Iterator it( d->cells.begin() ); Iterator endOfList( d->cells.end() ); while ( it != endOfList ) { if (sheet && sheet != (*it)->sheet()) { ++it; continue; } if ((*it)->contains(range)) { containsRange = true; } else if (range.contains((*it)->rect())) { delete *it; it = d->cells.remove(it); continue; } ++it; } if ( !containsRange ) { Range* rrange = createRange(range); rrange->setSheet(sheet); return d->cells.insert(pos, rrange); } return pos; } bool Region::isColumnAffected(uint col) const { ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; TQRect normalizedRegion = element->rect().normalize(); if ((int)col >= normalizedRegion.left() && (int)col <= normalizedRegion.right()) { return true; } } return false; } bool Region::isRowAffected(uint row) const { ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; TQRect normalizedRegion = element->rect().normalize(); if ((int)row >= normalizedRegion.top() && (int)row <= normalizedRegion.bottom()) { return true; } } return false; } bool Region::isColumnSelected(uint col) const { ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; TQRect region = element->rect().normalize(); if ((col == 0 || ((int)col >= region.left() && (int)col <= region.right())) && region.top() == 1 && region.bottom() == KS_rowMax) { return true; } } return false; } bool Region::isRowSelected(uint row) const { ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; TQRect region = element->rect().normalize(); if ((row == 0 || ((int)row >= region.top() && (int)row <= region.bottom())) && region.left() == 1 && region.right() == KS_colMax) { return true; } } return false; } bool Region::isColumnOrRowSelected() const { ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; TQRect region = element->rect().normalize(); if ((region.top() == 1 && region.bottom() == KS_rowMax) || (region.left() == 1 && region.right() == KS_colMax)) { return true; } } return false; } bool Region::contains(const TQPoint& point, Sheet* sheet) const { if (d->cells.isEmpty()) { return false; } ConstIterator endOfList(d->cells.constEnd()); for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) { Element *element = *it; if (element->contains(point)) { if (sheet && element->sheet() != sheet) { return false; } return true; } } return false; } bool Region::isEmpty() const { return d->cells.isEmpty(); } void Region::clear() { Iterator end(d->cells.end()); for (Iterator it = d->cells.begin(); it != end; it = d->cells.remove(it)) { delete *it; } } TQRect Region::boundingRect() const { int left = KS_colMax; int right = 1; int top = KS_rowMax; int bottom = 1; Region::ConstIterator endOfList = cells().constEnd(); for (Region::ConstIterator it = cells().constBegin(); it != endOfList; ++it) { TQRect range = (*it)->rect().normalize(); if (range.left() < left) { left = range.left(); } if (range.right() > right) { right = range.right(); } if (range.top() < top) { top = range.top(); } if (range.bottom() > bottom) { bottom = range.bottom(); } } return TQRect(left, top, right-left+1, bottom-top+1); } Region::ConstIterator Region::constBegin() const { return d->cells.constBegin(); } Region::ConstIterator Region::constEnd() const { return d->cells.constEnd(); } TQValueList& Region::cells() const { return d->cells; } bool Region::operator==(const Region& other) const { ConstIterator endOfList(d->cells.constEnd()); ConstIterator endOfOtherList(other.d->cells.constEnd()); ConstIterator it = d->cells.constBegin(); ConstIterator it2 = other.d->cells.constBegin(); while (it != endOfList && it2 != endOfOtherList) { if ((*it++)->rect() != (*it2++)->rect()) { return false; } } return true; } void Region::operator=(const Region& other) { d->view = other.d->view; clear(); ConstIterator end(other.d->cells.constEnd()); for (ConstIterator it = other.d->cells.constBegin(); it != end; ++it) { Element *element = *it; if (element->type() == Element::Point) { Point* point = static_cast(element); d->cells.append(createPoint(*point)); } else { Range* range = static_cast(element); d->cells.append(createRange(*range)); } } } Sheet* Region::filterSheetName(TQString& sRegion) { Sheet* sheet = 0; int delimiterPos = sRegion.find( '!' ); if (delimiterPos > -1) { TQString sheetName = sRegion.left(delimiterPos); // remove the '!' sRegion = sRegion.right(sRegion.length() - delimiterPos - 1); sheet = d->view->doc()->map()->findSheet(sheetName); if (!sheet) { kdDebug() << "Sheet " << sheetName << " not found. Using active sheet!" << endl; sheet = d->view->activeSheet(); } } return sheet; } Region::Point* Region::createPoint(const TQPoint& point) const { return new Point(point); } Region::Point* Region::createPoint(const TQString& string) const { return new Point(string); } Region::Point* Region::createPoint(const Point& point) const { return new Point(point); } Region::Range* Region::createRange(const TQRect& rect) const { return new Range(rect); } Region::Range* Region::createRange(const TQString& string) const { return new Range(string); } Region::Range* Region::createRange(const Range& range) const { return new Range(range); } /*************************************************************************** class Element ****************************************************************************/ Region::Element::Element() : m_sheet(0) { } Region::Element::~Element() { } /*************************************************************************** class Point ****************************************************************************/ Region::Point::Point(const TQPoint& point) : Region::Element(), m_point(point) { } Region::Point::Point(const TQString& sCell) : Region::Element(), m_point() { uint length = sCell.length(); if (length == 0) { kdDebug(36001) << "Region::Point::init: length = 0" << endl; return; } TQString string = sCell;//Region::filterSheetName(sCell); uint p = 0; // Fixed ? if (string[0] == '$') { p++; } // Malformed ? if (p == length) { kdDebug(36001) << "Region::Point::init: no point after '$' (string: '" << string.mid(p) << "'" << endl; return; } if (string[p] < 'A' || string[p] > 'Z') { if (string[p] < 'a' || string[p] > 'z') { kdDebug(36001) << "Region::Point::init: wrong first character in point (string: '" << string.mid(p) << "'" << endl; return; } } //default is error int x = -1; //search for the first character != text int result = string.find( TQRegExp("[^A-Za-z]+"), p ); //get the colomn number for the character between actual position and the first non text charakter if ( result != -1 ) { x = util_decodeColumnLabelText( string.mid( p, result - p ) ); // x is defined now } else // If there isn't any, then this is not a point -> return { kdDebug(36001) << "Region::Point::init: no number in string (string: '" << string.mid( p, result ) << "'" << endl; return; } p = result; //limit is KS_colMax if ( x > KS_colMax ) { kdDebug(36001) << "Region::Point::init: column value too high (col: " << x << ")" << endl; return; } // Malformed ? if (p == length) { kdDebug(36001) << "Region::Point::init: p==length after cols" << endl; return; } if (string[p] == '$') { p++; // Malformed ? if ( p == length ) { kdDebug(36001) << "Region::Point::init: p==length after $ of row" << endl; return; } } uint p2 = p; while ( p < length ) { if (!TQChar(string[p++]).isDigit()) { kdDebug(36001) << "Region::Point::init: no number" << endl; return; } } bool ok; int y = string.mid( p2, p-p2 ).toInt( &ok ); if ( !ok ) { kdDebug(36001) << "Region::Point::init: Invalid number (string: '" << string.mid( p2, p-p2 ) << "'" << endl; return; } if ( y > KS_rowMax ) { kdDebug(36001) << "Region::Point::init: row value too high (row: " << y << ")" << endl; return; } if ( y <= 0 ) { kdDebug(36001) << "Region::Point::init: y <= 0" << endl; return; } m_point = TQPoint(x, y); } Region::Point::~Point() { } TQString Region::Point::name(Sheet* originSheet) const { TQString name = ""; if (m_sheet && m_sheet != originSheet) { name = m_sheet->sheetName() + "!"; } return name + Cell::name(m_point.x(), m_point.y()); } bool Region::Point::contains(const TQPoint& point) const { return (m_point == point); } bool Region::Point::contains(const TQRect& range) const { return (range.width() == 1) && (range.height() == 1) && (range.topLeft() == m_point); } /*************************************************************************** class Range ****************************************************************************/ Region::Range::Range(const TQRect& rect) : Region::Element(), m_range(rect) { } Region::Range::Range(const TQString& sRange) : Region::Element(), m_range() { int delimiterPos = sRange.find(':'); if (delimiterPos == -1) { return; } //Region::filterSheetName(sRange); Region::Point ul(sRange.left(delimiterPos)); Region::Point lr(sRange.mid(delimiterPos + 1)); if (!ul.isValid() || !lr.isValid()) { return; } m_range = TQRect(ul.pos(), lr.pos()); } Region::Range::~Range() { } TQString Region::Range::name(Sheet* originSheet) const { TQString name = ""; if (m_sheet && m_sheet != originSheet) { name = m_sheet->sheetName() + "!"; } return name + Cell::name(m_range.left(), m_range.top()) + ":" + Cell::name(m_range.right(), m_range.bottom() ); } bool Region::Range::contains(const TQPoint& point) const { return m_range.normalize().contains(point); } bool Region::Range::contains(const TQRect& range) const { return m_range.normalize().contains(range.normalize()); } } // namespace KSpread