You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7349 lines
232 KiB
7349 lines
232 KiB
15 years ago
|
/* This file is part of the KDE project
|
||
|
|
||
|
Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
|
||
|
Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
|
||
|
Copyright 2004-2005 Tomas Mecir <mecirt@gmail.com>
|
||
|
Copyright 2004-2006 Inge Wallin <inge@lysator.liu.se>
|
||
|
Copyright 1999-2002,2004,2005 Laurent Montel <montel@kde.org>
|
||
|
Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
|
||
|
Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
|
||
|
Copyright 2002-2003 Norbert Andres <nandres@web.de>
|
||
|
Copyright 2003 Reinhart Geiser <geiseri@kde.org>
|
||
|
Copyright 2003-2005 Meni Livne <livne@kde.org>
|
||
|
Copyright 2003 Peter Simonsson <psn@linux.se>
|
||
|
Copyright 1999-2002 David Faure <faure@kde.org>
|
||
|
Copyright 2000-2002 Werner Trobin <trobin@kde.org>
|
||
|
Copyright 1999,2002 Harri Porten <porten@kde.org>
|
||
|
Copyright 2002 John Dailey <dailey@vt.edu>
|
||
|
Copyright 1998-2000 Torben Weis <weis@kde.org>
|
||
|
Copyright 2000 Bernd Wuebben <wuebben@kde.org>
|
||
|
Copyright 2000 Simon Hausmann <hausmann@kde.org
|
||
|
Copyright 1999 Stephan Kulow <coolo@kde.org>
|
||
|
Copyright 1999 Michael Reiher <michael.reiher.gmx.de>
|
||
|
Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
|
||
|
Copyright 1998-1999 Reginald Stadlbauer <reggie@kde.org>
|
||
|
|
||
|
|
||
|
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 <stdlib.h>
|
||
|
#include <ctype.h>
|
||
|
#include <float.h>
|
||
|
#include <math.h>
|
||
|
|
||
|
#include <qapplication.h>
|
||
|
#include <qpopupmenu.h>
|
||
|
#include <qregexp.h>
|
||
|
|
||
|
#include "kspread_canvas.h"
|
||
|
#include "kspread_condition.h"
|
||
|
#include "kspread_doc.h"
|
||
|
#include "kspread_format.h"
|
||
|
#include "kspread_global.h"
|
||
|
#include "kspread_map.h"
|
||
|
#include "kspread_sheet.h"
|
||
|
#include "kspread_sheetprint.h"
|
||
|
#include "kspread_style.h"
|
||
|
#include "kspread_style_manager.h"
|
||
|
#include "kspread_util.h"
|
||
|
#include "ksploadinginfo.h"
|
||
|
#include "kspread_genvalidationstyle.h"
|
||
|
#include "kspread_locale.h"
|
||
|
#include "kspread_value.h"
|
||
|
#include "kspread_view.h"
|
||
|
#include "kspread_value.h"
|
||
|
#include "formula.h"
|
||
|
#include "selection.h"
|
||
|
#include "valueconverter.h"
|
||
|
#include "valueformatter.h"
|
||
|
#include "valueparser.h"
|
||
|
|
||
|
#include <KoStyleStack.h>
|
||
|
#include <KoRect.h>
|
||
|
#include <KoXmlNS.h>
|
||
|
#include <KoDom.h>
|
||
|
#include <KoXmlWriter.h>
|
||
|
#include <KoOasisStyles.h>
|
||
|
|
||
|
#include <kmessagebox.h>
|
||
|
|
||
|
#include <kdebug.h>
|
||
|
|
||
|
using namespace KSpread;
|
||
|
|
||
|
#define BORDER_SPACE 1
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A pointer to the decimal separator
|
||
|
*/
|
||
|
|
||
|
namespace Cell_LNS
|
||
|
{
|
||
|
QChar decimal_point = '\0';
|
||
|
}
|
||
|
|
||
|
using namespace Cell_LNS;
|
||
|
|
||
|
|
||
|
// Some variables are placed in Cell::Extra because normally they're
|
||
|
// not required in simple case of cell(s). For example, most plain
|
||
|
// text cells don't need to store information about spanned columns
|
||
|
// and rows, as this is only the case with merged cells.
|
||
|
//
|
||
|
// When the cell is getting complex (e.g. merged with other cells,
|
||
|
// contains rich text, has validation criteria, etc), this Cell::Extra
|
||
|
// is allocated by Cell::Private and starts to be
|
||
|
// available. Otherwise, it won't exist at all.
|
||
|
|
||
|
class Cell::Extra
|
||
|
{
|
||
|
public:
|
||
|
Extra() {}
|
||
|
|
||
|
// Not empty when the cell holds a link
|
||
|
QString link;
|
||
|
|
||
|
// Number of cells explicitly merged by the user in X and Y directions.
|
||
|
int mergedXCells;
|
||
|
int mergedYCells;
|
||
|
|
||
|
// Number of additional cells.
|
||
|
int extraXCells;
|
||
|
int extraYCells;
|
||
|
|
||
|
// If this cell overlaps other cells, then we have the cells width and
|
||
|
// height stored here. These values do not mean anything unless
|
||
|
// extraXCells and/or extraYCells are different from 0.
|
||
|
double extraWidth;
|
||
|
double extraHeight;
|
||
|
|
||
|
// A list of cells that obscure this one.
|
||
|
// If this list is not empty, then this cell is obscured by another
|
||
|
// enlarged object. This means that we have to call this object in order
|
||
|
// of painting it for example instead of painting 'this'.
|
||
|
//
|
||
|
// FIXME (comment): If the list consists of more than one obscuring
|
||
|
// element, then is there an order between them that
|
||
|
// is important?
|
||
|
QValueList<Cell*> obscuringCells;
|
||
|
|
||
|
// If non-NULL, contains a pointer to a condition or a validity test.
|
||
|
Conditions *conditions;
|
||
|
Validity *validity;
|
||
|
|
||
|
// Store the number of line when multirow is used (default is 0)
|
||
|
int nbLines;
|
||
|
|
||
|
private:
|
||
|
// Don't allow implicit copy.
|
||
|
Extra& operator=( const Extra& );
|
||
|
};
|
||
|
|
||
|
|
||
|
class Cell::Private
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
Private();
|
||
|
~Private();
|
||
|
|
||
|
public:
|
||
|
|
||
|
// This cell's row and column. If either of them is 0, this is the
|
||
|
// default cell and its row/column can not be determined. Note that
|
||
|
// in the isDefault() method, only column is tested.
|
||
|
int row;
|
||
|
int column;
|
||
|
|
||
|
// Value of the cell, either typed by user or as result of formula
|
||
|
Value value;
|
||
|
|
||
|
// Holds the user's input.
|
||
|
//
|
||
|
// FIXME:
|
||
|
// Eventually, we'll want to get rid of strText and generate
|
||
|
// user's input on-the-fly. Then, for normal cells, we'll generate
|
||
|
// this string using converter()->asString
|
||
|
// (value()).
|
||
|
//
|
||
|
// Here the problem is, that strText also holds the formula -
|
||
|
// we'll need to provide some method to generate it from the
|
||
|
// parsed version, created in KSpread::Formula. Hence, we won't be
|
||
|
// able to get rid of strText until we switch to the new formula
|
||
|
// parser and until we write some method that re-generates the
|
||
|
// input formula...
|
||
|
//
|
||
|
// Alternately, we can keep using strText for formulas and
|
||
|
// generate it dynamically for static cells...
|
||
|
//
|
||
|
// /Tomas
|
||
|
//
|
||
|
QString strText;
|
||
|
|
||
|
// This is the text we want to display. Not necessarily the same
|
||
|
// as strText, e.g. strText="1" and strOutText="1.00" Also holds
|
||
|
// value that we got from calculation, formerly known as
|
||
|
// strFormulaOut
|
||
|
QString strOutText;
|
||
|
|
||
|
// the Formula object for the cell
|
||
|
KSpread::Formula *formula;
|
||
|
|
||
|
// Position and dimension of displayed text.
|
||
|
// FIXME (comment): Which coordinate system? pixels? mm/cm? zoom?
|
||
|
double textX;
|
||
|
double textY;
|
||
|
double textWidth;
|
||
|
double textHeight;
|
||
|
|
||
|
// result of "fm.ascent()" in makeLayout. used in offsetAlign.
|
||
|
int fmAscent;
|
||
|
|
||
|
// Pointers to neighboring cells.
|
||
|
// FIXME (comment): Which order?
|
||
|
Cell *nextCell;
|
||
|
Cell *previousCell;
|
||
|
|
||
|
bool hasExtra() const { return (cellExtra != 0); };
|
||
|
Extra *extra();
|
||
|
|
||
|
Format *format;
|
||
|
Q_UINT32 flags;
|
||
|
|
||
|
private:
|
||
|
// "Extra stuff", see explanation for Cell::Extra.
|
||
|
Extra *cellExtra;
|
||
|
};
|
||
|
|
||
|
|
||
|
Cell::Private::Private()
|
||
|
{
|
||
|
// Some basic data.
|
||
|
row = 0;
|
||
|
column = 0;
|
||
|
value = Value::empty();
|
||
|
formula = 0;
|
||
|
|
||
|
// Formatting
|
||
|
textX = 0.0;
|
||
|
textY = 0.0;
|
||
|
textWidth = 0.0;
|
||
|
textHeight = 0.0;
|
||
|
fmAscent = 0;
|
||
|
|
||
|
nextCell = 0;
|
||
|
previousCell = 0;
|
||
|
|
||
|
// Default is to not have the "extra" stuff in a cell.
|
||
|
cellExtra = 0;
|
||
|
format = 0;
|
||
|
flags = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
Cell::Private::~Private()
|
||
|
{
|
||
|
delete cellExtra;
|
||
|
delete formula;
|
||
|
}
|
||
|
|
||
|
|
||
|
Cell::Extra* Cell::Private::extra()
|
||
|
{
|
||
|
if ( !cellExtra ) {
|
||
|
cellExtra = new Extra;
|
||
|
cellExtra->conditions = 0;
|
||
|
cellExtra->validity = 0;
|
||
|
|
||
|
cellExtra->mergedXCells = 0;
|
||
|
cellExtra->mergedYCells = 0;
|
||
|
cellExtra->extraXCells = 0;
|
||
|
cellExtra->extraYCells = 0;
|
||
|
cellExtra->extraWidth = 0.0;
|
||
|
cellExtra->extraHeight = 0.0;
|
||
|
cellExtra->nbLines = 0;
|
||
|
// cellExtra->highlight = QColor(0,0,0);
|
||
|
}
|
||
|
|
||
|
return cellExtra;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Cell
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
|
||
|
Cell::Cell( Sheet * _sheet, int _column, int _row )
|
||
|
{
|
||
|
d = new Private;
|
||
|
d->row = _row;
|
||
|
d->column = _column;
|
||
|
d->format = new Format(_sheet, _sheet->doc()->styleManager()->defaultStyle());
|
||
|
d->format->setCell(this);
|
||
|
clearAllErrors();
|
||
|
}
|
||
|
|
||
|
|
||
|
Cell::Cell( Sheet * _sheet, Style * _style, int _column, int _row )
|
||
|
{
|
||
|
d = new Private;
|
||
|
d->row = _row;
|
||
|
d->column = _column;
|
||
|
d->format = new Format( _sheet, _style );
|
||
|
d->format->setCell(this);
|
||
|
|
||
|
clearAllErrors();
|
||
|
}
|
||
|
|
||
|
Format* Cell::format() const
|
||
|
{
|
||
|
return d->format;
|
||
|
}
|
||
|
|
||
|
// Return the sheet that this cell belongs to.
|
||
|
Sheet * Cell::sheet() const
|
||
|
{
|
||
|
return d->format->sheet();
|
||
|
}
|
||
|
|
||
|
// Return true if this is the default cell.
|
||
|
bool Cell::isDefault() const
|
||
|
{
|
||
|
return ( d->column == 0 );
|
||
|
}
|
||
|
|
||
|
// Return the row number of this cell.
|
||
|
int Cell::row() const
|
||
|
{
|
||
|
// Make sure this isn't called for the default cell. This assert
|
||
|
// can save you (could have saved me!) the hassle of some very
|
||
|
// obscure bugs.
|
||
|
|
||
|
if ( isDefault() )
|
||
|
{
|
||
|
kdWarning(36001) << "Error: Calling Cell::row() for default cell" << endl;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return d->row;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the column number of this cell.
|
||
|
//
|
||
|
int Cell::column() const
|
||
|
{
|
||
|
// Make sure this isn't called for the default cell. This assert
|
||
|
// can save you (could have saved me!) the hassle of some very
|
||
|
// obscure bugs.
|
||
|
if ( isDefault() )
|
||
|
{
|
||
|
kdWarning(36001) << "Error: Calling Cell::column() for default cell" << endl;
|
||
|
return 0;
|
||
|
}
|
||
|
return d->column;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the name of this cell, i.e. the string that the user would
|
||
|
// use to reference it. Example: A1, BZ16
|
||
|
//
|
||
|
QString Cell::name() const
|
||
|
{
|
||
|
return name( d->column, d->row );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the name of any cell given by (col, row).
|
||
|
//
|
||
|
QString Cell::name( int col, int row )
|
||
|
{
|
||
|
return columnName( col ) + QString::number( row );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the name of this cell, including the sheet name.
|
||
|
// Example: sheet1!A5
|
||
|
//
|
||
|
QString Cell::fullName() const
|
||
|
{
|
||
|
return fullName( sheet(), d->column, d->row );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the full name of any cell given a sheet and (col, row).
|
||
|
//
|
||
|
QString Cell::fullName( const Sheet* s, int col, int row )
|
||
|
{
|
||
|
return s->sheetName() + "!" + name( col, row );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the symbolic name of the column of this cell. Examples: A, BB.
|
||
|
//
|
||
|
QString Cell::columnName() const
|
||
|
{
|
||
|
return columnName( d->column );
|
||
|
}
|
||
|
|
||
|
KLocale* Cell::locale() const
|
||
|
{
|
||
|
return d->format->sheet()->doc()->locale();
|
||
|
}
|
||
|
|
||
|
// Return the symbolic name of any column.
|
||
|
//
|
||
|
QString Cell::columnName( uint column )
|
||
|
{
|
||
|
QString str;
|
||
|
unsigned digits = 1;
|
||
|
unsigned offset = 0;
|
||
|
|
||
|
column--;
|
||
|
|
||
|
if( column > 4058115285U ) return QString("@@@");
|
||
|
|
||
|
for( unsigned limit = 26; column >= limit+offset; limit *= 26, digits++ )
|
||
|
offset += limit;
|
||
|
|
||
|
for( unsigned c = column - offset; digits; --digits, c/=26 )
|
||
|
str.prepend( QChar( 'A' + (c%26) ) );
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return true if this cell is a formula.
|
||
|
//
|
||
|
bool Cell::isFormula() const
|
||
|
{
|
||
|
return d->strText[0] == '=';
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the input text of this cell. This could, for instance, be a
|
||
|
// formula.
|
||
|
//
|
||
|
// FIXME: These two functions are inconsistently named. It should be
|
||
|
// either text() and outText() or strText() and strOutText().
|
||
|
//
|
||
|
QString Cell::text() const
|
||
|
{
|
||
|
return d->strText;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the out text, i.e. the text that is visible in the cells
|
||
|
// square when shown. This could, for instance, be the calculated
|
||
|
// result of a formula.
|
||
|
//
|
||
|
QString Cell::strOutText() const
|
||
|
{
|
||
|
return d->strOutText;
|
||
|
}
|
||
|
|
||
|
Formula *Cell::formula () const
|
||
|
{
|
||
|
return d->formula;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the value of this cell.
|
||
|
//
|
||
|
const Value Cell::value() const
|
||
|
{
|
||
|
return d->value;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set the value of this cell. It also clears all errors if the value
|
||
|
// itself is not an error.
|
||
|
//
|
||
|
// In addition to this, it calculates the outstring and sets the dirty
|
||
|
// flags so that a redraw is forced.
|
||
|
//
|
||
|
void Cell::setValue( const Value& v )
|
||
|
{
|
||
|
if (v.type() != Value::Error)
|
||
|
clearAllErrors();
|
||
|
|
||
|
//If the value has not changed then we don't need to do anything
|
||
|
//(ie. no need to relayout, update dependant cells etc.),
|
||
|
//unless this cell contains a formula, in which case its dependancies might have changed
|
||
|
//even though the value has not. For example, if this cell was previously empty (and its value is
|
||
|
//therefore empty) and a new dependency upon an empty cell has been added. The new value would still
|
||
|
//be empty, but the dependencies need to be updated (via the call to valueChanged() below).
|
||
|
if ( ( d->value == v ) && ( !isFormula() ) )
|
||
|
return;
|
||
|
|
||
|
d->value = v;
|
||
|
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
setFlag(Flag_TextFormatDirty);
|
||
|
|
||
|
// Format and set the outText.
|
||
|
setOutputText();
|
||
|
|
||
|
// Set the displayed text, if we hold an error value.
|
||
|
if (d->value.type() == Value::Error)
|
||
|
d->strOutText = d->value.errorMessage ();
|
||
|
|
||
|
// Value of the cell has changed - trigger necessary actions
|
||
|
valueChanged ();
|
||
|
|
||
|
if ( !format()->sheet()->isLoading() )
|
||
|
format()->sheet()->setRegionPaintDirty(cellRect());
|
||
|
}
|
||
|
|
||
|
void Cell::setCellValue (const Value &v, FormatType fmtType, const QString &txt)
|
||
|
{
|
||
|
if ( !txt.isNull() )
|
||
|
{
|
||
|
d->strText = txt;
|
||
|
if ( isFormula() )
|
||
|
makeFormula();
|
||
|
}
|
||
|
else if ( !isFormula() )
|
||
|
d->strText = sheet()->doc()->converter()->asString (v).asString();
|
||
|
if (fmtType != No_format)
|
||
|
format()->setFormatType (fmtType);
|
||
|
setValue (v);
|
||
|
}
|
||
|
|
||
|
// FIXME: Continue commenting and cleaning here (ingwa)
|
||
|
|
||
|
|
||
|
Cell* Cell::previousCell() const
|
||
|
{
|
||
|
return d->previousCell;
|
||
|
}
|
||
|
|
||
|
Cell* Cell::nextCell() const
|
||
|
{
|
||
|
return d->nextCell;
|
||
|
}
|
||
|
|
||
|
void Cell::setPreviousCell( Cell* c )
|
||
|
{
|
||
|
d->previousCell = c;
|
||
|
}
|
||
|
|
||
|
void Cell::setNextCell( Cell* c )
|
||
|
{
|
||
|
d->nextCell = c;
|
||
|
}
|
||
|
|
||
|
Validity* Cell::getValidity( int newStruct )
|
||
|
{
|
||
|
if ( (!newStruct) && (!d->hasExtra()))
|
||
|
//we don't have validity struct and we don't want one
|
||
|
return 0;
|
||
|
|
||
|
if( ( d->extra()->validity == 0 ) && ( newStruct == -1 ) )
|
||
|
d->extra()->validity = new Validity;
|
||
|
return d->extra()->validity;
|
||
|
}
|
||
|
|
||
|
void Cell::removeValidity()
|
||
|
{
|
||
|
if (!d->hasExtra())
|
||
|
return;
|
||
|
|
||
|
delete d->extra()->validity;
|
||
|
d->extra()->validity = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Cell::copyFormat( const int column , const int row )
|
||
|
{
|
||
|
const Cell * cell = format()->sheet()->cellAt( column , row );
|
||
|
|
||
|
copyFormat( cell );
|
||
|
}
|
||
|
|
||
|
void Cell::copyFormat( const Cell* cell )
|
||
|
{
|
||
|
|
||
|
Q_ASSERT(cell);
|
||
|
|
||
|
d->value.setFormat(cell->d->value.format());
|
||
|
format()->copy(*(cell->format()));
|
||
|
|
||
|
/*format()->setAlign( cell->format()->align( _column, _row ) );
|
||
|
format()->setAlignY( cell->format()->alignY( _column, _row ) );
|
||
|
format()->setTextFont( cell->format()->textFont( _column, _row ) );
|
||
|
format()->setTextColor( cell->format()->textColor( _column, _row ) );
|
||
|
format()->setBgColor( cell->bgColor( _column, _row) );
|
||
|
setLeftBorderPen( cell->leftBorderPen( _column, _row ) );
|
||
|
setTopBorderPen( cell->topBorderPen( _column, _row ) );
|
||
|
setBottomBorderPen( cell->bottomBorderPen( _column, _row ) );
|
||
|
setRightBorderPen( cell->rightBorderPen( _column, _row ) );
|
||
|
format()->setFallDiagonalPen( cell->format()->fallDiagonalPen( _column, _row ) );
|
||
|
format()->setGoUpDiagonalPen( cell->format()->goUpDiagonalPen( _column, _row ) );
|
||
|
format()->setBackGroundBrush( cell->backGroundBrush( _column, _row) );
|
||
|
format()->setPrecision( cell->format()->precision( _column, _row ) );
|
||
|
format()->setPrefix( cell->format()->prefix( _column, _row ) );
|
||
|
format()->setPostfix( cell->format()->postfix( _column, _row ) );
|
||
|
format()->setFloatFormat( cell->format()->floatFormat( _column, _row ) );
|
||
|
format()->setFloatColor( cell->format()->floatColor( _column, _row ) );
|
||
|
format()->setMultiRow( cell->format()->multiRow( _column, _row ) );
|
||
|
format()->setVerticalText( cell->format()->verticalText( _column, _row ) );
|
||
|
format()->setDontPrintText( cell->format()->getDontprintText(_column, _row ) );
|
||
|
format()->setNotProtected( cell->format()->notProtected(_column, _row ) );
|
||
|
format()->setHideAll(cell->format()->isHideAll(_column, _row ) );
|
||
|
format()->setHideFormula(cell->format()->isHideFormula(_column, _row ) );
|
||
|
format()->setIndent( cell->format()->getIndent(_column, _row ) );
|
||
|
format()->setAngle( cell->format()->getAngle(_column, _row) );
|
||
|
format()->setFormatType( cell->format()->getFormatType(_column, _row) );
|
||
|
Format::Currency c;
|
||
|
if ( cell->format()->currencyInfo( c ) )
|
||
|
format()->setCurrency( c );*/
|
||
|
|
||
|
QValueList<Conditional> conditionList = cell->conditionList();
|
||
|
if (d->hasExtra())
|
||
|
delete d->extra()->conditions;
|
||
|
if ( cell->d->hasExtra() && cell->d->extra()->conditions )
|
||
|
setConditionList( conditionList );
|
||
|
else
|
||
|
if (d->hasExtra())
|
||
|
d->extra()->conditions = 0;
|
||
|
|
||
|
/*format()->setComment( cell->format()->comment( _column, _row ) );*/
|
||
|
}
|
||
|
|
||
|
void Cell::copyAll( Cell *cell )
|
||
|
{
|
||
|
Q_ASSERT( !isDefault() ); // trouble ahead...
|
||
|
copyFormat( cell );
|
||
|
copyContent( cell );
|
||
|
}
|
||
|
|
||
|
void Cell::copyContent( const Cell* cell )
|
||
|
{
|
||
|
Q_ASSERT( !isDefault() ); // trouble ahead...
|
||
|
|
||
|
if (cell->isFormula() && cell->column() > 0 && cell->row() > 0)
|
||
|
{
|
||
|
// change all the references, e.g. from A1 to A3 if copying
|
||
|
// from e.g. B2 to B4
|
||
|
QString d = cell->encodeFormula();
|
||
|
setCellText( cell->decodeFormula( d ) );
|
||
|
}
|
||
|
else
|
||
|
setCellText( cell->text() );
|
||
|
|
||
|
}
|
||
|
|
||
|
void Cell::defaultStyle()
|
||
|
{
|
||
|
format()->defaultStyleFormat();
|
||
|
|
||
|
if (!d->hasExtra())
|
||
|
return;
|
||
|
|
||
|
if ( d->extra()->conditions )
|
||
|
{
|
||
|
delete d->extra()->conditions;
|
||
|
d->extra()->conditions = 0;
|
||
|
}
|
||
|
|
||
|
delete d->extra()->validity;
|
||
|
d->extra()->validity = 0L;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Merge a number of cells, i.e. make this cell obscure a number of
|
||
|
// other cells. If _x and _y == 0, then the merging is removed.
|
||
|
|
||
|
void Cell::mergeCells( int _col, int _row, int _x, int _y )
|
||
|
{
|
||
|
// Start by unobscuring the cells that we obscure right now
|
||
|
int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
|
||
|
for ( int x = _col; x <= _col + extraXCells; ++x ) {
|
||
|
for ( int y = _row; y <= _row + extraYCells; ++y ) {
|
||
|
if ( x != _col || y != _row )
|
||
|
format()->sheet()->nonDefaultCell( x, y )->unobscure(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If no merging, then remove all traces, and return.
|
||
|
if ( _x == 0 && _y == 0 ) {
|
||
|
clearFlag( Flag_Merged );
|
||
|
if (d->hasExtra()) {
|
||
|
d->extra()->extraXCells = 0;
|
||
|
d->extra()->extraYCells = 0;
|
||
|
d->extra()->extraWidth = 0.0;
|
||
|
d->extra()->extraHeight = 0.0;
|
||
|
d->extra()->mergedXCells = 0;
|
||
|
d->extra()->mergedYCells = 0;
|
||
|
}
|
||
|
|
||
|
// Refresh the layout
|
||
|
setFlag( Flag_LayoutDirty );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// At this point, we know that we will merge some cells.
|
||
|
setFlag(Flag_Merged);
|
||
|
d->extra()->extraXCells = _x;
|
||
|
d->extra()->extraYCells = _y;
|
||
|
d->extra()->mergedXCells = _x;
|
||
|
d->extra()->mergedYCells = _y;
|
||
|
|
||
|
// Obscure the cells
|
||
|
for ( int x = _col; x <= _col + _x; ++x ) {
|
||
|
for ( int y = _row; y <= _row + _y; ++y ) {
|
||
|
if ( x != _col || y != _row )
|
||
|
format()->sheet()->nonDefaultCell( x, y )->obscure( this, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Refresh the layout
|
||
|
setFlag( Flag_LayoutDirty );
|
||
|
}
|
||
|
|
||
|
void Cell::move( int col, int row )
|
||
|
{
|
||
|
setLayoutDirtyFlag();
|
||
|
setCalcDirtyFlag();
|
||
|
setDisplayDirtyFlag();
|
||
|
|
||
|
//int ex = extraXCells();
|
||
|
//int ey = d->extra()->extraYCells();
|
||
|
|
||
|
if (d->hasExtra())
|
||
|
d->extra()->obscuringCells.clear();
|
||
|
|
||
|
// Unobscure the objects we obscure right now
|
||
|
int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
|
||
|
for( int x = d->column; x <= d->column + extraXCells; ++x )
|
||
|
for( int y = d->row; y <= d->row + extraYCells; ++y )
|
||
|
if ( x != d->column || y != d->row )
|
||
|
{
|
||
|
Cell *cell = format()->sheet()->nonDefaultCell( x, y );
|
||
|
cell->unobscure(this);
|
||
|
}
|
||
|
|
||
|
d->column = col;
|
||
|
d->row = row;
|
||
|
|
||
|
if (d->hasExtra())
|
||
|
{
|
||
|
// d->extra()->extraXCells = 0;
|
||
|
// d->extra()->extraYCells = 0;
|
||
|
d->extra()->mergedXCells = 0;
|
||
|
d->extra()->mergedYCells = 0;
|
||
|
}
|
||
|
|
||
|
// Cell value has been changed (because we're another cell now).
|
||
|
valueChanged ();
|
||
|
}
|
||
|
|
||
|
void Cell::setLayoutDirtyFlag( bool format )
|
||
|
{
|
||
|
setFlag( Flag_LayoutDirty );
|
||
|
if ( format )
|
||
|
setFlag( Flag_TextFormatDirty );
|
||
|
|
||
|
if (!d->hasExtra())
|
||
|
return;
|
||
|
|
||
|
QValueList<Cell*>::iterator it = d->extra()->obscuringCells.begin();
|
||
|
QValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
|
||
|
for ( ; it != end; ++it ) {
|
||
|
(*it)->setLayoutDirtyFlag( format );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Cell::needsPrinting() const
|
||
|
{
|
||
|
if ( isDefault() )
|
||
|
return false;
|
||
|
|
||
|
if ( !d->strText.stripWhiteSpace().isEmpty() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Cell borders?
|
||
|
if ( format()->hasProperty( Format::PTopBorder )
|
||
|
|| format()->hasProperty( Format::PLeftBorder )
|
||
|
|| format()->hasProperty( Format::PRightBorder )
|
||
|
|| format()->hasProperty( Format::PBottomBorder )
|
||
|
|| format()->hasProperty( Format::PFallDiagonal )
|
||
|
|| format()->hasProperty( Format::PGoUpDiagonal ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Background color or brush?
|
||
|
if ( format()->hasProperty( Format::PBackgroundBrush ) ) {
|
||
|
|
||
|
const QBrush& brush=backGroundBrush(column(),row());
|
||
|
|
||
|
//Only brushes that are visible (ie. they have a brush style and are not white)
|
||
|
//need to be drawn
|
||
|
if ( (brush.style() != Qt::NoBrush) &&
|
||
|
(brush.color() != Qt::white || brush.pixmap()) )
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( format()->hasProperty( Format::PBackgroundColor ) ) {
|
||
|
kdDebug() << "needsPrinting: Has background colour" << endl;
|
||
|
QColor backgroundColor=bgColor(column(),row());
|
||
|
|
||
|
//We don't need to print anything if the background is white
|
||
|
if (backgroundColor != Qt::white)
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool Cell::isEmpty() const
|
||
|
{
|
||
|
return isDefault() || d->strText.isEmpty();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return true if this cell is obscured by some other cell.
|
||
|
|
||
|
bool Cell::isObscured() const
|
||
|
{
|
||
|
if (!d->hasExtra())
|
||
|
return false;
|
||
|
|
||
|
return !( d->extra()->obscuringCells.isEmpty() );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return true if this cell is part of a merged cell, but not the
|
||
|
// master cell.
|
||
|
|
||
|
bool Cell::isPartOfMerged() const
|
||
|
{
|
||
|
if (!d->hasExtra())
|
||
|
return false;
|
||
|
|
||
|
QValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
|
||
|
QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
|
||
|
for ( ; it != end; ++it ) {
|
||
|
Cell *cell = *it;
|
||
|
|
||
|
if (cell->doesMergeCells()) {
|
||
|
// The cell might merge extra cells, and then overlap even
|
||
|
// beyond that so just knowing that the obscuring cell merges
|
||
|
// extra isn't enough. We have to know that this cell is one of
|
||
|
// the ones it is forcing over.
|
||
|
if (column() <= cell->column() + cell->d->extra()->mergedXCells
|
||
|
&& row() <= cell->row() + cell->mergedYCells() )
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Return the cell that obscures this one. If no cell is obscuring,
|
||
|
// then return this. This method is slightly complicated because we
|
||
|
// can have several layers of obscuring.
|
||
|
//
|
||
|
// Update: it seems that if we do an actual merge, then the obscuring
|
||
|
// cell is prepended and if just expanding, then it is appended. This
|
||
|
// means that we should be able to just look at the first one.
|
||
|
|
||
|
Cell *Cell::ultimateObscuringCell() const
|
||
|
{
|
||
|
if (!d->hasExtra())
|
||
|
return (Cell *) this;
|
||
|
|
||
|
else if (d->extra()->obscuringCells.isEmpty())
|
||
|
return (Cell *) this;
|
||
|
|
||
|
else
|
||
|
return d->extra()->obscuringCells.first();
|
||
|
|
||
|
#if 0
|
||
|
QValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
|
||
|
QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
|
||
|
for ( ; it != end; ++it ) {
|
||
|
Cell *cell = *it;
|
||
|
|
||
|
if (cell->doesMergeCells()) {
|
||
|
// The cell might merge extra cells, and then overlap even
|
||
|
// beyond that so just knowing that the obscuring cell merges
|
||
|
// extra isn't enough. We have to know that this cell is one of
|
||
|
// the ones it is forcing over.
|
||
|
if (column() <= cell->column() + cell->d->extra()->mergedXCells
|
||
|
&& row() <= cell->row() + cell->mergedYCells() )
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
QValueList<Cell*> Cell::obscuringCells() const
|
||
|
{
|
||
|
if (!d->hasExtra())
|
||
|
{
|
||
|
QValueList<Cell*> empty;
|
||
|
return empty;
|
||
|
}
|
||
|
return d->extra()->obscuringCells;
|
||
|
}
|
||
|
|
||
|
void Cell::clearObscuringCells()
|
||
|
{
|
||
|
if (!d->hasExtra())
|
||
|
return;
|
||
|
d->extra()->obscuringCells.clear();
|
||
|
}
|
||
|
|
||
|
void Cell::obscure( Cell *cell, bool isForcing )
|
||
|
{
|
||
|
if (d->hasExtra())
|
||
|
{
|
||
|
d->extra()->obscuringCells.remove( cell ); // removes *all* occurrences
|
||
|
cell->clearObscuringCells();
|
||
|
}
|
||
|
if ( isForcing )
|
||
|
{
|
||
|
d->extra()->obscuringCells.prepend( cell );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->extra()->obscuringCells.append( cell );
|
||
|
}
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
format()->sheet()->setRegionPaintDirty( cellRect() );
|
||
|
}
|
||
|
|
||
|
void Cell::unobscure( Cell * cell )
|
||
|
{
|
||
|
if (d->hasExtra())
|
||
|
d->extra()->obscuringCells.remove( cell );
|
||
|
setFlag( Flag_LayoutDirty );
|
||
|
format()->sheet()->setRegionPaintDirty( cellRect() );
|
||
|
}
|
||
|
|
||
|
QString Cell::encodeFormula( bool _era, int _col, int _row ) const
|
||
|
{
|
||
|
if ( _col == -1 )
|
||
|
_col = d->column;
|
||
|
if ( _row == -1 )
|
||
|
_row = d->row;
|
||
|
|
||
|
QString erg = "";
|
||
|
|
||
|
if(d->strText.isEmpty())
|
||
|
return d->strText;
|
||
|
|
||
|
bool fix1 = false;
|
||
|
bool fix2 = false;
|
||
|
bool onNumber = false;
|
||
|
unsigned int pos = 0;
|
||
|
const unsigned int length = d->strText.length();
|
||
|
|
||
|
// All this can surely be made 10 times faster, but I just "ported" it to QString
|
||
|
// without any attempt to optimize things -- this is really brittle (Werner)
|
||
|
while ( pos < length )
|
||
|
{
|
||
|
if ( d->strText[pos] == '"' )
|
||
|
{
|
||
|
erg += d->strText[pos++];
|
||
|
while ( pos < length && d->strText[pos] != '"' ) // till the end of the world^H^H^H "string"
|
||
|
{
|
||
|
erg += d->strText[pos++];
|
||
|
// Allow escaped double quotes (\")
|
||
|
if ( pos < length && d->strText[pos] == '\\' && d->strText[pos+1] == '"' )
|
||
|
{
|
||
|
erg += d->strText[pos++];
|
||
|
erg += d->strText[pos++];
|
||
|
}
|
||
|
}
|
||
|
if ( pos < length ) // also copy the trailing double quote
|
||
|
erg += d->strText[pos++];
|
||
|
|
||
|
onNumber = false;
|
||
|
}
|
||
|
else if ( d->strText[pos].isDigit() )
|
||
|
{
|
||
|
erg += d->strText[pos++];
|
||
|
fix1 = fix2 = false;
|
||
|
onNumber = true;
|
||
|
}
|
||
|
else if ( d->strText[pos] != '$' && !d->strText[pos].isLetter() )
|
||
|
{
|
||
|
erg += d->strText[pos++];
|
||
|
fix1 = fix2 = false;
|
||
|
onNumber = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
QString tmp = "";
|
||
|
if ( d->strText[pos] == '$' )
|
||
|
{
|
||
|
tmp = "$";
|
||
|
pos++;
|
||
|
fix1 = true;
|
||
|
}
|
||
|
if ( d->strText[pos].isLetter() )
|
||
|
{
|
||
|
QString buffer;
|
||
|
unsigned int pos2 = 0;
|
||
|
while ( pos < length && d->strText[pos].isLetter() )
|
||
|
{
|
||
|
tmp += d->strText[pos];
|
||
|
buffer[pos2++] = d->strText[pos++];
|
||
|
}
|
||
|
if ( d->strText[pos] == '$' )
|
||
|
{
|
||
|
tmp += "$";
|
||
|
pos++;
|
||
|
fix2 = true;
|
||
|
}
|
||
|
if ( d->strText[pos].isDigit() )
|
||
|
{
|
||
|
const unsigned int oldPos = pos;
|
||
|
while ( pos < length && d->strText[pos].isDigit() ) ++pos;
|
||
|
int row = 0;
|
||
|
if ( pos != oldPos )
|
||
|
row = d->strText.mid(oldPos, pos-oldPos).toInt();
|
||
|
// Is it a sheet name || is it a function name like DEC2HEX
|
||
|
/* or if we're parsing a number, this could just be the
|
||
|
exponential part of it (1.23E4) */
|
||
|
if ( ( d->strText[pos] == '!' ) ||
|
||
|
d->strText[pos].isLetter() ||
|
||
|
onNumber )
|
||
|
{
|
||
|
erg += tmp;
|
||
|
fix1 = fix2 = false;
|
||
|
pos = oldPos;
|
||
|
}
|
||
|
else // It must be a cell identifier
|
||
|
{
|
||
|
//now calculate the row as integer value
|
||
|
int col = 0;
|
||
|
col = util_decodeColumnLabelText( buffer );
|
||
|
if ( fix1 )
|
||
|
erg += QString( "$%1" ).arg( col );
|
||
|
else
|
||
|
if (_era)
|
||
|
erg += QChar(0xA7) + QString( "%1" ).arg( col );
|
||
|
else
|
||
|
erg += QString( "#%1" ).arg( col - _col );
|
||
|
|
||
|
if ( fix2 )
|
||
|
erg += QString( "$%1#").arg( row );
|
||
|
else
|
||
|
if (_era)
|
||
|
erg += QChar(0xA7) + QString( "%1#" ).arg( row );
|
||
|
else
|
||
|
erg += QString( "#%1#" ).arg( row - _row );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
erg += tmp;
|
||
|
fix1 = fix2 = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
erg += tmp;
|
||
|
fix1 = false;
|
||
|
}
|
||
|
onNumber = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return erg;
|
||
|
}
|
||
|
|
||
|
QString Cell::decodeFormula( const QString &_text, int _col, int _row) const
|
||
|
{
|
||
|
if ( _col == -1 )
|
||
|
_col = d->column;
|
||
|
if ( _row == -1 )
|
||
|
_row = d->row;
|
||
|
|
||
|
QString erg = "";
|
||
|
unsigned int pos = 0;
|
||
|
const unsigned int length = _text.length();
|
||
|
|
||
|
if ( _text.isEmpty() )
|
||
|
return QString();
|
||
|
|
||
|
while ( pos < length )
|
||
|
{
|
||
|
if ( _text[pos] == '"' )
|
||
|
{
|
||
|
erg += _text[pos++];
|
||
|
while ( pos < length && _text[pos] != '"' )
|
||
|
{
|
||
|
erg += _text[pos++];
|
||
|
// Allow escaped double quotes (\")
|
||
|
if ( pos < length && _text[pos] == '\\' && _text[pos+1] == '"' )
|
||
|
{
|
||
|
erg += _text[pos++];
|
||
|
erg += _text[pos++];
|
||
|
}
|
||
|
}
|
||
|
if ( pos < length )
|
||
|
erg += _text[pos++];
|
||
|
}
|
||
|
else if ( _text[pos] == '#' || _text[pos] == '$' || _text[pos] == QChar(0xA7))
|
||
|
{
|
||
|
bool abs1 = false;
|
||
|
bool abs2 = false;
|
||
|
bool era1 = false; // if 1st is relative but encoded absolutely
|
||
|
bool era2 = false;
|
||
|
|
||
|
QChar _t = _text[pos++];
|
||
|
if ( _t == '$' )
|
||
|
abs1 = true;
|
||
|
else if ( _t == QChar(0xA7) )
|
||
|
era1 = true;
|
||
|
|
||
|
int col = 0;
|
||
|
unsigned int oldPos = pos;
|
||
|
while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
|
||
|
if ( pos != oldPos )
|
||
|
col = _text.mid(oldPos, pos-oldPos).toInt();
|
||
|
if ( !abs1 && !era1 )
|
||
|
col += _col;
|
||
|
// Skip '#' or '$'
|
||
|
|
||
|
_t = _text[pos++];
|
||
|
if ( _t == '$' )
|
||
|
abs2 = true;
|
||
|
else if ( _t == QChar(0xA7) )
|
||
|
era2 = true;
|
||
|
|
||
|
int row = 0;
|
||
|
oldPos = pos;
|
||
|
while ( pos < length && ( _text[pos].isDigit() || _text[pos] == '-' ) ) ++pos;
|
||
|
if ( pos != oldPos )
|
||
|
row = _text.mid(oldPos, pos-oldPos).toInt();
|
||
|
if ( !abs2 && !era2)
|
||
|
row += _row;
|
||
|
// Skip '#' or '$'
|
||
|
++pos;
|
||
|
if ( row < 1 || col < 1 || row > KS_rowMax || col > KS_colMax )
|
||
|
{
|
||
|
kdDebug(36001) << "Cell::decodeFormula: row or column out of range (col: " << col << " | row: " << row << ")" << endl;
|
||
|
erg = "=\"#### " + i18n("REFERENCE TO COLUMN OR ROW IS OUT OF RANGE") + "\"";
|
||
|
return erg;
|
||
|
}
|
||
|
if ( abs1 )
|
||
|
erg += "$";
|
||
|
erg += Cell::columnName(col); //Get column text
|
||
|
|
||
|
if ( abs2 )
|
||
|
erg += "$";
|
||
|
erg += QString::number( row );
|
||
|
}
|
||
|
else
|
||
|
erg += _text[pos++];
|
||
|
}
|
||
|
|
||
|
return erg;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Cell::freeAllObscuredCells()
|
||
|
{
|
||
|
//
|
||
|
// Free all obscured cells.
|
||
|
//
|
||
|
|
||
|
if (!d->hasExtra())
|
||
|
return;
|
||
|
|
||
|
for ( int x = d->column + d->extra()->mergedXCells;
|
||
|
x <= d->column + d->extra()->extraXCells; ++x ) {
|
||
|
for ( int y = d->row + d->extra()->mergedYCells;
|
||
|
y <= d->row + d->extra()->extraYCells; ++y ) {
|
||
|
if ( x != d->column || y != d->row ) {
|
||
|
Cell *cell = format()->sheet()->cellAt( x, y );
|
||
|
cell->unobscure(this);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d->extra()->extraXCells = d->extra()->mergedXCells;
|
||
|
d->extra()->extraYCells = d->extra()->mergedYCells;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// ----------------------------------------------------------------
|
||
|
// Layout
|
||
|
|
||
|
|
||
|
// Recalculate the entire layout. This includes the following members:
|
||
|
//
|
||
|
// d->textX, d->textY
|
||
|
// d->textWidth, d->textHeight
|
||
|
// d->fmAscent
|
||
|
// d->extra()->extraXCells, d->extra()->extraYCells
|
||
|
// d->extra()->extraWidth, d->extra()->extraHeight
|
||
|
// d->extra()->nbLines (if multirow)
|
||
|
//
|
||
|
// and, of course,
|
||
|
//
|
||
|
// d->strOutText
|
||
|
//
|
||
|
|
||
|
void Cell::makeLayout( QPainter &_painter, int _col, int _row )
|
||
|
{
|
||
|
// Are _col and _row really needed ?
|
||
|
//
|
||
|
// Yes they are: they are useful if this is the default layout, in
|
||
|
// which case d->row and d->column are 0 and 0, but _col and _row
|
||
|
// are the real coordinates of the cell.
|
||
|
|
||
|
// There is no need to remake the layout if it hasn't changed.
|
||
|
if ( !testFlag( Flag_LayoutDirty ) )
|
||
|
return;
|
||
|
|
||
|
// Some initializations.
|
||
|
if (d->hasExtra())
|
||
|
d->extra()->nbLines = 0;
|
||
|
clearFlag( Flag_CellTooShortX );
|
||
|
clearFlag( Flag_CellTooShortY );
|
||
|
|
||
|
// Initiate the cells that this one is obscuring to the ones that
|
||
|
// are actually merged.
|
||
|
freeAllObscuredCells();
|
||
|
if (d->hasExtra())
|
||
|
mergeCells( d->column, d->row,
|
||
|
d->extra()->mergedXCells, d->extra()->mergedYCells );
|
||
|
|
||
|
// If the column for this cell is hidden or the row is too low,
|
||
|
// there is no use in remaking the layout.
|
||
|
ColumnFormat *cl1 = format()->sheet()->columnFormat( _col );
|
||
|
RowFormat *rl1 = format()->sheet()->rowFormat( _row );
|
||
|
if ( cl1->isHide()
|
||
|
|| ( rl1->dblHeight() <= format()->sheet()->doc()->unzoomItY( 2 ) ) ) {
|
||
|
clearFlag( Flag_LayoutDirty );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Recalculate the output text, d->strOutText.
|
||
|
setOutputText();
|
||
|
|
||
|
// Empty text? Reset the outstring and, if this is the default
|
||
|
// cell, return.
|
||
|
if ( d->strOutText.isEmpty() ) {
|
||
|
d->strOutText = QString::null;
|
||
|
|
||
|
if ( isDefault() ) {
|
||
|
clearFlag( Flag_LayoutDirty );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Up to here, we have just cared about the contents, not the
|
||
|
// painting of it. Now it is time to see if the contents fits into
|
||
|
// the cell and, if not, maybe rearrange the outtext a bit.
|
||
|
//
|
||
|
// First, Determine the correct font with zoom taken into account,
|
||
|
// and apply it to _painter. Then calculate text dimensions, i.e.
|
||
|
// d->textWidth and d->textHeight.
|
||
|
applyZoomedFont( _painter, _col, _row );
|
||
|
textSize( _painter );
|
||
|
|
||
|
//
|
||
|
// Calculate the size of the cell
|
||
|
//
|
||
|
RowFormat *rl = format()->sheet()->rowFormat( d->row );
|
||
|
ColumnFormat *cl = format()->sheet()->columnFormat( d->column );
|
||
|
|
||
|
double width = cl->dblWidth();
|
||
|
double height = rl->dblHeight();
|
||
|
|
||
|
// Calculate extraWidth and extraHeight if we have a merged cell.
|
||
|
if ( testFlag( Flag_Merged ) ) {
|
||
|
int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
|
||
|
|
||
|
// FIXME: Introduce double extraWidth/Height here and use them
|
||
|
// instead (see FIXME about this in paintCell()).
|
||
|
|
||
|
for ( int x = _col + 1; x <= _col + extraXCells; x++ )
|
||
|
width += format()->sheet()->columnFormat( x )->dblWidth();
|
||
|
|
||
|
for ( int y = _row + 1; y <= _row + extraYCells; y++ )
|
||
|
height += format()->sheet()->rowFormat( y )->dblHeight();
|
||
|
}
|
||
|
|
||
|
// Cache the newly calculated extraWidth and extraHeight if we have
|
||
|
// already allocated a struct for it. Otherwise it will be zero, so
|
||
|
// don't bother.
|
||
|
if (d->hasExtra()) {
|
||
|
d->extra()->extraWidth = width;
|
||
|
d->extra()->extraHeight = height;
|
||
|
}
|
||
|
|
||
|
QFontMetrics fm = _painter.fontMetrics();
|
||
|
d->fmAscent = fm.ascent();
|
||
|
|
||
|
// Check if we need to break the line into multiple lines and are
|
||
|
// allowed to do so. If so, set `lines' to the number of lines that
|
||
|
// are needed to fit into the total width of the combined cell.
|
||
|
//
|
||
|
// Also recalculate d->textHeight, d->textWidth, d->extra->nbLines
|
||
|
// and d->strOutText.
|
||
|
//
|
||
|
int lines = 1;
|
||
|
if ( d->textWidth > (width - 2 * BORDER_SPACE
|
||
|
- format()->leftBorderWidth( _col, _row )
|
||
|
- format()->rightBorderWidth( _col, _row ) )
|
||
|
&& format()->multiRow( _col, _row ) )
|
||
|
{
|
||
|
// Copy of d->strOutText but without the newlines.
|
||
|
// QString o = d->strOutText.replace( QChar('\n'), " " );
|
||
|
|
||
|
// don't remove the existing LF, these are intended line wraps (whishlist #9881)
|
||
|
QString o = d->strOutText;
|
||
|
|
||
|
// Break the line at appropriate places, i.e. spaces, if
|
||
|
// necessary. This means to change the spaces where breaks occur
|
||
|
// into newlines.
|
||
|
if ( o.find(' ') != -1 )
|
||
|
{
|
||
|
d->strOutText = "";
|
||
|
|
||
|
// Make sure that we have a space at the end.
|
||
|
o += ' ';
|
||
|
|
||
|
int start = 0; // Start of the line we are handling now
|
||
|
int breakpos = 0; // The next candidate pos to break the string
|
||
|
int pos1 = 0;
|
||
|
int availableWidth = (int) ( width - 2 * BORDER_SPACE
|
||
|
- format()->leftBorderWidth( _col, _row )
|
||
|
- format()->rightBorderWidth( _col, _row ) );
|
||
|
|
||
|
do {
|
||
|
|
||
|
breakpos = o.find( ' ', breakpos );
|
||
|
int linefeed = o.find( '\n', pos1 );
|
||
|
|
||
|
// kdDebug() << "start: " << start << "; breakpos: " << breakpos << "; pos1: " << pos1 << "; linefeed: " << linefeed << endl;
|
||
|
|
||
|
//don't miss LF as a position to calculate current lineWidth
|
||
|
int work_breakpos = breakpos;
|
||
|
if (pos1 < linefeed && linefeed < breakpos)
|
||
|
work_breakpos = linefeed;
|
||
|
|
||
|
double lineWidth = format()->sheet()->doc()
|
||
|
->unzoomItX( fm.width( d->strOutText.mid( start, (pos1 - start) )
|
||
|
+ o.mid( pos1, work_breakpos - pos1 ) ) );
|
||
|
|
||
|
//linefeed could be -1 when no linefeed is found!
|
||
|
if (breakpos > linefeed && linefeed > 0)
|
||
|
{
|
||
|
// kdDebug() << "applying linefeed to start;" << endl;
|
||
|
start = linefeed;
|
||
|
lines++;
|
||
|
}
|
||
|
|
||
|
if ( lineWidth <= availableWidth ) {
|
||
|
// We have room for the rest of the line. End it here.
|
||
|
d->strOutText += o.mid( pos1, breakpos - pos1 );
|
||
|
pos1 = breakpos;
|
||
|
}
|
||
|
else {
|
||
|
// Still not enough room. Try to split further.
|
||
|
if ( o.at( pos1 ) == ' ' )
|
||
|
pos1++;
|
||
|
|
||
|
if ( pos1 != 0 && breakpos != -1 ) {
|
||
|
d->strOutText += "\n" + o.mid( pos1, breakpos - pos1 );
|
||
|
lines++;
|
||
|
}
|
||
|
else
|
||
|
d->strOutText += o.mid( pos1, breakpos - pos1 );
|
||
|
|
||
|
start = pos1;
|
||
|
pos1 = breakpos;
|
||
|
}
|
||
|
|
||
|
breakpos++;
|
||
|
} while( o.find( ' ', breakpos ) != -1 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lines = o.contains('\n');
|
||
|
}
|
||
|
|
||
|
d->textHeight *= lines;
|
||
|
if (lines > 1)
|
||
|
d->extra()->nbLines = lines;
|
||
|
|
||
|
d->textX = 0.0;
|
||
|
|
||
|
// Calculate the maximum width, taking into account linebreaks,
|
||
|
// and put it in d->textWidth.
|
||
|
QString t;
|
||
|
int i;
|
||
|
int pos = 0;
|
||
|
d->textWidth = 0.0;
|
||
|
do {
|
||
|
i = d->strOutText.find( "\n", pos );
|
||
|
|
||
|
if ( i == -1 )
|
||
|
t = d->strOutText.mid( pos, d->strOutText.length() - pos );
|
||
|
else {
|
||
|
t = d->strOutText.mid( pos, i - pos );
|
||
|
pos = i + 1;
|
||
|
}
|
||
|
|
||
|
double tw = format()->sheet()->doc()->unzoomItX( fm.width( t ) );
|
||
|
if ( tw > d->textWidth )
|
||
|
d->textWidth = tw;
|
||
|
} while ( i != -1 );
|
||
|
}
|
||
|
|
||
|
// Calculate d->textX and d->textY
|
||
|
offsetAlign( _col, _row );
|
||
|
|
||
|
int a = effAlignX();
|
||
|
|
||
|
// Get indentation. This is only used for left aligned text.
|
||
|
double indent = 0.0;
|
||
|
if ( a == Format::Left && !isEmpty() )
|
||
|
indent = format()->getIndent( _col, _row );
|
||
|
|
||
|
// Set Flag_CellTooShortX if the text is vertical or angled, and too
|
||
|
// high for the cell.
|
||
|
if ( format()->verticalText( _col, _row ) || format()->getAngle( _col, _row ) != 0 ) {
|
||
|
//RowFormat *rl = format()->sheet()->rowFormat( _row );
|
||
|
|
||
|
if ( d->textHeight >= height/*rl->dblHeight()*/ )
|
||
|
setFlag( Flag_CellTooShortX );
|
||
|
}
|
||
|
|
||
|
// Do we have to occupy additional cells to the right? This is only
|
||
|
// done for cells that have no merged cells in the Y direction.
|
||
|
//
|
||
|
// FIXME: Check if all cells along the merged edge to the right are
|
||
|
// empty and use the extra space? No, probably not.
|
||
|
//
|
||
|
if ( d->textWidth + indent > ( width - 2 * BORDER_SPACE
|
||
|
- format()->leftBorderWidth( _col, _row )
|
||
|
- format()->rightBorderWidth( _col, _row ) )
|
||
|
&& ( !d->hasExtra() || d->extra()->mergedYCells == 0 ) )
|
||
|
{
|
||
|
int c = d->column;
|
||
|
|
||
|
// Find free cells to the right of this one.
|
||
|
int end = 0;
|
||
|
while ( !end ) {
|
||
|
ColumnFormat *cl2 = format()->sheet()->columnFormat( c + 1 );
|
||
|
Cell *cell = format()->sheet()->visibleCellAt( c + 1, d->row );
|
||
|
|
||
|
if ( cell->isEmpty() ) {
|
||
|
width += cl2->dblWidth() - 1;
|
||
|
c++;
|
||
|
|
||
|
// Enough space?
|
||
|
if ( d->textWidth + indent <= ( width - 2 * BORDER_SPACE
|
||
|
- format()->leftBorderWidth( _col, _row )
|
||
|
- format()->rightBorderWidth( _col, _row ) ) )
|
||
|
end = 1;
|
||
|
}
|
||
|
else
|
||
|
// Not enough space, but the next cell is not empty
|
||
|
end = -1;
|
||
|
}
|
||
|
|
||
|
// Try to use additional space from the neighboring cells that
|
||
|
// were calculated in the last step. This is the place that we
|
||
|
// set d->extra()->extraXCells and d->extra()->extraWidth.
|
||
|
//
|
||
|
// Currently this is only done for left aligned cells. We have to
|
||
|
// check to make sure we haven't already force-merged enough cells
|
||
|
//
|
||
|
// FIXME: Why not right/center aligned text?
|
||
|
//
|
||
|
// FIXME: Shouldn't we check to see if end == -1 here before
|
||
|
// setting Flag_CellTooShortX?
|
||
|
//
|
||
|
if ( format()->align( _col, _row ) == Format::Left
|
||
|
|| ( format()->align( _col, _row ) == Format::Undefined
|
||
|
&& !value().isNumber() ) )
|
||
|
{
|
||
|
if ( c - d->column > d->extra()->mergedXCells ) {
|
||
|
d->extra()->extraXCells = c - d->column;
|
||
|
d->extra()->extraWidth = width;
|
||
|
for ( int i = d->column + 1; i <= c; ++i ) {
|
||
|
Cell *cell = format()->sheet()->nonDefaultCell( i, d->row );
|
||
|
cell->obscure( this );
|
||
|
}
|
||
|
|
||
|
// Not enough space
|
||
|
if ( end == -1 )
|
||
|
setFlag( Flag_CellTooShortX );
|
||
|
}
|
||
|
else
|
||
|
setFlag( Flag_CellTooShortX );
|
||
|
}
|
||
|
else
|
||
|
setFlag( Flag_CellTooShortX );
|
||
|
}
|
||
|
|
||
|
// Do we have to occupy additional cells at the bottom ?
|
||
|
//
|
||
|
// FIXME: Setting to make the current cell grow.
|
||
|
//
|
||
|
if ( format()->multiRow( _col, _row )
|
||
|
&& d->textHeight > ( height - 2 * BORDER_SPACE
|
||
|
- format()->topBorderWidth( _col, _row )
|
||
|
- format()->bottomBorderWidth( _col, _row ) ) )
|
||
|
{
|
||
|
int r = d->row;
|
||
|
int end = 0;
|
||
|
|
||
|
// Find free cells bottom to this one
|
||
|
while ( !end ) {
|
||
|
RowFormat *rl2 = format()->sheet()->rowFormat( r + 1 );
|
||
|
Cell *cell = format()->sheet()->visibleCellAt( d->column, r + 1 );
|
||
|
|
||
|
if ( cell->isEmpty() ) {
|
||
|
height += rl2->dblHeight() - 1.0;
|
||
|
r++;
|
||
|
|
||
|
// Enough space ?
|
||
|
if ( d->textHeight <= ( height - 2 * BORDER_SPACE
|
||
|
- format()->topBorderWidth( _col, _row )
|
||
|
- format()->bottomBorderWidth( _col, _row ) ) )
|
||
|
end = 1;
|
||
|
}
|
||
|
else
|
||
|
// Not enough space, but the next cell is not empty.
|
||
|
end = -1;
|
||
|
}
|
||
|
|
||
|
// Check to make sure we haven't already force-merged enough cells.
|
||
|
if ( r - d->row > d->extra()->mergedYCells )
|
||
|
{
|
||
|
d->extra()->extraYCells = r - d->row;
|
||
|
d->extra()->extraHeight = height;
|
||
|
|
||
|
for ( int i = d->row + 1; i <= r; ++i )
|
||
|
{
|
||
|
Cell *cell = format()->sheet()->nonDefaultCell( d->column, i );
|
||
|
cell->obscure( this );
|
||
|
}
|
||
|
|
||
|
// Not enough space?
|
||
|
if ( end == -1 )
|
||
|
setFlag( Flag_CellTooShortY );
|
||
|
}
|
||
|
else
|
||
|
setFlag( Flag_CellTooShortY );
|
||
|
}
|
||
|
|
||
|
clearFlag( Flag_LayoutDirty );
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Cell::valueChanged ()
|
||
|
{
|
||
|
update();
|
||
|
|
||
|
format()->sheet()->valueChanged (this);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Recalculate d->strOutText.
|
||
|
//
|
||
|
|
||
|
void Cell::setOutputText()
|
||
|
{
|
||
|
if ( isDefault() ) {
|
||
|
d->strOutText = QString::null;
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions )
|
||
|
d->extra()->conditions->checkMatches();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If nothing has changed, we don't need to remake the text layout.
|
||
|
if ( !testFlag(Flag_TextFormatDirty) )
|
||
|
return;
|
||
|
|
||
|
// We don't want to remake the layout unnecessarily.
|
||
|
clearFlag( Flag_TextFormatDirty );
|
||
|
|
||
|
// Display a formula if warranted. If not, display the value instead;
|
||
|
// this is the most common case.
|
||
|
if ( (!hasError()) && isFormula() && format()->sheet()->getShowFormula()
|
||
|
&& !( format()->sheet()->isProtected() && format()->isHideFormula( d->column, d->row ) )
|
||
|
|| isEmpty() )
|
||
|
d->strOutText = d->strText;
|
||
|
else {
|
||
|
d->strOutText = sheet()->doc()->formatter()->formatText (this,
|
||
|
formatType());
|
||
|
}
|
||
|
|
||
|
// Check conditions if needed.
|
||
|
if ( d->hasExtra() && d->extra()->conditions )
|
||
|
d->extra()->conditions->checkMatches();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Recalculate d->textX and d->textY.
|
||
|
//
|
||
|
// Used in makeLayout() and calculateTextParameters().
|
||
|
//
|
||
|
|
||
|
void Cell::offsetAlign( int _col, int _row )
|
||
|
{
|
||
|
int a;
|
||
|
Format::AlignY ay;
|
||
|
int tmpAngle;
|
||
|
bool tmpVerticalText;
|
||
|
bool tmpMultiRow;
|
||
|
|
||
|
if ( d->hasExtra()
|
||
|
&& d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
{
|
||
|
Style *style = d->extra()->conditions->matchedStyle();
|
||
|
|
||
|
if ( style->hasFeature( Style::SAlignX, true ) )
|
||
|
a = style->alignX();
|
||
|
else
|
||
|
a = format()->align( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SVerticalText, true ) )
|
||
|
tmpVerticalText = style->hasProperty( Style::PVerticalText );
|
||
|
else
|
||
|
tmpVerticalText = format()->verticalText( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SMultiRow, true ) )
|
||
|
tmpMultiRow = style->hasProperty( Style::PMultiRow );
|
||
|
else
|
||
|
tmpMultiRow = format()->multiRow( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SAlignY, true ) )
|
||
|
ay = style->alignY();
|
||
|
else
|
||
|
ay = format()->alignY( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SAngle, true ) )
|
||
|
tmpAngle = style->rotateAngle();
|
||
|
else
|
||
|
tmpAngle = format()->getAngle( _col, _row );
|
||
|
}
|
||
|
else {
|
||
|
a = format()->align( _col, _row );
|
||
|
ay = format()->alignY( _col, _row );
|
||
|
tmpAngle = format()->getAngle( _col, _row );
|
||
|
tmpVerticalText = format()->verticalText( _col, _row );
|
||
|
tmpMultiRow = format()->multiRow( _col, _row );
|
||
|
}
|
||
|
|
||
|
RowFormat *rl = format()->sheet()->rowFormat( _row );
|
||
|
ColumnFormat *cl = format()->sheet()->columnFormat( _col );
|
||
|
|
||
|
double w = cl->dblWidth();
|
||
|
double h = rl->dblHeight();
|
||
|
|
||
|
if ( d->hasExtra() ) {
|
||
|
if ( d->extra()->extraXCells ) w = d->extra()->extraWidth;
|
||
|
if ( d->extra()->extraYCells ) h = d->extra()->extraHeight;
|
||
|
}
|
||
|
|
||
|
const double effTop = BORDER_SPACE + 0.5 * effTopBorderPen( _col, _row ).width();
|
||
|
const double effBottom = h - BORDER_SPACE - 0.5 * effBottomBorderPen( _col, _row ).width();
|
||
|
|
||
|
// Calculate d->textY based on the vertical alignment and a few
|
||
|
// other inputs.
|
||
|
switch( ay )
|
||
|
{
|
||
|
case Format::Top:
|
||
|
{
|
||
|
if ( tmpAngle == 0 )
|
||
|
{
|
||
|
d->textY = effTop + (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
else if ( tmpAngle < 0 )
|
||
|
{
|
||
|
d->textY = effTop;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effTop
|
||
|
+ (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case Format::Bottom:
|
||
|
{
|
||
|
if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
|
||
|
{
|
||
|
d->textY = effBottom;
|
||
|
}
|
||
|
else if ( tmpAngle != 0 )
|
||
|
{
|
||
|
// Is enough place available?
|
||
|
if ( effBottom - effTop - d->textHeight > 0 )
|
||
|
{
|
||
|
if ( tmpAngle < 0 )
|
||
|
{
|
||
|
d->textY = effBottom - d->textHeight;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effBottom - d->textHeight
|
||
|
+ ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( tmpAngle < 0 )
|
||
|
{
|
||
|
d->textY = effTop;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effTop
|
||
|
+ ( (double) d->fmAscent * cos( tmpAngle * M_PI / 180 )
|
||
|
/ format()->sheet()->doc()->zoomedResolutionY() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if ( tmpMultiRow && !tmpVerticalText )
|
||
|
{
|
||
|
// Is enough place available?
|
||
|
if ( effBottom - effTop - d->textHeight > 0 )
|
||
|
{
|
||
|
d->textY = effBottom - d->textHeight
|
||
|
+ (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effTop
|
||
|
+ (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Is enough place available?
|
||
|
if ( effBottom - effTop - d->textHeight > 0 )
|
||
|
{
|
||
|
d->textY = effBottom - d->textHeight
|
||
|
+ (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effTop
|
||
|
+ (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case Format::Middle:
|
||
|
case Format::UndefinedY:
|
||
|
{
|
||
|
if ( !tmpVerticalText && !tmpMultiRow && !tmpAngle )
|
||
|
{
|
||
|
d->textY = ( h - d->textHeight ) / 2
|
||
|
+ (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
else if ( tmpAngle != 0 )
|
||
|
{
|
||
|
// Is enough place available?
|
||
|
if ( effBottom - effTop - d->textHeight > 0 )
|
||
|
{
|
||
|
if ( tmpAngle < 0 )
|
||
|
{
|
||
|
d->textY = ( h - d->textHeight ) / 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = ( h - d->textHeight ) / 2
|
||
|
+ (double) d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( tmpAngle < 0 )
|
||
|
{
|
||
|
d->textY = effTop;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effTop
|
||
|
+ ( (double)d->fmAscent * cos( tmpAngle * M_PI / 180 ) / format()->sheet()->doc()->zoomedResolutionY() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if ( tmpMultiRow && !tmpVerticalText )
|
||
|
{
|
||
|
// Is enough place available?
|
||
|
if ( effBottom - effTop - d->textHeight > 0 )
|
||
|
{
|
||
|
d->textY = ( h - d->textHeight ) / 2
|
||
|
+ (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->textY = effTop
|
||
|
+ (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Is enough place available?
|
||
|
if ( effBottom - effTop - d->textHeight > 0 )
|
||
|
{
|
||
|
d->textY = ( h - d->textHeight ) / 2
|
||
|
+ (double) d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
else
|
||
|
d->textY = effTop
|
||
|
+ (double)d->fmAscent / format()->sheet()->doc()->zoomedResolutionY();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
a = effAlignX();
|
||
|
if ( format()->sheet()->getShowFormula() &&
|
||
|
!( format()->sheet()->isProtected() && format()->isHideFormula( _col, _row ) ) )
|
||
|
{
|
||
|
a = Format::Left;
|
||
|
}
|
||
|
|
||
|
// Calculate d->textX based on alignment and textwidth.
|
||
|
switch ( a ) {
|
||
|
case Format::Left:
|
||
|
d->textX = 0.5 * effLeftBorderPen( _col, _row ).width() + BORDER_SPACE;
|
||
|
break;
|
||
|
case Format::Right:
|
||
|
d->textX = w - BORDER_SPACE - d->textWidth
|
||
|
- 0.5 * effRightBorderPen( _col, _row ).width();
|
||
|
break;
|
||
|
case Format::Center:
|
||
|
d->textX = 0.5 * ( w - BORDER_SPACE - d->textWidth -
|
||
|
0.5 * effRightBorderPen( _col, _row ).width() );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Recalculate the current text dimensions, i.e. d->textWidth and
|
||
|
// d->textHeight.
|
||
|
//
|
||
|
// Used in makeLayout() and calculateTextParameters().
|
||
|
//
|
||
|
void Cell::textSize( QPainter &_paint )
|
||
|
{
|
||
|
QFontMetrics fm = _paint.fontMetrics();
|
||
|
// Horizontal text ?
|
||
|
|
||
|
int tmpAngle;
|
||
|
int _row = row();
|
||
|
int _col = column();
|
||
|
bool tmpVerticalText;
|
||
|
bool fontUnderlined;
|
||
|
Format::AlignY ay;
|
||
|
|
||
|
// Set tmpAngle, tmpeVerticalText, ay and fontUnderlined according
|
||
|
// to if there is a matching condition or not.
|
||
|
if ( d->hasExtra()
|
||
|
&& d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
{
|
||
|
Style *style = d->extra()->conditions->matchedStyle();
|
||
|
|
||
|
if ( style->hasFeature( Style::SAngle, true ) )
|
||
|
tmpAngle = style->rotateAngle();
|
||
|
else
|
||
|
tmpAngle = format()->getAngle( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SVerticalText, true ) )
|
||
|
tmpVerticalText = style->hasProperty( Style::PVerticalText );
|
||
|
else
|
||
|
tmpVerticalText = format()->verticalText( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SAlignY, true ) )
|
||
|
ay = style->alignY();
|
||
|
else
|
||
|
ay = format()->alignY( _col, _row );
|
||
|
|
||
|
if ( style->hasFeature( Style::SFontFlag, true ) )
|
||
|
fontUnderlined = ( style->fontFlags() & (uint) Style::FUnderline );
|
||
|
else
|
||
|
fontUnderlined = format()->textFontUnderline( _col, _row );
|
||
|
}
|
||
|
else {
|
||
|
// The cell has no condition with a maxed style.
|
||
|
tmpAngle = format()->getAngle( _col, _row );
|
||
|
tmpVerticalText = format()->verticalText( _col, _row );
|
||
|
ay = format()->alignY( _col, _row );
|
||
|
fontUnderlined = format()->textFontUnderline( _col, _row );
|
||
|
}
|
||
|
|
||
|
// Set d->textWidth and d->textHeight to correct values according to
|
||
|
// if the text is horizontal, vertical or rotated.
|
||
|
if ( !tmpVerticalText && !tmpAngle ) {
|
||
|
// Horizontal text.
|
||
|
|
||
|
d->textWidth = format()->sheet()->doc()->unzoomItX( fm.width( d->strOutText ) );
|
||
|
int offsetFont = 0;
|
||
|
if ( ( ay == Format::Bottom ) && fontUnderlined ) {
|
||
|
offsetFont = fm.underlinePos() + 1;
|
||
|
}
|
||
|
|
||
|
d->textHeight = format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent()
|
||
|
+ offsetFont );
|
||
|
}
|
||
|
else if ( tmpAngle!= 0 ) {
|
||
|
// Rotated text.
|
||
|
|
||
|
d->textHeight = format()->sheet()->doc()
|
||
|
->unzoomItY( int( cos( tmpAngle * M_PI / 180 )
|
||
|
* ( fm.ascent() + fm.descent() )
|
||
|
+ abs( int( ( fm.width( d->strOutText )
|
||
|
* sin( tmpAngle * M_PI / 180 ) ) ) ) ) );
|
||
|
|
||
|
d->textWidth = format()->sheet()->doc()
|
||
|
->unzoomItX( int( abs( int( ( sin( tmpAngle * M_PI / 180 )
|
||
|
* ( fm.ascent() + fm.descent() ) ) ) )
|
||
|
+ fm.width( d->strOutText )
|
||
|
* cos ( tmpAngle * M_PI / 180 ) ) );
|
||
|
}
|
||
|
else {
|
||
|
// Vertical text.
|
||
|
int width = 0;
|
||
|
for ( unsigned int i = 0; i < d->strOutText.length(); i++ )
|
||
|
width = QMAX( width, fm.width( d->strOutText.at( i ) ) );
|
||
|
|
||
|
d->textWidth = format()->sheet()->doc()->unzoomItX( width );
|
||
|
d->textHeight = format()->sheet()->doc()->unzoomItY( ( fm.ascent() + fm.descent() )
|
||
|
* d->strOutText.length() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Get the effective font to use after the zooming and apply it to `painter'.
|
||
|
//
|
||
|
// Used in makeLayout() and calculateTextParameters().
|
||
|
//
|
||
|
|
||
|
void Cell::applyZoomedFont( QPainter &painter, int _col, int _row )
|
||
|
{
|
||
|
QFont tmpFont( format()->textFont( _col, _row ) );
|
||
|
|
||
|
// If there is a matching condition on this cell then set the
|
||
|
// according style parameters.
|
||
|
if ( d->hasExtra()
|
||
|
&& d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() ) {
|
||
|
|
||
|
Style * s = d->extra()->conditions->matchedStyle();
|
||
|
|
||
|
// Other size?
|
||
|
if ( s->hasFeature( Style::SFontSize, true ) )
|
||
|
tmpFont.setPointSizeFloat( s->fontSize() );
|
||
|
|
||
|
// Other attributes?
|
||
|
if ( s->hasFeature( Style::SFontFlag, true ) ) {
|
||
|
uint flags = s->fontFlags();
|
||
|
|
||
|
tmpFont.setBold( flags & (uint) Style::FBold );
|
||
|
tmpFont.setUnderline( flags & (uint) Style::FUnderline );
|
||
|
tmpFont.setItalic( flags & (uint) Style::FItalic );
|
||
|
tmpFont.setStrikeOut( flags & (uint) Style::FStrike );
|
||
|
}
|
||
|
|
||
|
// Other family?
|
||
|
if ( s->hasFeature( Style::SFontFamily, true ) )
|
||
|
tmpFont.setFamily( s->fontFamily() );
|
||
|
}
|
||
|
#if 0
|
||
|
else
|
||
|
/*
|
||
|
* could somebody please explaint why we check for isProtected or isHideFormula here
|
||
|
*/
|
||
|
if ( d->extra()->conditions
|
||
|
&& d->extra()->conditions->currentCondition( condition )
|
||
|
&& !(format()->sheet()->getShowFormula()
|
||
|
&& !( format()->sheet()->isProtected()
|
||
|
&& format()->isHideFormula( d->column, d->row ) ) ) )
|
||
|
{
|
||
|
if ( condition.fontcond )
|
||
|
tmpFont = *(condition.fontcond);
|
||
|
else
|
||
|
tmpFont = condition.style->font();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Scale the font size according to the current zoom.
|
||
|
tmpFont.setPointSizeFloat( 0.01 * format()->sheet()->doc()->zoom()
|
||
|
* tmpFont.pointSizeFloat() );
|
||
|
|
||
|
painter.setFont( tmpFont );
|
||
|
}
|
||
|
|
||
|
|
||
|
//used in Sheet::adjustColumnHelper and Sheet::adjustRow
|
||
|
void Cell::calculateTextParameters( QPainter &_painter,
|
||
|
int _col, int _row )
|
||
|
{
|
||
|
// Apply the correct font to _painter.
|
||
|
applyZoomedFont( _painter, _col, _row );
|
||
|
|
||
|
// Recalculate d->textWidth and d->textHeight
|
||
|
textSize( _painter );
|
||
|
|
||
|
// Recalculate d->textX and d->textY.
|
||
|
offsetAlign( _col, _row );
|
||
|
}
|
||
|
|
||
|
|
||
|
// ----------------------------------------------------------------
|
||
|
// Formula handling
|
||
|
|
||
|
|
||
|
bool Cell::makeFormula()
|
||
|
{
|
||
|
clearFormula ();
|
||
|
|
||
|
d->formula = new KSpread::Formula (sheet(), this);
|
||
|
d->formula->setExpression (d->strText);
|
||
|
|
||
|
if (!d->formula->isValid ()) {
|
||
|
// Did a syntax error occur ?
|
||
|
clearFormula();
|
||
|
|
||
|
if (format()->sheet()->doc()->getShowMessageError())
|
||
|
{
|
||
|
QString tmp(i18n("Error in cell %1\n\n"));
|
||
|
tmp = tmp.arg( fullName() );
|
||
|
KMessageBox::error( (QWidget*)0L, tmp);
|
||
|
}
|
||
|
setFlag(Flag_ParseError);
|
||
|
Value v;
|
||
|
v.setError ( "####" );
|
||
|
setValue (v);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// we must recalc
|
||
|
setCalcDirtyFlag ();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Cell::clearFormula()
|
||
|
{
|
||
|
delete d->formula;
|
||
|
d->formula = 0L;
|
||
|
}
|
||
|
|
||
|
bool Cell::calc(bool delay)
|
||
|
{
|
||
|
if ( !isFormula() )
|
||
|
return true;
|
||
|
|
||
|
if (d->formula == 0)
|
||
|
{
|
||
|
if ( testFlag( Flag_ParseError ) ) // there was a parse error
|
||
|
return false;
|
||
|
else
|
||
|
{
|
||
|
/* we were probably at a "isLoading() = true" state when we originally
|
||
|
* parsed
|
||
|
*/
|
||
|
makeFormula ();
|
||
|
|
||
|
if ( d->formula == 0 ) // there was a parse error
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !testFlag( Flag_CalcDirty ) )
|
||
|
return true;
|
||
|
|
||
|
if ( delay )
|
||
|
{
|
||
|
if ( format()->sheet()->doc()->delayCalculation() )
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
setFlag(Flag_TextFormatDirty);
|
||
|
clearFlag(Flag_CalcDirty);
|
||
|
|
||
|
Value result = d->formula->eval ();
|
||
|
setValue (result);
|
||
|
if (result.isNumber())
|
||
|
checkNumberFormat(); // auto-chooses number or scientific
|
||
|
|
||
|
clearFlag(Flag_CalcDirty);
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ================================================================
|
||
|
// Painting
|
||
|
|
||
|
|
||
|
// Paint the cell. This is the main function that calls a lot of
|
||
|
// helper functions.
|
||
|
//
|
||
|
// `rect' is the rectangle that we should paint on. If the cell
|
||
|
// does not overlap this rectangle, we can return immediately.
|
||
|
// `coordinate' is the origin (the upper left) of the cell in document
|
||
|
// coordinates.
|
||
|
//
|
||
|
|
||
|
void Cell::paintCell( const KoRect &rect, QPainter & painter,
|
||
|
View *view,
|
||
|
const KoPoint &coordinate,
|
||
|
const QPoint &cellRef,
|
||
|
int paintBorder,
|
||
|
QPen & rightPen, QPen & bottomPen,
|
||
|
QPen & leftPen, QPen & topPen,
|
||
|
QValueList<QPoint> &mergedCellsPainted,
|
||
|
bool drawCursor )
|
||
|
{
|
||
|
bool paintBorderRight = paintBorder & Border_Right;
|
||
|
bool paintBorderBottom = paintBorder & Border_Bottom;
|
||
|
bool paintBorderLeft = paintBorder & Border_Left;
|
||
|
bool paintBorderTop = paintBorder & Border_Top;
|
||
|
|
||
|
// If we are already painting this cell, then return immediately.
|
||
|
// This avoids infinite recursion.
|
||
|
if ( testFlag( Flag_PaintingCell ) )
|
||
|
return;
|
||
|
|
||
|
// Indicate that we are painting this cell now.
|
||
|
setFlag( Flag_PaintingCell );
|
||
|
|
||
|
// This flag indicates that we are working on drawing the cells that
|
||
|
// another cell is obscuring. The value is the number of levels down we
|
||
|
// are currently working -- i.e. a cell obscured by a cell which is
|
||
|
// obscured by a cell.
|
||
|
static int paintingObscured = 0;
|
||
|
|
||
|
#if 0
|
||
|
if (paintingObscured == 0)
|
||
|
kdDebug(36001) << "painting cell " << name() << endl;
|
||
|
else
|
||
|
kdDebug(36001) << " painting obscured cell " << name() << endl;
|
||
|
#endif
|
||
|
|
||
|
// Sanity check: If we're working on drawing an obscured cell, that
|
||
|
// means this cell should have a cell that obscures it.
|
||
|
Q_ASSERT(!(paintingObscured > 0 && d->extra()->obscuringCells.isEmpty()));
|
||
|
|
||
|
// The parameter cellref should be *this, unless this is the default cell.
|
||
|
Q_ASSERT(isDefault()
|
||
|
|| (((cellRef.x() == d->column) && (cellRef.y() == d->row))));
|
||
|
|
||
|
Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
|
||
|
|
||
|
double left = coordinate.x();
|
||
|
|
||
|
ColumnFormat * colFormat = format()->sheet()->columnFormat( cellRef.x() );
|
||
|
RowFormat * rowFormat = format()->sheet()->rowFormat( cellRef.y() );
|
||
|
|
||
|
// Set width, height to the total width and height that this cell
|
||
|
// covers, including obscured cells, and width0, height0 to the
|
||
|
// width and height of this cell, maybe merged but never implicitly
|
||
|
// extended.
|
||
|
double width0 = colFormat->dblWidth();
|
||
|
double height0 = rowFormat->dblHeight();
|
||
|
double width = width0;
|
||
|
double height = height0;
|
||
|
|
||
|
// Handle right-to-left layout.
|
||
|
// In an RTL sheet the cells have to be painted at their opposite horizontal
|
||
|
// location on the canvas, meaning that column A will be the rightmost column
|
||
|
// on screen, column B will be to the left of it and so on. Here we change
|
||
|
// the horizontal coordinate at which we start painting the cell in case the
|
||
|
// sheet's direction is RTL. We do this only if paintingObscured is 0,
|
||
|
// otherwise the cell's painting location will flip back and forth in
|
||
|
// consecutive calls to paintCell when painting obscured cells.
|
||
|
if ( sheetDir == Sheet::RightToLeft && paintingObscured == 0
|
||
|
&& view && view->canvasWidget() )
|
||
|
{
|
||
|
double dwidth = view->doc()->unzoomItX(view->canvasWidget()->width());
|
||
|
left = dwidth - coordinate.x() - width;
|
||
|
}
|
||
|
|
||
|
// See if this cell is merged or has overflown into neighbor cells.
|
||
|
// In that case, the width/height is greater than just the cell
|
||
|
// itself.
|
||
|
if (d->hasExtra()) {
|
||
|
if (d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0) {
|
||
|
// merged cell extends to the left if sheet is RTL
|
||
|
if ( sheetDir == Sheet::RightToLeft ) {
|
||
|
left -= d->extra()->extraWidth - width;
|
||
|
}
|
||
|
width0 = d->extra()->extraWidth;
|
||
|
height0 = d->extra()->extraHeight;
|
||
|
width = d->extra()->extraWidth;
|
||
|
height = d->extra()->extraHeight;
|
||
|
}
|
||
|
else {
|
||
|
#if 0
|
||
|
width += d->extra()->extraXCells ? d->extra()->extraWidth : 0;
|
||
|
height += d->extra()->extraYCells ? d->extra()->extraHeight : 0;
|
||
|
#else
|
||
|
// FIXME: Make extraWidth/Height really contain the *extra* width/height.
|
||
|
if ( d->extra()->extraXCells )
|
||
|
width = d->extra()->extraWidth;
|
||
|
if ( d->extra()->extraYCells )
|
||
|
height = d->extra()->extraHeight;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if the cell is "selected", i.e. it should be drawn with the
|
||
|
// color that indicates selection (dark blue). If more than one
|
||
|
// square is selected, the last one uses the ordinary colors. In
|
||
|
// that case, "selected" will be set to false even though the cell
|
||
|
// itself really is selected.
|
||
|
bool selected = false;
|
||
|
if ( view != NULL ) {
|
||
|
selected = view->selectionInfo()->contains( cellRef );
|
||
|
|
||
|
// But the cell doesn't look selected if this is the marker cell.
|
||
|
Cell *cell = format()->sheet()->cellAt( view->selectionInfo()->marker() );
|
||
|
QPoint bottomRight( view->selectionInfo()->marker().x() + cell->extraXCells(),
|
||
|
view->selectionInfo()->marker().y() + cell->extraYCells() );
|
||
|
QRect markerArea( view->selectionInfo()->marker(), bottomRight );
|
||
|
selected = selected && !( markerArea.contains( cellRef ) );
|
||
|
|
||
|
// Don't draw any selection at all when printing.
|
||
|
if ( painter.device()->isExtDev() || !drawCursor )
|
||
|
selected = false;
|
||
|
}
|
||
|
|
||
|
// Need to make a new layout ?
|
||
|
//
|
||
|
// FIXME: We have already used (at least) extraWidth/Height above,
|
||
|
// and now we are recalculating the layout. This has to be
|
||
|
// moved up above all uses.
|
||
|
//
|
||
|
// FIXME: This needs to be taken out eventually - it is done in
|
||
|
// canvas::paintUpdates().
|
||
|
if ( testFlag( Flag_LayoutDirty ) )
|
||
|
makeLayout( painter, cellRef.x(), cellRef.y() );
|
||
|
|
||
|
// ---------------- Start the actual painting. ----------------
|
||
|
|
||
|
// If the rect of this cell doesn't intersect the rect that should
|
||
|
// be painted, we can skip the rest and return. (Note that we need
|
||
|
// to calculate `left' first before we can do this.)
|
||
|
const KoRect cellRect( left, coordinate.y(), width, height );
|
||
|
const KoRect cellRect0( left, coordinate.y(), width0, height0 );
|
||
|
if ( !cellRect.intersects( rect ) ) {
|
||
|
clearFlag( Flag_PaintingCell );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Get the background color.
|
||
|
//
|
||
|
// If there is a condition giving the background color for this cell
|
||
|
// (and it matches), use that one, otherwise get the standard
|
||
|
// background.
|
||
|
QColor backgroundColor;
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundColor, true ) )
|
||
|
backgroundColor = d->extra()->conditions->matchedStyle()->bgColor();
|
||
|
else
|
||
|
backgroundColor = bgColor( cellRef.x(), cellRef.y() );
|
||
|
|
||
|
// 1. Paint the background.
|
||
|
if ( !isPartOfMerged() )
|
||
|
paintBackground( painter, cellRect0, cellRef, selected, backgroundColor );
|
||
|
|
||
|
// 2. Paint the default borders if we are on screen or if we are printing
|
||
|
// and the checkbox to do this is checked.
|
||
|
if ( painter.device()->devType() != QInternal::Printer
|
||
|
|| format()->sheet()->print()->printGrid())
|
||
|
paintDefaultBorders( painter, rect, cellRect, cellRef,
|
||
|
paintBorderRight, paintBorderBottom,
|
||
|
paintBorderLeft, paintBorderTop,
|
||
|
rightPen, bottomPen, leftPen, topPen );
|
||
|
|
||
|
// 3. Paint all the cells that this one obscures. They may only be
|
||
|
// partially obscured.
|
||
|
//
|
||
|
// The `paintingObscured' variable is used to avoid infinite
|
||
|
// recursion since cells sometimes paint their obscuring cell as
|
||
|
// well.
|
||
|
paintingObscured++;
|
||
|
|
||
|
if (d->hasExtra() && (d->extra()->extraXCells > 0
|
||
|
|| d->extra()->extraYCells > 0)) {
|
||
|
//kdDebug(36001) << "painting obscured cells for " << name() << endl;
|
||
|
|
||
|
paintObscuredCells( rect, painter, view, cellRect, cellRef,
|
||
|
paintBorderRight, paintBorderBottom,
|
||
|
paintBorderLeft, paintBorderTop,
|
||
|
rightPen, bottomPen, leftPen, topPen,
|
||
|
mergedCellsPainted);
|
||
|
|
||
|
// FIXME: Is this the right place for this?
|
||
|
if ( d->extra()->mergedXCells > 0 || d->extra()->mergedYCells > 0 )
|
||
|
mergedCellsPainted.prepend( cellRef );
|
||
|
}
|
||
|
paintingObscured--;
|
||
|
|
||
|
// 4. Paint the borders of the cell if no other cell is forcing this
|
||
|
// one, i.e. this cell is not part of a merged cell.
|
||
|
//
|
||
|
|
||
|
// If we print pages, then we disable clipping, otherwise borders are
|
||
|
// cut in the middle at the page borders.
|
||
|
if ( painter.device()->isExtDev() )
|
||
|
painter.setClipping( false );
|
||
|
|
||
|
// Paint the borders if this cell is not part of another merged cell.
|
||
|
if ( !isPartOfMerged() ) {
|
||
|
// if (!testFlag(Flag_Highlight))
|
||
|
paintCellBorders( painter, rect, cellRect0,
|
||
|
cellRef,
|
||
|
paintBorderRight, paintBorderBottom,
|
||
|
paintBorderLeft, paintBorderTop,
|
||
|
rightPen, bottomPen, leftPen, topPen );
|
||
|
}
|
||
|
|
||
|
// Turn clipping back on.
|
||
|
if ( painter.device()->isExtDev() )
|
||
|
painter.setClipping( true );
|
||
|
|
||
|
// 5. Paint diagonal lines and page borders.
|
||
|
paintCellDiagonalLines( painter, cellRect0, cellRef );
|
||
|
|
||
|
paintPageBorders( painter, cellRect0, cellRef,
|
||
|
paintBorderRight, paintBorderBottom );
|
||
|
|
||
|
|
||
|
// 6. Now paint the content, if this cell isn't obscured.
|
||
|
if ( !isObscured() ) {
|
||
|
|
||
|
// 6a. Paint possible comment indicator.
|
||
|
if ( !painter.device()->isExtDev()
|
||
|
|| format()->sheet()->print()->printCommentIndicator() )
|
||
|
paintCommentIndicator( painter, cellRect, cellRef, backgroundColor );
|
||
|
|
||
|
// 6b. Paint possible formula indicator.
|
||
|
if ( !painter.device()->isExtDev()
|
||
|
|| format()->sheet()->print()->printFormulaIndicator() )
|
||
|
paintFormulaIndicator( painter, cellRect, backgroundColor );
|
||
|
|
||
|
// 6c. Paint possible indicator for clipped text.
|
||
|
paintMoreTextIndicator( painter, cellRect, backgroundColor );
|
||
|
|
||
|
//6c. Paint cell highlight
|
||
|
#if 0
|
||
|
if (highlightBorder != Border_None)
|
||
|
paintCellHighlight ( painter, cellRect, cellRef, highlightBorder,
|
||
|
rightHighlightPen, bottomHighlightPen,
|
||
|
leftHighlightPen, topHighlightPen );
|
||
|
#endif
|
||
|
|
||
|
// 6d. Paint the text in the cell unless:
|
||
|
// a) it is empty
|
||
|
// b) something indicates that the text should not be painted
|
||
|
// c) the sheet is protected and the cell is hidden.
|
||
|
if ( !d->strOutText.isEmpty()
|
||
|
&& ( !painter.device()->isExtDev()
|
||
|
|| !format()->getDontprintText( cellRef.x(), cellRef.y() ) )
|
||
|
&& !( format()->sheet()->isProtected()
|
||
|
&& format()->isHideAll( cellRef.x(), cellRef.y() ) ) )
|
||
|
{
|
||
|
paintText( painter, cellRect, cellRef );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 7. If this cell is obscured and we are not already painting obscured
|
||
|
// cells, then paint the obscuring cell(s). Otherwise don't do
|
||
|
// anything so that we don't cause an infinite loop.
|
||
|
if ( isObscured() && paintingObscured == 0 &&
|
||
|
!( sheetDir == Sheet::RightToLeft && painter.device()->isExtDev() ) )
|
||
|
{
|
||
|
|
||
|
//kdDebug(36001) << "painting cells that obscure " << name() << endl;
|
||
|
|
||
|
// Store the obscuringCells list in a list of QPoint(column, row)
|
||
|
// This avoids crashes during the iteration through
|
||
|
// obscuringCells, when the cells may get non valid or the list
|
||
|
// itself gets changed during a call of obscuringCell->paintCell
|
||
|
// (this happens e.g. when there is an updateDepend)
|
||
|
if (d->hasExtra()) {
|
||
|
QValueList<QPoint> listPoints;
|
||
|
QValueList<Cell*>::iterator it = d->extra()->obscuringCells.begin();
|
||
|
QValueList<Cell*>::iterator end = d->extra()->obscuringCells.end();
|
||
|
for ( ; it != end; ++it ) {
|
||
|
Cell *obscuringCell = *it;
|
||
|
|
||
|
listPoints.append( QPoint( obscuringCell->column(), obscuringCell->row() ) );
|
||
|
}
|
||
|
|
||
|
QValueList<QPoint>::iterator it1 = listPoints.begin();
|
||
|
QValueList<QPoint>::iterator end1 = listPoints.end();
|
||
|
for ( ; it1 != end1; ++it1 ) {
|
||
|
QPoint obscuringCellRef = *it1;
|
||
|
|
||
|
// Only paint those obscuring cells that haven't been already
|
||
|
// painted yet.
|
||
|
//
|
||
|
// This optimization removes an O(n^4) behaviour where n is
|
||
|
// the number of cells on one edge in a merged cell.
|
||
|
if ( mergedCellsPainted.contains( obscuringCellRef ) )
|
||
|
continue;
|
||
|
|
||
|
Cell *obscuringCell = format()->sheet()->cellAt( obscuringCellRef.x(),
|
||
|
obscuringCellRef.y() );
|
||
|
|
||
|
if ( obscuringCell != 0 ) {
|
||
|
double x = format()->sheet()->dblColumnPos( obscuringCellRef.x() );
|
||
|
double y = format()->sheet()->dblRowPos( obscuringCellRef.y() );
|
||
|
if ( view != 0 ) {
|
||
|
x -= view->canvasWidget()->xOffset();
|
||
|
y -= view->canvasWidget()->yOffset();
|
||
|
}
|
||
|
|
||
|
KoPoint corner( x, y );
|
||
|
painter.save();
|
||
|
|
||
|
// Get the effective pens for the borders. These are
|
||
|
// determined by possible conditions on the cell with
|
||
|
// associated styles.
|
||
|
QPen rp( obscuringCell->effRightBorderPen( obscuringCellRef.x(),
|
||
|
obscuringCellRef.y() ) );
|
||
|
QPen bp( obscuringCell->effBottomBorderPen( obscuringCellRef.x(),
|
||
|
obscuringCellRef.y() ) );
|
||
|
QPen lp( obscuringCell->effLeftBorderPen( obscuringCellRef.x(),
|
||
|
obscuringCellRef.y() ) );
|
||
|
QPen tp( obscuringCell->effTopBorderPen( obscuringCellRef.x(),
|
||
|
obscuringCellRef.y() ) );
|
||
|
|
||
|
|
||
|
//kdDebug(36001) << " painting obscuring cell "
|
||
|
// << obscuringCell->name() << endl;
|
||
|
// QPen highlightPen;
|
||
|
|
||
|
//Note: Painting of highlight isn't quite right. If several
|
||
|
// cells are merged, then the whole merged cell will be
|
||
|
// painted with the colour of the last cell referenced
|
||
|
// which is inside the merged range.
|
||
|
obscuringCell->paintCell( rect, painter, view,
|
||
|
corner, obscuringCellRef,
|
||
|
Border_Left|Border_Top|Border_Right|Border_Bottom,
|
||
|
rp, bp, lp, tp,
|
||
|
mergedCellsPainted); // new pens
|
||
|
painter.restore();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We are done with the painting, so remove the flag on the cell.
|
||
|
clearFlag( Flag_PaintingCell );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// The following code was commented out in the above function. I'll
|
||
|
// leave it here in case this functionality is ever re-implemented and
|
||
|
// someone wants some code to start from.
|
||
|
//
|
||
|
#if 0
|
||
|
|
||
|
/**
|
||
|
* Modification for drawing the button
|
||
|
*/
|
||
|
if ( d->style == Cell::ST_Button ) {
|
||
|
QBrush fill( Qt::lightGray );
|
||
|
QApplication::style().drawControl( QStyle::CE_PushButton, &_painter, this,
|
||
|
QRect( _tx + 1, _ty + 1, w2 - 1, h2 - 1 ),
|
||
|
defaultColorGroup ); //, selected, &fill );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Modification for drawing the combo box
|
||
|
*/
|
||
|
else if ( d->style == Cell::ST_Select ) {
|
||
|
QApplication::style().drawComboButton( &_painter, _tx + 1, _ty + 1,
|
||
|
w2 - 1, h2 - 1,
|
||
|
defaultColorGroup, selected );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
void Cell::paintCellHighlight(QPainter& painter,
|
||
|
const KoRect& cellRect,
|
||
|
const QPoint& cellRef,
|
||
|
const int highlightBorder,
|
||
|
const QPen& rightPen,
|
||
|
const QPen& bottomPen,
|
||
|
const QPen& leftPen,
|
||
|
const QPen& topPen
|
||
|
)
|
||
|
{
|
||
|
//painter.drawLine(cellRect.left(),cellRect.top(),cellRect.right(),cellRect.bottom());
|
||
|
//QPen pen(d->extra()->highlight);
|
||
|
//painter.setPen(highlightPen);
|
||
|
|
||
|
QBrush nullBrush;
|
||
|
painter.setBrush(nullBrush);
|
||
|
|
||
|
QRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
|
||
|
|
||
|
//The highlight rect is just inside the main cell rect
|
||
|
//This saves the hassle of repainting nearby cells when the highlight is changed as the highlight areas
|
||
|
//do not overlap
|
||
|
zoomedCellRect.setLeft(zoomedCellRect.left()+1);
|
||
|
//zoomedCellRect.setRight(zoomedCellRect.right()-1);
|
||
|
zoomedCellRect.setTop(zoomedCellRect.top()+1);
|
||
|
//zoomedCellRect.setBottom(zoomedCellRect.bottom()-1);
|
||
|
|
||
|
if ( cellRef.x() != KS_colMax )
|
||
|
zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
|
||
|
if ( cellRef.y() != KS_rowMax )
|
||
|
zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
|
||
|
|
||
|
if (highlightBorder & Border_Top)
|
||
|
{
|
||
|
painter.setPen(topPen);
|
||
|
painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.top());
|
||
|
}
|
||
|
if (highlightBorder & Border_Left)
|
||
|
{
|
||
|
painter.setPen(leftPen);
|
||
|
painter.drawLine(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.left(),zoomedCellRect.bottom());
|
||
|
}
|
||
|
if (highlightBorder & Border_Right)
|
||
|
{
|
||
|
painter.setPen(rightPen);
|
||
|
painter.drawLine(zoomedCellRect.right(),zoomedCellRect.top(),zoomedCellRect.right(),zoomedCellRect.bottom());
|
||
|
}
|
||
|
if (highlightBorder & Border_Bottom)
|
||
|
{
|
||
|
painter.setPen(bottomPen);
|
||
|
painter.drawLine(zoomedCellRect.left(),zoomedCellRect.bottom(),zoomedCellRect.right(),zoomedCellRect.bottom());
|
||
|
}
|
||
|
|
||
|
if (highlightBorder & Border_SizeGrip)
|
||
|
{
|
||
|
QBrush brush(rightPen.color());
|
||
|
painter.setBrush(brush);
|
||
|
painter.setPen(rightPen);
|
||
|
painter.drawRect(zoomedCellRect.right()-3,zoomedCellRect.bottom()-3,4,4);
|
||
|
}
|
||
|
|
||
|
//painter.drawRect(zoomedCellRect.left(),zoomedCellRect.top(),zoomedCellRect.width(),zoomedCellRect.height());
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Paint all the cells that this cell obscures (helper function to paintCell).
|
||
|
//
|
||
|
void Cell::paintObscuredCells(const KoRect& rect, QPainter& painter,
|
||
|
View* view,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &cellRef,
|
||
|
bool paintBorderRight,
|
||
|
bool _paintBorderBottom,
|
||
|
bool paintBorderLeft,
|
||
|
bool _paintBorderTop,
|
||
|
QPen & rightPen, QPen & _bottomPen,
|
||
|
QPen & leftPen, QPen & _topPen,
|
||
|
QValueList<QPoint> &mergedCellsPainted)
|
||
|
{
|
||
|
// If there are no obscured cells, return.
|
||
|
if ( !extraXCells() && !extraYCells() )
|
||
|
return;
|
||
|
|
||
|
double ypos = cellRect.y();
|
||
|
int maxY = extraYCells();
|
||
|
int maxX = extraXCells();
|
||
|
|
||
|
// Loop through the rectangle of squares that we obscure and paint them.
|
||
|
for ( int y = 0; y <= maxY; ++y ) {
|
||
|
double xpos = cellRect.x();
|
||
|
RowFormat* rl = format()->sheet()->rowFormat( cellRef.y() + y );
|
||
|
|
||
|
for( int x = 0; x <= maxX; ++ x ) {
|
||
|
ColumnFormat * cl = format()->sheet()->columnFormat( cellRef.x() + x );
|
||
|
if ( y != 0 || x != 0 ) {
|
||
|
uint column = cellRef.x() + x;
|
||
|
uint row = cellRef.y() + y;
|
||
|
|
||
|
QPen topPen;
|
||
|
QPen bottomPen;
|
||
|
bool paintBorderTop;
|
||
|
bool paintBorderBottom;
|
||
|
|
||
|
Cell *cell = format()->sheet()->cellAt( column, row );
|
||
|
KoPoint corner( xpos, ypos );
|
||
|
|
||
|
// Check if the upper and lower borders should be painted, and
|
||
|
// if so which pens we should use. There used to be a nasty
|
||
|
// bug here (#61452).
|
||
|
// Check top pen. Only check if this is not on the top row.
|
||
|
topPen = _topPen;
|
||
|
paintBorderTop = _paintBorderTop;
|
||
|
if ( row > 1 && !cell->isPartOfMerged() ) {
|
||
|
Cell *cellUp = format()->sheet()->cellAt( column, row - 1 );
|
||
|
|
||
|
if ( cellUp->isDefault() )
|
||
|
paintBorderTop = false;
|
||
|
else {
|
||
|
// If the cell towards the top is part of a merged cell, get
|
||
|
// the pointer to the master cell.
|
||
|
cellUp = cellUp->ultimateObscuringCell();
|
||
|
|
||
|
topPen = cellUp->effBottomBorderPen( cellUp->column(),
|
||
|
cellUp->row() );
|
||
|
|
||
|
#if 0
|
||
|
int penWidth = QMAX(1, sheet()->doc()->zoomItY( topPen.width() ));
|
||
|
topPen.setWidth( penWidth );
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// FIXME: I thought we had to check bottom pen as well.
|
||
|
// However, it looks as if we don't need to. It works anyway.
|
||
|
bottomPen = _bottomPen;
|
||
|
paintBorderBottom = _paintBorderBottom;
|
||
|
|
||
|
int paintBorder = Border_None;
|
||
|
if (paintBorderLeft) paintBorder |= Cell::Border_Left;
|
||
|
if (paintBorderRight) paintBorder |= Cell::Border_Right;
|
||
|
if (paintBorderTop) paintBorder |= Cell::Border_Top;
|
||
|
if (paintBorderBottom) paintBorder |= Cell::Border_Bottom;
|
||
|
|
||
|
/*Cell::BorderSides highlightBorder = Border_None;
|
||
|
QPen highlightPen;*/
|
||
|
|
||
|
|
||
|
//kdDebug(36001) << "calling paintcell for obscured cell "
|
||
|
// << cell->name() << endl;
|
||
|
cell->paintCell( rect, painter, view,
|
||
|
corner,
|
||
|
QPoint( cellRef.x() + x, cellRef.y() + y ),
|
||
|
paintBorder,
|
||
|
rightPen, bottomPen, leftPen, topPen,
|
||
|
mergedCellsPainted);
|
||
|
}
|
||
|
xpos += cl->dblWidth();
|
||
|
}
|
||
|
|
||
|
ypos += rl->dblHeight();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint the background of this cell.
|
||
|
//
|
||
|
void Cell::paintBackground( QPainter& painter, const KoRect &cellRect,
|
||
|
const QPoint &cellRef, bool selected,
|
||
|
QColor &backgroundColor )
|
||
|
{
|
||
|
QColorGroup defaultColorGroup = QApplication::palette().active();
|
||
|
QRect zoomedCellRect = sheet()->doc()->zoomRect( cellRect );
|
||
|
|
||
|
// If this is not the KS_rowMax and/or KS_colMax, then we reduce
|
||
|
// width and/or height by one. This is due to the fact that the
|
||
|
// right/bottom most pixel is shared with the left/top most pixel of
|
||
|
// the following cell. Only in the case of KS_colMax/KS_rowMax we
|
||
|
// need to draw even this pixel, as there isn't a following cell to
|
||
|
// draw the background pixel.
|
||
|
if ( cellRef.x() != KS_colMax )
|
||
|
zoomedCellRect.setWidth( zoomedCellRect.width() - 1 );
|
||
|
if ( cellRef.y() != KS_rowMax )
|
||
|
zoomedCellRect.setHeight( zoomedCellRect.height() - 1 );
|
||
|
|
||
|
// Determine the correct background color
|
||
|
if ( selected )
|
||
|
{
|
||
|
//If the cell's background color is too bright, use the default highlight color
|
||
|
//Otherwise use a lighter version of the cell's background color.
|
||
|
QColor c;
|
||
|
|
||
|
int averageColor = (backgroundColor.red() + backgroundColor.green() + backgroundColor.blue()) / 3;
|
||
|
|
||
|
if (averageColor > 180)
|
||
|
if (averageColor > 225)
|
||
|
c = View::highlightColor();
|
||
|
else
|
||
|
c = backgroundColor.light( 115 ); //15% lighter
|
||
|
else
|
||
|
c = backgroundColor.light( 125 ); //25% lighter
|
||
|
|
||
|
painter.setBackgroundColor( c );
|
||
|
}
|
||
|
else {
|
||
|
QColor bg( backgroundColor );
|
||
|
|
||
|
// Handle printers separately.
|
||
|
if ( !painter.device()->isExtDev() ) {
|
||
|
if ( bg.isValid() )
|
||
|
painter.setBackgroundColor( bg );
|
||
|
else
|
||
|
painter.setBackgroundColor( defaultColorGroup.base() );
|
||
|
}
|
||
|
else {
|
||
|
//bad hack but there is a qt bug
|
||
|
//so I can print backgroundcolor
|
||
|
QBrush bb( bg );
|
||
|
if ( !bg.isValid() )
|
||
|
bb.setColor( Qt::white );
|
||
|
|
||
|
painter.fillRect( zoomedCellRect, bb );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Erase the background of the cell.
|
||
|
if ( !painter.device()->isExtDev() )
|
||
|
painter.eraseRect( zoomedCellRect );
|
||
|
|
||
|
// Get a background brush
|
||
|
QBrush bb;
|
||
|
if ( d->hasExtra()
|
||
|
&& d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SBackgroundBrush, true ) )
|
||
|
bb = d->extra()->conditions->matchedStyle()->backGroundBrush();
|
||
|
else
|
||
|
bb = backGroundBrush( cellRef.x(), cellRef.y() );
|
||
|
|
||
|
// Draw background pattern if necessary.
|
||
|
if ( bb.style() != Qt::NoBrush )
|
||
|
painter.fillRect( zoomedCellRect, bb );
|
||
|
|
||
|
backgroundColor = painter.backgroundColor();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint the standard light grey borders that are always visible.
|
||
|
//
|
||
|
void Cell::paintDefaultBorders( QPainter& painter, const KoRect &rect,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &cellRef,
|
||
|
bool paintBorderRight, bool /*paintBorderBottom*/,
|
||
|
bool paintBorderLeft, bool paintBorderTop,
|
||
|
QPen const & rightPen, QPen const & /*bottomPen*/,
|
||
|
QPen const & leftPen, QPen const & topPen )
|
||
|
{
|
||
|
/*
|
||
|
*** Notes about optimisation ***
|
||
|
|
||
|
This function was painting the top , left , right & bottom lines in almost all cells previously, contrary to what the comment
|
||
|
below says should happen. There doesn't appear to be a UI option to enable or disable showing of the grid when printing at the moment,
|
||
|
so I have disabled drawing of right and bottom borders for all cells.
|
||
|
|
||
|
I also couldn't work out under what conditions the variables dt / db would come out as anything other than 0 in the code
|
||
|
for painting the various borders. The effTopBorderPen / effBottomBorderPen calls were taking up a lot of time
|
||
|
according some profiling I did. If that code really is necessary, we need to find a more efficient way of getting the widths
|
||
|
than grabbing the whole QPen object and asking it.
|
||
|
|
||
|
|
||
|
--Robert Knight (robertknight@gmail.com)
|
||
|
*/
|
||
|
Doc* doc = sheet()->doc();
|
||
|
|
||
|
Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
|
||
|
bool paintingToExternalDevice = painter.device()->isExtDev();
|
||
|
|
||
|
// Each cell is responsible for drawing it's top and left portions
|
||
|
// of the "default" grid. --Or not drawing it if it shouldn't be
|
||
|
// there. It's also responsible to paint the right and bottom, if
|
||
|
// it is the last cell on a print out.
|
||
|
|
||
|
bool paintTop;
|
||
|
bool paintLeft;
|
||
|
bool paintBottom=false;
|
||
|
bool paintRight=false;
|
||
|
|
||
|
paintLeft = ( paintBorderLeft && leftPen.style() == Qt::NoPen
|
||
|
&& sheet()->getShowGrid() && sheetDir==Sheet::LeftToRight );
|
||
|
paintRight = ( paintBorderRight && rightPen.style() == Qt::NoPen
|
||
|
&& sheet()->getShowGrid() && sheetDir==Sheet::RightToLeft );
|
||
|
paintTop = ( paintBorderTop && topPen.style() == Qt::NoPen
|
||
|
&& sheet()->getShowGrid() );
|
||
|
// paintBottom = ( paintBorderBottom && sheet()->getShowGrid()
|
||
|
// && bottomPen.style() == Qt::NoPen );
|
||
|
|
||
|
|
||
|
//Set the single-pixel with pen for drawing the borders with.
|
||
|
painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
|
||
|
|
||
|
// If there are extra cells, there might be more conditions.
|
||
|
if (d->hasExtra()) {
|
||
|
QValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
|
||
|
QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
|
||
|
for ( ; it != end; ++it ) {
|
||
|
Cell *cell = *it;
|
||
|
|
||
|
paintTop = paintTop && ( cell->row() == cellRef.y() );
|
||
|
paintBottom = false;
|
||
|
|
||
|
if ( sheetDir == Sheet::RightToLeft ) {
|
||
|
paintRight = paintRight && ( cell->column() == cellRef.x() );
|
||
|
paintLeft = false;
|
||
|
}
|
||
|
else {
|
||
|
paintLeft = paintLeft && ( cell->column() == cellRef.x() );
|
||
|
paintRight = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The left border.
|
||
|
if ( paintLeft ) {
|
||
|
int dt = 0;
|
||
|
int db = 0;
|
||
|
|
||
|
#if 0
|
||
|
if ( cellRef.x() > 1 ) {
|
||
|
Cell *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
|
||
|
cellRef.y() );
|
||
|
QPen t = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
|
||
|
QPen b = cell_west->effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
|
||
|
|
||
|
if ( t.style() != Qt::NoPen )
|
||
|
dt = ( t.width() + 1 )/2;
|
||
|
if ( b.style() != Qt::NoPen )
|
||
|
db = ( t.width() / 2);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( paintingToExternalDevice ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.right() ) ),
|
||
|
doc->zoomItY( QMAX( rect.top(), cellRect.y() + dt ) ),
|
||
|
doc->zoomItX( QMIN( rect.right(), cellRect.right() ) ),
|
||
|
doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
|
||
|
else
|
||
|
painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() ) ),
|
||
|
doc->zoomItY( QMAX( rect.top(), cellRect.y() + dt ) ),
|
||
|
doc->zoomItX( QMIN( rect.right(), cellRect.x() ) ),
|
||
|
doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.y() + dt ),
|
||
|
doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.bottom() - db ) );
|
||
|
else
|
||
|
painter.drawLine( doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.y() + dt ),
|
||
|
doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.bottom() - db ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// The top border.
|
||
|
if ( paintTop ) {
|
||
|
int dl = 0;
|
||
|
int dr = 0;
|
||
|
|
||
|
#if 0
|
||
|
if ( cellRef.y() > 1 ) {
|
||
|
Cell *cell_north = format()->sheet()->cellAt( cellRef.x(),
|
||
|
cellRef.y() - 1 );
|
||
|
|
||
|
QPen l = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
|
||
|
QPen r = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
|
||
|
|
||
|
if ( l.style() != Qt::NoPen )
|
||
|
dl = ( l.width() - 1 ) / 2 + 1;
|
||
|
if ( r.style() != Qt::NoPen )
|
||
|
dr = r.width() / 2;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( paintingToExternalDevice ) {
|
||
|
painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() + dl ) ),
|
||
|
doc->zoomItY( QMAX( rect.top(), cellRect.y() ) ),
|
||
|
doc->zoomItX( QMIN( rect.right(), cellRect.right() - dr ) ),
|
||
|
doc->zoomItY( QMIN( rect.bottom(), cellRect.y() ) ) );
|
||
|
}
|
||
|
else {
|
||
|
painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
|
||
|
doc->zoomItY( cellRect.y() ),
|
||
|
doc->zoomItX( cellRect.right() - dr ),
|
||
|
doc->zoomItY( cellRect.y() ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// The right border.
|
||
|
if ( paintRight ) {
|
||
|
int dt = 0;
|
||
|
int db = 0;
|
||
|
|
||
|
#if 0
|
||
|
if ( cellRef.x() < KS_colMax ) {
|
||
|
Cell *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
|
||
|
cellRef.y() );
|
||
|
|
||
|
QPen t = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
|
||
|
QPen b = cell_east->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
|
||
|
|
||
|
if ( t.style() != Qt::NoPen )
|
||
|
dt = ( t.width() + 1 ) / 2;
|
||
|
if ( b.style() != Qt::NoPen )
|
||
|
db = ( t.width() / 2);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() ) ),
|
||
|
doc->zoomItY( QMAX( rect.top(), cellRect.y() + dt ) ),
|
||
|
doc->zoomItX( QMIN( rect.right(), cellRect.x() ) ),
|
||
|
doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
|
||
|
else
|
||
|
painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.right() ) ),
|
||
|
doc->zoomItY( QMAX( rect.top(), cellRect.y() + dt ) ),
|
||
|
doc->zoomItX( QMIN( rect.right(), cellRect.right() ) ),
|
||
|
doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() - db ) ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.y() + dt ),
|
||
|
doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.bottom() - db ) );
|
||
|
else
|
||
|
painter.drawLine( doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.y() + dt ),
|
||
|
doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.bottom() - db ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The bottom border.
|
||
|
/*if ( paintBottom ) {
|
||
|
int dl = 0;
|
||
|
int dr = 0;
|
||
|
if ( cellRef.y() < KS_rowMax ) {
|
||
|
Cell *cell_south = format()->sheet()->cellAt( cellRef.x(),
|
||
|
cellRef.y() + 1 );
|
||
|
|
||
|
QPen l = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
|
||
|
QPen r = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
|
||
|
|
||
|
if ( l.style() != Qt::NoPen )
|
||
|
dl = ( l.width() - 1 ) / 2 + 1;
|
||
|
if ( r.style() != Qt::NoPen )
|
||
|
dr = r.width() / 2;
|
||
|
}
|
||
|
|
||
|
painter.setPen( QPen( sheet()->doc()->gridColor(), 1, Qt::SolidLine ) );
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
painter.drawLine( doc->zoomItX( QMAX( rect.left(), cellRect.x() + dl ) ),
|
||
|
doc->zoomItY( QMAX( rect.top(), cellRect.bottom() ) ),
|
||
|
doc->zoomItX( QMIN( rect.right(), cellRect.right() - dr ) ),
|
||
|
doc->zoomItY( QMIN( rect.bottom(), cellRect.bottom() ) ) );
|
||
|
}
|
||
|
else {
|
||
|
painter.drawLine( doc->zoomItX( cellRect.x() + dl ),
|
||
|
doc->zoomItY( cellRect.bottom() ),
|
||
|
doc->zoomItX( cellRect.right() - dr ),
|
||
|
doc->zoomItY( cellRect.bottom() ) );
|
||
|
}
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint a comment indicator if the cell has a comment.
|
||
|
//
|
||
|
void Cell::paintCommentIndicator( QPainter& painter,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &/*cellRef*/,
|
||
|
QColor &backgroundColor )
|
||
|
{
|
||
|
Doc * doc = sheet()->doc();
|
||
|
|
||
|
// Point the little corner if there is a comment attached
|
||
|
// to this cell.
|
||
|
if ( ( format()->propertiesMask() & (uint) Format::PComment )
|
||
|
&& cellRect.width() > 10.0
|
||
|
&& cellRect.height() > 10.0
|
||
|
&& ( sheet()->print()->printCommentIndicator()
|
||
|
|| ( !painter.device()->isExtDev() && sheet()->getShowCommentIndicator() ) ) ) {
|
||
|
QColor penColor = Qt::red;
|
||
|
|
||
|
// If background has high red part, switch to blue.
|
||
|
if ( qRed( backgroundColor.rgb() ) > 127 &&
|
||
|
qGreen( backgroundColor.rgb() ) < 80 &&
|
||
|
qBlue( backgroundColor.rgb() ) < 80 )
|
||
|
{
|
||
|
penColor = Qt::blue;
|
||
|
}
|
||
|
|
||
|
// Get the triangle.
|
||
|
QPointArray point( 3 );
|
||
|
if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
|
||
|
point.setPoint( 0, doc->zoomItX( cellRect.x() + 6.0 ),
|
||
|
doc->zoomItY( cellRect.y() ) );
|
||
|
point.setPoint( 1, doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.y() ) );
|
||
|
point.setPoint( 2, doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.y() + 6.0 ) );
|
||
|
}
|
||
|
else {
|
||
|
point.setPoint( 0, doc->zoomItX( cellRect.right() - 5.0 ),
|
||
|
doc->zoomItY( cellRect.y() ) );
|
||
|
point.setPoint( 1, doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.y() ) );
|
||
|
point.setPoint( 2, doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.y() + 5.0 ) );
|
||
|
}
|
||
|
|
||
|
// And draw it.
|
||
|
painter.setBrush( QBrush( penColor ) );
|
||
|
painter.setPen( Qt::NoPen );
|
||
|
painter.drawPolygon( point );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Paint a small rectangle if this cell holds a formula.
|
||
|
//
|
||
|
void Cell::paintFormulaIndicator( QPainter& painter,
|
||
|
const KoRect &cellRect,
|
||
|
QColor &backgroundColor )
|
||
|
{
|
||
|
if ( isFormula() &&
|
||
|
format()->sheet()->getShowFormulaIndicator() &&
|
||
|
cellRect.width() > 10.0 &&
|
||
|
cellRect.height() > 10.0 )
|
||
|
{
|
||
|
Doc* doc = sheet()->doc();
|
||
|
|
||
|
QColor penColor = Qt::blue;
|
||
|
// If background has high blue part, switch to red.
|
||
|
if ( qRed( backgroundColor.rgb() ) < 80 &&
|
||
|
qGreen( backgroundColor.rgb() ) < 80 &&
|
||
|
qBlue( backgroundColor.rgb() ) > 127 )
|
||
|
{
|
||
|
penColor = Qt::red;
|
||
|
}
|
||
|
|
||
|
// Get the triangle...
|
||
|
QPointArray point( 3 );
|
||
|
if ( format()->sheet()->layoutDirection()==Sheet::RightToLeft ) {
|
||
|
point.setPoint( 0, doc->zoomItX( cellRect.right() - 6.0 ),
|
||
|
doc->zoomItY( cellRect.bottom() ) );
|
||
|
point.setPoint( 1, doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.bottom() ) );
|
||
|
point.setPoint( 2, doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.bottom() - 6.0 ) );
|
||
|
}
|
||
|
else {
|
||
|
point.setPoint( 0, doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.bottom() - 6.0 ) );
|
||
|
point.setPoint( 1, doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.bottom() ) );
|
||
|
point.setPoint( 2, doc->zoomItX( cellRect.x() + 6.0 ),
|
||
|
doc->zoomItY( cellRect.bottom() ) );
|
||
|
}
|
||
|
|
||
|
// ...and draw it.
|
||
|
painter.setBrush( QBrush( penColor ) );
|
||
|
painter.setPen( Qt::NoPen );
|
||
|
painter.drawPolygon( point );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint an indicator that the text in the cell is cut.
|
||
|
//
|
||
|
void Cell::paintMoreTextIndicator( QPainter& painter,
|
||
|
const KoRect &cellRect,
|
||
|
QColor &backgroundColor )
|
||
|
{
|
||
|
// Show a red triangle when it's not possible to write all text in cell.
|
||
|
// Don't print the red triangle if we're printing.
|
||
|
if( testFlag( Flag_CellTooShortX ) &&
|
||
|
!painter.device()->isExtDev() &&
|
||
|
cellRect.height() > 4.0 &&
|
||
|
cellRect.width() > 4.0 )
|
||
|
{
|
||
|
Doc* doc = sheet()->doc();
|
||
|
|
||
|
QColor penColor = Qt::red;
|
||
|
// If background has high red part, switch to blue.
|
||
|
if ( qRed( backgroundColor.rgb() ) > 127
|
||
|
&& qGreen( backgroundColor.rgb() ) < 80
|
||
|
&& qBlue( backgroundColor.rgb() ) < 80 )
|
||
|
{
|
||
|
penColor = Qt::blue;
|
||
|
}
|
||
|
|
||
|
// Get the triangle...
|
||
|
QPointArray point( 3 );
|
||
|
if ( d->strOutText.isRightToLeft() ) {
|
||
|
point.setPoint( 0, doc->zoomItX( cellRect.left() + 4.0 ),
|
||
|
doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 -4.0 ) );
|
||
|
point.setPoint( 1, doc->zoomItX( cellRect.left() ),
|
||
|
doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ));
|
||
|
point.setPoint( 2, doc->zoomItX( cellRect.left() + 4.0 ),
|
||
|
doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 +4.0 ) );
|
||
|
}
|
||
|
else {
|
||
|
point.setPoint( 0, doc->zoomItX( cellRect.right() - 4.0 ),
|
||
|
doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 - 4.0 ) );
|
||
|
point.setPoint( 1, doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 ) );
|
||
|
point.setPoint( 2, doc->zoomItX( cellRect.right() - 4.0 ),
|
||
|
doc->zoomItY( cellRect.y() + cellRect.height() / 2.0 + 4.0 ) );
|
||
|
}
|
||
|
|
||
|
// ...and paint it.
|
||
|
painter.setBrush( QBrush( penColor ) );
|
||
|
painter.setPen( Qt::NoPen );
|
||
|
painter.drawPolygon( point );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint the real contents of a cell - the text.
|
||
|
//
|
||
|
void Cell::paintText( QPainter& painter,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &cellRef )
|
||
|
{
|
||
|
Doc *doc = sheet()->doc();
|
||
|
|
||
|
ColumnFormat *colFormat = format()->sheet()->columnFormat( cellRef.x() );
|
||
|
|
||
|
QColorGroup defaultColorGroup = QApplication::palette().active();
|
||
|
QColor textColorPrint = effTextColor( cellRef.x(), cellRef.y() );
|
||
|
|
||
|
// Resolve the text color if invalid (=default).
|
||
|
if ( !textColorPrint.isValid() ) {
|
||
|
if ( painter.device()->isExtDev() )
|
||
|
textColorPrint = Qt::black;
|
||
|
else
|
||
|
textColorPrint = QApplication::palette().active().text();
|
||
|
}
|
||
|
|
||
|
QPen tmpPen( textColorPrint );
|
||
|
|
||
|
// Set the font according to the current zoom.
|
||
|
applyZoomedFont( painter, cellRef.x(), cellRef.y() );
|
||
|
|
||
|
// Check for red font color for negative values.
|
||
|
if ( !d->hasExtra()
|
||
|
|| !d->extra()->conditions
|
||
|
|| !d->extra()->conditions->matchedStyle() ) {
|
||
|
if ( value().isNumber()
|
||
|
&& !( format()->sheet()->getShowFormula()
|
||
|
&& !( format()->sheet()->isProtected()
|
||
|
&& format()->isHideFormula( d->column, d->row ) ) ) )
|
||
|
{
|
||
|
double v = value().asFloat();
|
||
|
if ( format()->floatColor( cellRef.x(), cellRef.y()) == Format::NegRed
|
||
|
&& v < 0.0 )
|
||
|
tmpPen.setColor( Qt::red );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for blue color, for hyperlink.
|
||
|
if ( !link().isEmpty() ) {
|
||
|
tmpPen.setColor( QApplication::palette().active().link() );
|
||
|
QFont f = painter.font();
|
||
|
f.setUnderline( true );
|
||
|
painter.setFont( f );
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/****
|
||
|
|
||
|
For now I am commenting this out -- with the default color display you
|
||
|
can read normal text through a highlighted background. Maybe this isn't
|
||
|
always the case, though, and we can put the highlighted text color back in.
|
||
|
In that case, we need to somewhere in here figure out if the text overlaps
|
||
|
another cell outside of the selection, otherwise that portion of the text
|
||
|
will be printed white on white. So just that portion would need to be
|
||
|
painted again in the normal color.
|
||
|
|
||
|
This should probably be done eventually, anyway, because I like using the
|
||
|
reverse text color for highlighted cells. I just don't like extending the
|
||
|
cell 'highlight' background outside of the selection rectangle because it
|
||
|
looks REALLY ugly.
|
||
|
*/
|
||
|
|
||
|
if ( selected && ( cellRef.x() != marker.x() || cellRef.y() != marker.y() ) )
|
||
|
{
|
||
|
QPen p( tmpPen );
|
||
|
p.setColor( defaultColorGroup.highlightedText() );
|
||
|
painter.setPen( p );
|
||
|
}
|
||
|
else {
|
||
|
painter.setPen(tmpPen);
|
||
|
}
|
||
|
#endif
|
||
|
painter.setPen( tmpPen );
|
||
|
|
||
|
QString tmpText = d->strOutText;
|
||
|
double tmpHeight = d->textHeight;
|
||
|
double tmpWidth = d->textWidth;
|
||
|
|
||
|
// If the cell is to narrow to paint the whole contents, then pick
|
||
|
// out a part of the content that we paint. The result of this is
|
||
|
// dependent on the data type of the content.
|
||
|
//
|
||
|
// FIXME: Make this dependent on the height as well.
|
||
|
//
|
||
|
if ( testFlag( Flag_CellTooShortX ) ) {
|
||
|
d->strOutText = textDisplaying( painter );
|
||
|
|
||
|
// Recalculate the text width and the offset.
|
||
|
textSize( painter );
|
||
|
offsetAlign( column(), row() );
|
||
|
}
|
||
|
|
||
|
// Hide zero.
|
||
|
if ( format()->sheet()->getHideZero()
|
||
|
&& value().isNumber()
|
||
|
&& value().asFloat() == 0 ) {
|
||
|
d->strOutText = QString::null;
|
||
|
}
|
||
|
|
||
|
// Clear extra cell if column or row is hidden
|
||
|
//
|
||
|
// FIXME: I think this should be done before the call to
|
||
|
// textDisplaying() above.
|
||
|
//
|
||
|
if ( colFormat->isHide() || ( cellRect.height() <= 2 ) ) {
|
||
|
freeAllObscuredCells(); /* TODO: This looks dangerous...must check when I
|
||
|
have time */
|
||
|
d->strOutText = "";
|
||
|
}
|
||
|
|
||
|
double indent = 0.0;
|
||
|
double offsetCellTooShort = 0.0;
|
||
|
int a = effAlignX();
|
||
|
|
||
|
// Apply indent if text is align to left not when text is at right or middle.
|
||
|
if ( a == Format::Left && !isEmpty() ) {
|
||
|
// FIXME: The following condition should be remade into a call to
|
||
|
// a new convenience function:
|
||
|
// if ( hasConditionStyleFeature( Style::SIndent, true )...
|
||
|
// This should be done throughout the entire file.
|
||
|
//
|
||
|
if ( d->hasExtra()
|
||
|
&& d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SIndent, true ) )
|
||
|
indent = d->extra()->conditions->matchedStyle()->indent();
|
||
|
else
|
||
|
indent = format()->getIndent( column(), row() );
|
||
|
}
|
||
|
|
||
|
// Made an offset, otherwise ### is under red triangle.
|
||
|
if ( a == Format::Right && !isEmpty() && testFlag( Flag_CellTooShortX ) )
|
||
|
offsetCellTooShort = format()->sheet()->doc()->unzoomItX( 4 );
|
||
|
|
||
|
QFontMetrics fm2 = painter.fontMetrics();
|
||
|
double offsetFont = 0.0;
|
||
|
|
||
|
if ( format()->alignY( column(), row() ) == Format::Bottom
|
||
|
&& format()->textFontUnderline( column(), row() ) )
|
||
|
offsetFont = format()->sheet()->doc()->unzoomItX( fm2.underlinePos() + 1 );
|
||
|
|
||
|
int tmpAngle;
|
||
|
bool tmpMultiRow;
|
||
|
bool tmpVerticalText;
|
||
|
|
||
|
// Check for angled or vertical text.
|
||
|
if ( d->hasExtra()
|
||
|
&& d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
{
|
||
|
Style *matchedStyle = d->extra()->conditions->matchedStyle();
|
||
|
|
||
|
if ( matchedStyle->hasFeature( Style::SAngle, true ) )
|
||
|
tmpAngle = d->extra()->conditions->matchedStyle()->rotateAngle();
|
||
|
else
|
||
|
tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
|
||
|
|
||
|
if ( matchedStyle->hasFeature( Style::SVerticalText, true ) )
|
||
|
tmpVerticalText = matchedStyle->hasProperty( Style::PVerticalText );
|
||
|
else
|
||
|
tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
|
||
|
|
||
|
if ( matchedStyle->hasFeature( Style::SMultiRow, true ) )
|
||
|
tmpMultiRow = matchedStyle->hasProperty( Style::PMultiRow );
|
||
|
else
|
||
|
tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
|
||
|
}
|
||
|
else {
|
||
|
tmpAngle = format()->getAngle( cellRef.x(), cellRef.y() );
|
||
|
tmpVerticalText = format()->verticalText( cellRef.x(), cellRef.y() );
|
||
|
tmpMultiRow = format()->multiRow( cellRef.x(), cellRef.y() );
|
||
|
}
|
||
|
|
||
|
// Actually paint the text.
|
||
|
// There are 4 possible cases:
|
||
|
// - One line of text , horizontal
|
||
|
// - Angled text
|
||
|
// - Multiple rows of text , horizontal
|
||
|
// - Vertical text
|
||
|
if ( !tmpMultiRow && !tmpVerticalText && !tmpAngle ) {
|
||
|
// Case 1: The simple case, one line, no angle.
|
||
|
|
||
|
painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX - offsetCellTooShort ),
|
||
|
doc->zoomItY( cellRect.y() + d->textY - offsetFont ), d->strOutText );
|
||
|
}
|
||
|
else if ( tmpAngle != 0 ) {
|
||
|
// Case 2: an angle.
|
||
|
|
||
|
int angle = tmpAngle;
|
||
|
QFontMetrics fm = painter.fontMetrics();
|
||
|
|
||
|
painter.rotate( angle );
|
||
|
double x;
|
||
|
if ( angle > 0 )
|
||
|
x = indent + cellRect.x() + d->textX;
|
||
|
else
|
||
|
x = indent + cellRect.x() + d->textX
|
||
|
- doc->unzoomItX((int) (( fm.descent() + fm.ascent() ) * sin( angle * M_PI / 180 )));
|
||
|
double y;
|
||
|
if ( angle > 0 )
|
||
|
y = cellRect.y() + d->textY;
|
||
|
else
|
||
|
y = cellRect.y() + d->textY + d->textHeight;
|
||
|
painter.drawText( doc->zoomItX( x * cos( angle * M_PI / 180 ) +
|
||
|
y * sin( angle * M_PI / 180 ) ),
|
||
|
doc->zoomItY( -x * sin( angle * M_PI / 180 ) +
|
||
|
y * cos( angle * M_PI / 180 ) ),
|
||
|
d->strOutText );
|
||
|
painter.rotate( -angle );
|
||
|
}
|
||
|
else if ( tmpMultiRow && !tmpVerticalText ) {
|
||
|
// Case 3: Multiple rows, but horizontal.
|
||
|
|
||
|
QString t;
|
||
|
int i;
|
||
|
int pos = 0;
|
||
|
double dy = 0.0;
|
||
|
QFontMetrics fm = painter.fontMetrics();
|
||
|
do {
|
||
|
i = d->strOutText.find( "\n", pos );
|
||
|
if ( i == -1 )
|
||
|
t = d->strOutText.mid( pos, d->strOutText.length() - pos );
|
||
|
else {
|
||
|
t = d->strOutText.mid( pos, i - pos );
|
||
|
pos = i + 1;
|
||
|
}
|
||
|
|
||
|
int align = effAlignX();
|
||
|
if ( format()->sheet()->getShowFormula()
|
||
|
&& !( format()->sheet()->isProtected()
|
||
|
&& format()->isHideFormula( d->column, d->row ) ) )
|
||
|
align = Format::Left;
|
||
|
|
||
|
// #### Torben: This looks duplicated for me
|
||
|
switch ( align ) {
|
||
|
case Format::Left:
|
||
|
d->textX = effLeftBorderPen( cellRef.x(), cellRef.y() ).width() + BORDER_SPACE;
|
||
|
break;
|
||
|
|
||
|
case Format::Right:
|
||
|
d->textX = cellRect.width() - BORDER_SPACE - doc->unzoomItX( fm.width( t ) )
|
||
|
- effRightBorderPen( cellRef.x(), cellRef.y() ).width();
|
||
|
break;
|
||
|
|
||
|
case Format::Center:
|
||
|
d->textX = ( cellRect.width() - doc->unzoomItX( fm.width( t ) ) ) / 2;
|
||
|
}
|
||
|
|
||
|
painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
|
||
|
doc->zoomItY( cellRect.y() + d->textY + dy ), t );
|
||
|
dy += doc->unzoomItY( fm.descent() + fm.ascent() );
|
||
|
} while ( i != -1 );
|
||
|
}
|
||
|
else if ( tmpVerticalText && !d->strOutText.isEmpty() ) {
|
||
|
// Case 4: Vertical text.
|
||
|
|
||
|
QString t;
|
||
|
int i = 0;
|
||
|
int len = 0;
|
||
|
double dy = 0.0;
|
||
|
QFontMetrics fm = painter.fontMetrics();
|
||
|
do {
|
||
|
len = d->strOutText.length();
|
||
|
t = d->strOutText.at( i );
|
||
|
painter.drawText( doc->zoomItX( indent + cellRect.x() + d->textX ),
|
||
|
doc->zoomItY( cellRect.y() + d->textY + dy ), t );
|
||
|
dy += doc->unzoomItY( fm.descent() + fm.ascent() );
|
||
|
i++;
|
||
|
} while ( i != len );
|
||
|
}
|
||
|
|
||
|
// Check for too short cell and set the outText for future reference.
|
||
|
if ( testFlag( Flag_CellTooShortX ) ) {
|
||
|
d->strOutText = tmpText;
|
||
|
d->textHeight = tmpHeight;
|
||
|
d->textWidth = tmpWidth;
|
||
|
}
|
||
|
|
||
|
if ( format()->sheet()->getHideZero() && value().isNumber()
|
||
|
&& value().asFloat() == 0 )
|
||
|
d->strOutText = tmpText;
|
||
|
|
||
|
if ( colFormat->isHide() || ( cellRect.height() <= 2 ) )
|
||
|
d->strOutText = tmpText;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint page borders on the page. Only do this on the screen.
|
||
|
//
|
||
|
void Cell::paintPageBorders( QPainter& painter,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &cellRef,
|
||
|
bool paintBorderRight,
|
||
|
bool paintBorderBottom )
|
||
|
{
|
||
|
// Not screen? Return immediately.
|
||
|
if ( painter.device()->isExtDev() )
|
||
|
return;
|
||
|
|
||
|
if ( ! format()->sheet()->isShowPageBorders() )
|
||
|
return;
|
||
|
|
||
|
SheetPrint* print = format()->sheet()->print();
|
||
|
|
||
|
Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
|
||
|
|
||
|
Doc* doc = sheet()->doc();
|
||
|
int zcellRect_left = doc->zoomItX (cellRect.left());
|
||
|
int zcellRect_right = doc->zoomItX (cellRect.right());
|
||
|
int zcellRect_top = doc->zoomItY (cellRect.top());
|
||
|
int zcellRect_bottom = doc->zoomItY (cellRect.bottom());
|
||
|
|
||
|
// Draw page borders
|
||
|
|
||
|
if ( cellRef.x() >= print->printRange().left()
|
||
|
&& cellRef.x() <= print->printRange().right() + 1
|
||
|
&& cellRef.y() >= print->printRange().top()
|
||
|
&& cellRef.y() <= print->printRange().bottom() + 1 )
|
||
|
{
|
||
|
if ( print->isOnNewPageX( cellRef.x() )
|
||
|
&& cellRef.y() <= print->printRange().bottom() )
|
||
|
{
|
||
|
painter.setPen( sheet()->doc()->pageBorderColor() );
|
||
|
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_right, zcellRect_top,
|
||
|
zcellRect_right, zcellRect_bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_left, zcellRect_top,
|
||
|
zcellRect_left, zcellRect_bottom );
|
||
|
}
|
||
|
|
||
|
if ( print->isOnNewPageY( cellRef.y() ) &&
|
||
|
( cellRef.x() <= print->printRange().right() ) )
|
||
|
{
|
||
|
painter.setPen( sheet()->doc()->pageBorderColor() );
|
||
|
painter.drawLine( zcellRect_left, zcellRect_top,
|
||
|
zcellRect_right, zcellRect_top );
|
||
|
}
|
||
|
|
||
|
if ( paintBorderRight ) {
|
||
|
if ( print->isOnNewPageX( cellRef.x() + 1 )
|
||
|
&& cellRef.y() <= print->printRange().bottom() ) {
|
||
|
painter.setPen( sheet()->doc()->pageBorderColor() );
|
||
|
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_left, zcellRect_top,
|
||
|
zcellRect_left, zcellRect_bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_right, zcellRect_top,
|
||
|
zcellRect_right, zcellRect_bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( paintBorderBottom ) {
|
||
|
if ( print->isOnNewPageY( cellRef.y() + 1 )
|
||
|
&& cellRef.x() <= print->printRange().right() ) {
|
||
|
painter.setPen( sheet()->doc()->pageBorderColor() );
|
||
|
painter.drawLine( zcellRect_left, zcellRect_bottom,
|
||
|
zcellRect_right, zcellRect_bottom );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint the cell borders.
|
||
|
//
|
||
|
void Cell::paintCellBorders( QPainter& painter, const KoRect& rect,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &cellRef,
|
||
|
bool paintRight, bool paintBottom,
|
||
|
bool paintLeft, bool paintTop,
|
||
|
QPen & _rightPen, QPen & _bottomPen,
|
||
|
QPen & _leftPen, QPen & _topPen )
|
||
|
{
|
||
|
|
||
|
//Sanity check: If we are not painting any of the borders then the function
|
||
|
//really shouldn't be called at all.
|
||
|
if ( (!paintLeft) && (!paintRight) && (!paintTop) && (!paintBottom) )
|
||
|
return;
|
||
|
|
||
|
Doc * doc = sheet()->doc();
|
||
|
|
||
|
Sheet::LayoutDirection sheetDir = format()->sheet()->layoutDirection();
|
||
|
|
||
|
// compute zoomed rectangles
|
||
|
// I don't use KoRect, because that ends up producing lots of warnings
|
||
|
// about double->int conversions in calls to painter.drawLine
|
||
|
int zrect_left (doc->zoomItX (rect.left()));
|
||
|
int zrect_right (doc->zoomItX (rect.right()));
|
||
|
int zrect_top (doc->zoomItY (rect.top()));
|
||
|
int zrect_bottom (doc->zoomItY (rect.bottom()));
|
||
|
int zcellRect_left (doc->zoomItX (cellRect.left()));
|
||
|
int zcellRect_right (doc->zoomItX (cellRect.right()));
|
||
|
int zcellRect_top (doc->zoomItY (cellRect.top()));
|
||
|
int zcellRect_bottom (doc->zoomItY (cellRect.bottom()));
|
||
|
|
||
|
/* we might not paint some borders if this cell is merged with another in
|
||
|
that direction
|
||
|
bool paintLeft = paintBorderLeft;
|
||
|
bool paintRight = paintBorderRight;
|
||
|
bool paintTop = paintBorderTop;
|
||
|
bool paintBottom = paintBorderBottom;
|
||
|
*/
|
||
|
|
||
|
// paintRight = paintRight && ( extraXCells() == 0 );
|
||
|
// paintBottom = paintBottom && ( d->extra()->extraYCells() == 0 );
|
||
|
|
||
|
if (d->hasExtra()) {
|
||
|
QValueList<Cell*>::const_iterator it = d->extra()->obscuringCells.begin();
|
||
|
QValueList<Cell*>::const_iterator end = d->extra()->obscuringCells.end();
|
||
|
for ( ; it != end; ++it ) {
|
||
|
Cell* cell = *it;
|
||
|
|
||
|
int xDiff = cellRef.x() - cell->column();
|
||
|
int yDiff = cellRef.y() - cell->row();
|
||
|
paintLeft = paintLeft && xDiff == 0;
|
||
|
paintTop = paintTop && yDiff == 0;
|
||
|
|
||
|
// Paint the border(s) if either this one should or if we have a
|
||
|
// merged cell with this cell as its border.
|
||
|
paintRight = paintRight && cell->mergedXCells() == xDiff;
|
||
|
paintBottom = paintBottom && cell->mergedYCells() == yDiff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Must create copies of these since otherwise the zoomIt()
|
||
|
// operation will be performed on them repeatedly.
|
||
|
QPen leftPen( _leftPen );
|
||
|
QPen rightPen( _rightPen );
|
||
|
QPen topPen( _topPen );
|
||
|
QPen bottomPen( _bottomPen );
|
||
|
|
||
|
// Determine the pens that should be used for drawing
|
||
|
// the borders.
|
||
|
//
|
||
|
int left_penWidth = QMAX( 1, doc->zoomItX( leftPen.width() ) );
|
||
|
int right_penWidth = QMAX( 1, doc->zoomItX( rightPen.width() ) );
|
||
|
int top_penWidth = QMAX( 1, doc->zoomItY( topPen.width() ) );
|
||
|
int bottom_penWidth = QMAX( 1, doc->zoomItY( bottomPen.width() ) );
|
||
|
|
||
|
leftPen.setWidth( left_penWidth );
|
||
|
rightPen.setWidth( right_penWidth );
|
||
|
topPen.setWidth( top_penWidth );
|
||
|
bottomPen.setWidth( bottom_penWidth );
|
||
|
|
||
|
if ( paintLeft && leftPen.style() != Qt::NoPen ) {
|
||
|
int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 +
|
||
|
( ( QMAX( 0, -1 + top_penWidth ) ) % 2 );
|
||
|
int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
|
||
|
|
||
|
painter.setPen( leftPen );
|
||
|
|
||
|
//kdDebug(36001) << " painting left border of cell " << name() << endl;
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
// FIXME: There is probably Cut&Paste bugs here as well as below.
|
||
|
// The QMIN/QMAX and left/right pairs don't really make sense.
|
||
|
//
|
||
|
// UPDATE: In fact, most of these QMIN/QMAX combinations
|
||
|
// are TOTALLY BOGUS. For one thing, the idea
|
||
|
// that we always have full cells on paper is wrong
|
||
|
// since we can have embedded sheets in e.g. kword,
|
||
|
// and those can be arbitrarily clipped. WE HAVE TO
|
||
|
// REVISE THIS WHOLE BORDER PAINTING SECTION!
|
||
|
//
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( QMIN( zrect_right, zcellRect_right ),
|
||
|
QMAX( zrect_top, zcellRect_top - top ),
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
|
||
|
else
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
QMAX( zrect_top, zcellRect_top - top ),
|
||
|
QMAX( zrect_left, zcellRect_left ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_right,
|
||
|
zcellRect_top - top,
|
||
|
zcellRect_right,
|
||
|
zcellRect_bottom + bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_left,
|
||
|
zcellRect_top - top,
|
||
|
zcellRect_left,
|
||
|
zcellRect_bottom + bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( paintRight && rightPen.style() != Qt::NoPen ) {
|
||
|
int top = ( QMAX( 0, -1 + top_penWidth ) ) / 2 +
|
||
|
( ( QMAX( 0, -1 + top_penWidth ) ) % 2 );
|
||
|
int bottom = ( QMAX( 0, -1 + bottom_penWidth ) ) / 2 + 1;
|
||
|
|
||
|
painter.setPen( rightPen );
|
||
|
|
||
|
//kdDebug(36001) << " painting right border of cell " << name() << endl;
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
QMAX( zrect_top, zcellRect_top - top ),
|
||
|
QMAX( zrect_left, zcellRect_left ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
|
||
|
else {
|
||
|
// FIXME: This is the way all these things should look.
|
||
|
// Make it so.
|
||
|
//
|
||
|
// Only print the right border if it is visible.
|
||
|
if ( zcellRect_right <= zrect_right + right_penWidth / 2)
|
||
|
painter.drawLine( zcellRect_right,
|
||
|
QMAX( zrect_top, zcellRect_top - top ),
|
||
|
zcellRect_right,
|
||
|
QMIN( zrect_bottom, zcellRect_bottom + bottom ) );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_left,
|
||
|
zcellRect_top - top,
|
||
|
zcellRect_left,
|
||
|
zcellRect_bottom + bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_right,
|
||
|
zcellRect_top - top,
|
||
|
zcellRect_right,
|
||
|
zcellRect_bottom + bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( paintTop && topPen.style() != Qt::NoPen ) {
|
||
|
painter.setPen( topPen );
|
||
|
|
||
|
//kdDebug(36001) << " painting top border of cell " << name()
|
||
|
// << " [" << zcellRect_left << "," << zcellRect_right
|
||
|
// << ": " << zcellRect_right - zcellRect_left << "]" << endl;
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( zcellRect_top >= zrect_top + top_penWidth / 2)
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
zcellRect_top,
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
zcellRect_top );
|
||
|
}
|
||
|
else {
|
||
|
painter.drawLine( zcellRect_left, zcellRect_top,
|
||
|
zcellRect_right, zcellRect_top );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( paintBottom && bottomPen.style() != Qt::NoPen ) {
|
||
|
painter.setPen( bottomPen );
|
||
|
|
||
|
//kdDebug(36001) << " painting bottom border of cell " << name()
|
||
|
// << " [" << zcellRect_left << "," << zcellRect_right
|
||
|
// << ": " << zcellRect_right - zcellRect_left << "]" << endl;
|
||
|
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( zcellRect_bottom <= zrect_bottom + bottom_penWidth / 2)
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
zcellRect_bottom,
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
zcellRect_bottom );
|
||
|
}
|
||
|
else {
|
||
|
painter.drawLine( zcellRect_left, zcellRect_bottom,
|
||
|
zcellRect_right, zcellRect_bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// FIXME: Look very closely at when the following code is really needed.
|
||
|
// I can't really see any case, but I might be wrong.
|
||
|
// Since the code below is buggy, and incredibly complex,
|
||
|
// I am currently disabling it. If somebody wants to enable
|
||
|
// it again, then please also solve bug 68977: "Embedded KSpread
|
||
|
// document printing problem" at the same time.
|
||
|
return;
|
||
|
|
||
|
#if 0
|
||
|
// Look at the cells on our corners. It may happen that we
|
||
|
// just erased parts of their borders corner, so we might need
|
||
|
// to repaint these corners.
|
||
|
//
|
||
|
QPen vert_pen, horz_pen;
|
||
|
int vert_penWidth, horz_penWidth;
|
||
|
|
||
|
// Some useful referenses.
|
||
|
Cell *cell_north = format()->sheet()->cellAt( cellRef.x(),
|
||
|
cellRef.y() - 1 );
|
||
|
Cell *cell_northwest = format()->sheet()->cellAt( cellRef.x() - 1,
|
||
|
cellRef.y() - 1 );
|
||
|
Cell *cell_west = format()->sheet()->cellAt( cellRef.x() - 1,
|
||
|
cellRef.y() );
|
||
|
Cell *cell_northeast = format()->sheet()->cellAt( cellRef.x() + 1,
|
||
|
cellRef.y() - 1 );
|
||
|
Cell *cell_east = format()->sheet()->cellAt( cellRef.x() + 1,
|
||
|
cellRef.y() );
|
||
|
Cell *cell_south = format()->sheet()->cellAt( cellRef.x(),
|
||
|
cellRef.y() + 1 );
|
||
|
Cell *cell_southwest = format()->sheet()->cellAt( cellRef.x() - 1,
|
||
|
cellRef.y() + 1 );
|
||
|
Cell *cell_southeast = format()->sheet()->cellAt( cellRef.x() + 1,
|
||
|
cellRef.y() + 1 );
|
||
|
|
||
|
// Fix the borders which meet at the top left corner
|
||
|
if ( cell_north->effLeftBorderValue( cellRef.x(), cellRef.y() - 1 )
|
||
|
>= cell_northwest->effRightBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
|
||
|
vert_pen = cell_north->effLeftBorderPen( cellRef.x(), cellRef.y() - 1 );
|
||
|
else
|
||
|
vert_pen = cell_northwest->effRightBorderPen( cellRef.x() - 1,
|
||
|
cellRef.y() - 1 );
|
||
|
|
||
|
vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) );
|
||
|
vert_pen.setWidth( vert_penWidth );
|
||
|
|
||
|
if ( vert_pen.style() != Qt::NoPen ) {
|
||
|
if ( cell_west->effTopBorderValue( cellRef.x() - 1, cellRef.y() )
|
||
|
>= cell_northwest->effBottomBorderValue( cellRef.x() - 1, cellRef.y() - 1 ) )
|
||
|
horz_pen = cell_west->effTopBorderPen( cellRef.x() - 1, cellRef.y() );
|
||
|
else
|
||
|
horz_pen = cell_northwest->effBottomBorderPen( cellRef.x() - 1,
|
||
|
cellRef.y() - 1 );
|
||
|
|
||
|
horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) );
|
||
|
int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
|
||
|
|
||
|
painter.setPen( vert_pen );
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_right ),
|
||
|
QMAX( zrect_top, zcellRect_top ),
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
QMIN( zrect_bottom, zcellRect_top + bottom ) );
|
||
|
else
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
QMAX( zrect_top, zcellRect_top ),
|
||
|
QMIN( zrect_right, zcellRect_left ),
|
||
|
QMIN( zrect_bottom, zcellRect_top + bottom ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_right, zcellRect_top,
|
||
|
zcellRect_right, zcellRect_top + bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_left, zcellRect_top,
|
||
|
zcellRect_left, zcellRect_top + bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fix the borders which meet at the top right corner
|
||
|
if ( cell_north->effRightBorderValue( cellRef.x(), cellRef.y() - 1 )
|
||
|
>= cell_northeast->effLeftBorderValue( cellRef.x() + 1,
|
||
|
cellRef.y() - 1 ) )
|
||
|
vert_pen = cell_north->effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
|
||
|
else
|
||
|
vert_pen = cell_northeast->effLeftBorderPen( cellRef.x() + 1,
|
||
|
cellRef.y() - 1 );
|
||
|
|
||
|
// vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() - 1 );
|
||
|
vert_penWidth = QMAX( 1, doc->zoomItX( vert_pen.width() ) );
|
||
|
vert_pen.setWidth( vert_penWidth );
|
||
|
if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
|
||
|
if ( cell_east->effTopBorderValue( cellRef.x() + 1, cellRef.y() )
|
||
|
>= cell_northeast->effBottomBorderValue( cellRef.x() + 1,
|
||
|
cellRef.y() - 1 ) )
|
||
|
horz_pen = cell_east->effTopBorderPen( cellRef.x() + 1, cellRef.y() );
|
||
|
else
|
||
|
horz_pen = cell_northeast->effBottomBorderPen( cellRef.x() + 1,
|
||
|
cellRef.y() - 1 );
|
||
|
|
||
|
// horz_pen = effTopBorderPen( cellRef.x() + 1, cellRef.y() );
|
||
|
horz_penWidth = QMAX( 1, doc->zoomItY( horz_pen.width() ) );
|
||
|
int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2 + 1;
|
||
|
|
||
|
painter.setPen( vert_pen );
|
||
|
//If we are on paper printout, we limit the length of the lines
|
||
|
//On paper, we always have full cells, on screen not
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
QMAX( zrect_top, zcellRect_top ),
|
||
|
QMIN( zrect_right, zcellRect_left ),
|
||
|
QMIN( zrect_bottom, zcellRect_top + bottom ) );
|
||
|
else
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_right ),
|
||
|
QMAX( zrect_top, zcellRect_top ),
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
QMIN( zrect_bottom, zcellRect_top + bottom ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_left, zcellRect_top,
|
||
|
zcellRect_left, zcellRect_top + bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_right, zcellRect_top,
|
||
|
zcellRect_right, zcellRect_top + bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bottom
|
||
|
if ( cellRef.y() < KS_rowMax ) {
|
||
|
// Fix the borders which meet at the bottom left corner
|
||
|
if ( cell_south->effLeftBorderValue( cellRef.x(), cellRef.y() + 1 )
|
||
|
>= cell_southwest->effRightBorderValue( cellRef.x() - 1,
|
||
|
cellRef.y() + 1 ) )
|
||
|
vert_pen = cell_south->effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
|
||
|
else
|
||
|
vert_pen = cell_southwest->effRightBorderPen( cellRef.x() - 1,
|
||
|
cellRef.y() + 1 );
|
||
|
|
||
|
// vert_pen = effLeftBorderPen( cellRef.x(), cellRef.y() + 1 );
|
||
|
vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) );
|
||
|
vert_pen.setWidth( vert_penWidth );
|
||
|
if ( vert_pen.style() != Qt::NoPen ) {
|
||
|
if ( cell_west->effBottomBorderValue( cellRef.x() - 1, cellRef.y() )
|
||
|
>= cell_southwest->effTopBorderValue( cellRef.x() - 1,
|
||
|
cellRef.y() + 1 ) )
|
||
|
horz_pen = cell_west->effBottomBorderPen( cellRef.x() - 1,
|
||
|
cellRef.y() );
|
||
|
else
|
||
|
horz_pen = cell_southwest->effTopBorderPen( cellRef.x() - 1,
|
||
|
cellRef.y() + 1 );
|
||
|
|
||
|
// horz_pen = effBottomBorderPen( cellRef.x() - 1, cellRef.y() );
|
||
|
horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) );
|
||
|
int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2;
|
||
|
|
||
|
painter.setPen( vert_pen );
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_right ),
|
||
|
QMAX( zrect_top, zcellRect_bottom - bottom ),
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom ) );
|
||
|
else
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
QMAX( zrect_top, zcellRect_bottom - bottom ),
|
||
|
QMIN( zrect_right, zcellRect_left ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
|
||
|
zcellRect_right, zcellRect_bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
|
||
|
zcellRect_left, zcellRect_bottom );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fix the borders which meet at the bottom right corner
|
||
|
if ( cell_south->effRightBorderValue( cellRef.x(), cellRef.y() + 1 )
|
||
|
>= cell_southeast->effLeftBorderValue( cellRef.x() + 1,
|
||
|
cellRef.y() + 1 ) )
|
||
|
vert_pen = cell_south->effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
|
||
|
else
|
||
|
vert_pen = cell_southeast->effLeftBorderPen( cellRef.x() + 1,
|
||
|
cellRef.y() + 1 );
|
||
|
|
||
|
// vert_pen = effRightBorderPen( cellRef.x(), cellRef.y() + 1 );
|
||
|
vert_penWidth = QMAX( 1, doc->zoomItY( vert_pen.width() ) );
|
||
|
vert_pen.setWidth( vert_penWidth );
|
||
|
if ( ( vert_pen.style() != Qt::NoPen ) && ( cellRef.x() < KS_colMax ) ) {
|
||
|
if ( cell_east ->effBottomBorderValue( cellRef.x() + 1, cellRef.y() )
|
||
|
>= cell_southeast->effTopBorderValue( cellRef.x() + 1,
|
||
|
cellRef.y() + 1 ) )
|
||
|
|
||
|
horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() )
|
||
|
->effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
|
||
|
else
|
||
|
horz_pen = format()->sheet()->cellAt( cellRef.x() + 1, cellRef.y() + 1 )
|
||
|
->effTopBorderPen( cellRef.x() + 1, cellRef.y() + 1 );
|
||
|
|
||
|
// horz_pen = effBottomBorderPen( cellRef.x() + 1, cellRef.y() );
|
||
|
horz_penWidth = QMAX( 1, doc->zoomItX( horz_pen.width() ) );
|
||
|
int bottom = ( QMAX( 0, -1 + horz_penWidth ) ) / 2;
|
||
|
|
||
|
painter.setPen( vert_pen );
|
||
|
// If we are on paper printout, we limit the length of the lines.
|
||
|
// On paper, we always have full cells, on screen not.
|
||
|
if ( painter.device()->isExtDev() ) {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_left ),
|
||
|
QMAX( zrect_top, zcellRect_bottom - bottom ),
|
||
|
QMIN( zrect_right, zcellRect_left ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom ) );
|
||
|
else
|
||
|
painter.drawLine( QMAX( zrect_left, zcellRect_right ),
|
||
|
QMAX( zrect_top, zcellRect_bottom - bottom ),
|
||
|
QMIN( zrect_right, zcellRect_right ),
|
||
|
QMIN( zrect_bottom, zcellRect_bottom ) );
|
||
|
}
|
||
|
else {
|
||
|
if ( sheetDir == Sheet::RightToLeft )
|
||
|
painter.drawLine( zcellRect_left, zcellRect_bottom - bottom,
|
||
|
zcellRect_left, zcellRect_bottom );
|
||
|
else
|
||
|
painter.drawLine( zcellRect_right, zcellRect_bottom - bottom,
|
||
|
zcellRect_right, zcellRect_bottom );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
// Paint diagonal lines through the cell.
|
||
|
//
|
||
|
void Cell::paintCellDiagonalLines( QPainter& painter,
|
||
|
const KoRect &cellRect,
|
||
|
const QPoint &cellRef )
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
return;
|
||
|
|
||
|
Doc* doc = sheet()->doc();
|
||
|
|
||
|
if ( effFallDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) {
|
||
|
painter.setPen( effFallDiagonalPen( cellRef.x(), cellRef.y() ) );
|
||
|
painter.drawLine( doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.y() ),
|
||
|
doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.bottom() ) );
|
||
|
}
|
||
|
|
||
|
if ( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ).style() != Qt::NoPen ) {
|
||
|
painter.setPen( effGoUpDiagonalPen( cellRef.x(), cellRef.y() ) );
|
||
|
painter.drawLine( doc->zoomItX( cellRect.x() ),
|
||
|
doc->zoomItY( cellRect.bottom() ),
|
||
|
doc->zoomItX( cellRect.right() ),
|
||
|
doc->zoomItY( cellRect.y() ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// End of Painting
|
||
|
// ================================================================
|
||
|
|
||
|
|
||
|
int Cell::defineAlignX()
|
||
|
{
|
||
|
int a = format()->align( column(), row() );
|
||
|
if ( a == Format::Undefined )
|
||
|
{
|
||
|
//numbers should be right-aligned by default, as well as BiDi text
|
||
|
if ((formatType() == Text_format) || value().isString())
|
||
|
a = (d->strOutText.isRightToLeft()) ?
|
||
|
Format::Right : Format::Left;
|
||
|
else {
|
||
|
Value val = value();
|
||
|
while (val.isArray()) val = val.element (0, 0);
|
||
|
if (val.isBoolean() || val.isNumber())
|
||
|
a = Format::Right;
|
||
|
else
|
||
|
a = Format::Left;
|
||
|
}
|
||
|
}
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
int Cell::effAlignX()
|
||
|
{
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SAlignX, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->alignX();
|
||
|
|
||
|
return defineAlignX();
|
||
|
}
|
||
|
|
||
|
// Cut strOutText, so that it only holds the part that can be displayed.
|
||
|
//
|
||
|
// Used in paintText().
|
||
|
//
|
||
|
|
||
|
QString Cell::textDisplaying( QPainter &_painter )
|
||
|
{
|
||
|
QFontMetrics fm = _painter.fontMetrics();
|
||
|
int a = format()->align( column(), row() );
|
||
|
|
||
|
bool isNumeric = value().isNumber();
|
||
|
|
||
|
if ( !format()->verticalText( column(),row() ) ) {
|
||
|
// Non-vertical text: the ordinary case.
|
||
|
|
||
|
// Not enough space but align to left
|
||
|
double len = 0.0;
|
||
|
int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
|
||
|
for ( int i = column(); i <= column() + extraXCells; i++ ) {
|
||
|
ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
|
||
|
len += cl2->dblWidth() - 1.0; //-1.0 because the pixel in between 2 cells is shared between both cells
|
||
|
}
|
||
|
|
||
|
QString tmp;
|
||
|
double tmpIndent = 0.0;
|
||
|
if ( !isEmpty() )
|
||
|
tmpIndent = format()->getIndent( column(), row() );
|
||
|
|
||
|
// Start out with the whole text, cut one character at a time, and
|
||
|
// when the text finally fits, return it.
|
||
|
for ( int i = d->strOutText.length(); i != 0; i-- )
|
||
|
{
|
||
|
//Note that numbers are always treated as left-aligned since if we have to cut digits off, they should
|
||
|
//always be the least significant ones at the end of the string
|
||
|
if ( a == Format::Left || a == Format::Undefined || isNumeric)
|
||
|
tmp = d->strOutText.left(i);
|
||
|
else if ( a == Format::Right)
|
||
|
tmp = d->strOutText.right(i);
|
||
|
else
|
||
|
tmp = d->strOutText.mid( ( d->strOutText.length() - i ) / 2, i);
|
||
|
|
||
|
if (isNumeric)
|
||
|
{
|
||
|
//For numeric values, we can cut off digits after the decimal point to make it fit,
|
||
|
//but not the integer part of the number.
|
||
|
//If this number still contains a fraction part then we don't need to do anything, if we have run
|
||
|
//out of space to fit even the integer part of the number then display #########
|
||
|
//TODO Perhaps try to display integer part in standard form if there is not enough room for it?
|
||
|
|
||
|
if (!tmp.contains('.'))
|
||
|
d->strOutText=QString().fill('#',20);
|
||
|
}
|
||
|
|
||
|
// 4 equal length of red triangle +1 point.
|
||
|
if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) + tmpIndent
|
||
|
< len - 4.0 - 1.0 )
|
||
|
{
|
||
|
if ( format()->getAngle( column(), row() ) != 0 )
|
||
|
{
|
||
|
QString tmp2;
|
||
|
RowFormat *rl = format()->sheet()->rowFormat( row() );
|
||
|
if ( d->textHeight > rl->dblHeight() )
|
||
|
{
|
||
|
for ( int j = d->strOutText.length(); j != 0; j-- )
|
||
|
{
|
||
|
tmp2 = d->strOutText.left( j );
|
||
|
if ( format()->sheet()->doc()->unzoomItY( fm.width( tmp2 ) ) < rl->dblHeight() - 1.0 )
|
||
|
{
|
||
|
return d->strOutText.left( QMIN( tmp.length(), tmp2.length() ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
return tmp;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
return tmp;
|
||
|
}
|
||
|
}
|
||
|
return QString( "" );
|
||
|
}
|
||
|
else if ( format()->verticalText( column(), row() ) ) {
|
||
|
// Vertical text.
|
||
|
|
||
|
RowFormat *rl = format()->sheet()->rowFormat( row() );
|
||
|
double tmpIndent = 0.0;
|
||
|
|
||
|
// Not enough space but align to left.
|
||
|
double len = 0.0;
|
||
|
int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
|
||
|
for ( int i = column(); i <= column() + extraXCells; i++ ) {
|
||
|
ColumnFormat *cl2 = format()->sheet()->columnFormat( i );
|
||
|
|
||
|
// -1.0 because the pixel in between 2 cells is shared between both cells
|
||
|
len += cl2->dblWidth() - 1.0;
|
||
|
}
|
||
|
|
||
|
if ( !isEmpty() )
|
||
|
tmpIndent = format()->getIndent( column(), row() );
|
||
|
|
||
|
if ( ( d->textWidth + tmpIndent > len ) || d->textWidth == 0.0 )
|
||
|
return QString( "" );
|
||
|
|
||
|
for ( int i = d->strOutText.length(); i != 0; i-- ) {
|
||
|
if ( format()->sheet()->doc()->unzoomItY( fm.ascent() + fm.descent() ) * i
|
||
|
< rl->dblHeight() - 1.0 )
|
||
|
return d->strOutText.left( i );
|
||
|
}
|
||
|
|
||
|
return QString( "" );
|
||
|
}
|
||
|
|
||
|
ColumnFormat *cl = format()->sheet()->columnFormat( column() );
|
||
|
double w = cl->dblWidth();
|
||
|
|
||
|
if ( d->hasExtra() && (d->extra()->extraWidth != 0.0) )
|
||
|
w = d->extra()->extraWidth;
|
||
|
|
||
|
QString tmp;
|
||
|
for ( int i = d->strOutText.length(); i != 0; i-- ) {
|
||
|
tmp = d->strOutText.left( i );
|
||
|
|
||
|
// 4 equals lenght of red triangle +1 pixel
|
||
|
if ( format()->sheet()->doc()->unzoomItX( fm.width( tmp ) ) < w - 4.0 - 1.0 )
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
return QString::null;
|
||
|
}
|
||
|
|
||
|
|
||
|
double Cell::dblWidth( int _col, const Canvas *_canvas ) const
|
||
|
{
|
||
|
if ( _col < 0 )
|
||
|
_col = d->column;
|
||
|
|
||
|
if ( _canvas )
|
||
|
{
|
||
|
if ( testFlag(Flag_Merged) )
|
||
|
return d->extra()->extraWidth;
|
||
|
|
||
|
const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
|
||
|
return cl->dblWidth( _canvas );
|
||
|
}
|
||
|
|
||
|
if ( testFlag(Flag_Merged) )
|
||
|
return d->extra()->extraWidth;
|
||
|
|
||
|
const ColumnFormat *cl = format()->sheet()->columnFormat( _col );
|
||
|
return cl->dblWidth();
|
||
|
}
|
||
|
|
||
|
int Cell::width( int _col, const Canvas *_canvas ) const
|
||
|
{
|
||
|
return int( dblWidth( _col, _canvas ) );
|
||
|
}
|
||
|
|
||
|
double Cell::dblHeight( int _row, const Canvas *_canvas ) const
|
||
|
{
|
||
|
if ( _row < 0 )
|
||
|
_row = d->row;
|
||
|
|
||
|
if ( _canvas )
|
||
|
{
|
||
|
if ( testFlag(Flag_Merged) )
|
||
|
return d->extra()->extraHeight;
|
||
|
|
||
|
const RowFormat *rl = format()->sheet()->rowFormat( _row );
|
||
|
return rl->dblHeight( _canvas );
|
||
|
}
|
||
|
|
||
|
if ( testFlag(Flag_Merged) )
|
||
|
return d->extra()->extraHeight;
|
||
|
|
||
|
const RowFormat *rl = format()->sheet()->rowFormat( _row );
|
||
|
return rl->dblHeight();
|
||
|
}
|
||
|
|
||
|
int Cell::height( int _row, const Canvas *_canvas ) const
|
||
|
{
|
||
|
return int( dblHeight( _row, _canvas ) );
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////
|
||
|
//
|
||
|
// Misc Properties.
|
||
|
// Reimplementation of Format methods.
|
||
|
//
|
||
|
///////////////////////////////////////////
|
||
|
|
||
|
const QBrush& Cell::backGroundBrush( int _col, int _row ) const
|
||
|
{
|
||
|
if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
|
||
|
{
|
||
|
const Cell* cell = d->extra()->obscuringCells.first();
|
||
|
return cell->backGroundBrush( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
return format()->backGroundBrush( _col, _row );
|
||
|
}
|
||
|
|
||
|
const QColor& Cell::bgColor( int _col, int _row ) const
|
||
|
{
|
||
|
if ( d->hasExtra() && (!d->extra()->obscuringCells.isEmpty()) )
|
||
|
{
|
||
|
const Cell* cell = d->extra()->obscuringCells.first();
|
||
|
return cell->bgColor( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
return format()->bgColor( _col, _row );
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////
|
||
|
//
|
||
|
// Borders.
|
||
|
// Reimplementation of Format methods.
|
||
|
//
|
||
|
///////////////////////////////////////////
|
||
|
|
||
|
void Cell::setLeftBorderPen( const QPen& p )
|
||
|
{
|
||
|
if ( column() == 1 )
|
||
|
{
|
||
|
Cell* cell = format()->sheet()->cellAt( column() - 1, row() );
|
||
|
if ( cell && cell->format()->hasProperty( Format::PRightBorder )
|
||
|
&& format()->sheet()->cellAt( column(), row() ) == this )
|
||
|
cell->format()->clearProperty( Format::PRightBorder );
|
||
|
}
|
||
|
|
||
|
format()->setLeftBorderPen( p );
|
||
|
}
|
||
|
|
||
|
void Cell::setTopBorderPen( const QPen& p )
|
||
|
{
|
||
|
if ( row() == 1 )
|
||
|
{
|
||
|
Cell* cell = format()->sheet()->cellAt( column(), row() - 1 );
|
||
|
if ( cell && cell->format()->hasProperty( Format::PBottomBorder )
|
||
|
&& format()->sheet()->cellAt( column(), row() ) == this )
|
||
|
cell->format()->clearProperty( Format::PBottomBorder );
|
||
|
}
|
||
|
format()->setTopBorderPen( p );
|
||
|
}
|
||
|
|
||
|
void Cell::setRightBorderPen( const QPen& p )
|
||
|
{
|
||
|
Cell* cell = 0L;
|
||
|
if ( column() < KS_colMax )
|
||
|
cell = format()->sheet()->cellAt( column() + 1, row() );
|
||
|
|
||
|
if ( cell && cell->format()->hasProperty( Format::PLeftBorder )
|
||
|
&& format()->sheet()->cellAt( column(), row() ) == this )
|
||
|
cell->format()->clearProperty( Format::PLeftBorder );
|
||
|
|
||
|
format()->setRightBorderPen( p );
|
||
|
}
|
||
|
|
||
|
void Cell::setBottomBorderPen( const QPen& p )
|
||
|
{
|
||
|
Cell* cell = 0L;
|
||
|
if ( row() < KS_rowMax )
|
||
|
cell = format()->sheet()->cellAt( column(), row() + 1 );
|
||
|
|
||
|
if ( cell && cell->format()->hasProperty( Format::PTopBorder )
|
||
|
&& format()->sheet()->cellAt( column(), row() ) == this )
|
||
|
cell->format()->clearProperty( Format::PTopBorder );
|
||
|
|
||
|
format()->setBottomBorderPen( p );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::rightBorderPen( int _col, int _row ) const
|
||
|
{
|
||
|
if ( !format()->hasProperty( Format::PRightBorder ) && ( _col < KS_colMax ) )
|
||
|
{
|
||
|
Cell * cell = format()->sheet()->cellAt( _col + 1, _row );
|
||
|
if ( cell && cell->format()->hasProperty( Format::PLeftBorder ) )
|
||
|
return cell->leftBorderPen( _col + 1, _row );
|
||
|
}
|
||
|
|
||
|
return format()->rightBorderPen( _col, _row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::leftBorderPen( int _col, int _row ) const
|
||
|
{
|
||
|
if ( !format()->hasProperty( Format::PLeftBorder ) )
|
||
|
{
|
||
|
const Cell * cell = format()->sheet()->cellAt( _col - 1, _row );
|
||
|
if ( cell && cell->format()->hasProperty( Format::PRightBorder ) )
|
||
|
return cell->rightBorderPen( _col - 1, _row );
|
||
|
}
|
||
|
|
||
|
return format()->leftBorderPen( _col, _row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::bottomBorderPen( int _col, int _row ) const
|
||
|
{
|
||
|
if ( !format()->hasProperty( Format::PBottomBorder ) && ( _row < KS_rowMax ) )
|
||
|
{
|
||
|
const Cell * cell = format()->sheet()->cellAt( _col, _row + 1 );
|
||
|
if ( cell && cell->format()->hasProperty( Format::PTopBorder ) )
|
||
|
return cell->topBorderPen( _col, _row + 1 );
|
||
|
}
|
||
|
|
||
|
return format()->bottomBorderPen( _col, _row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::topBorderPen( int _col, int _row ) const
|
||
|
{
|
||
|
if ( !format()->hasProperty( Format::PTopBorder ) )
|
||
|
{
|
||
|
const Cell * cell = format()->sheet()->cellAt( _col, _row - 1 );
|
||
|
if ( cell->format()->hasProperty( Format::PBottomBorder ) )
|
||
|
return cell->bottomBorderPen( _col, _row - 1 );
|
||
|
}
|
||
|
|
||
|
return format()->topBorderPen( _col, _row );
|
||
|
}
|
||
|
|
||
|
const QColor & Cell::effTextColor( int col, int row ) const
|
||
|
{
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::STextPen, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->pen().color();
|
||
|
|
||
|
return format()->textColor( col, row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::effLeftBorderPen( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effLeftBorderPen( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SLeftBorder, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->leftBorderPen();
|
||
|
|
||
|
return leftBorderPen( col, row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::effTopBorderPen( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effTopBorderPen( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::STopBorder, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->topBorderPen();
|
||
|
|
||
|
return topBorderPen( col, row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::effRightBorderPen( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effRightBorderPen( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SRightBorder, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->rightBorderPen();
|
||
|
|
||
|
return rightBorderPen( col, row );
|
||
|
}
|
||
|
|
||
|
const QPen& Cell::effBottomBorderPen( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effBottomBorderPen( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SBottomBorder, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->bottomBorderPen();
|
||
|
|
||
|
return bottomBorderPen( col, row );
|
||
|
}
|
||
|
|
||
|
const QPen & Cell::effGoUpDiagonalPen( int col, int row ) const
|
||
|
{
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SGoUpDiagonal, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->goUpDiagonalPen();
|
||
|
|
||
|
return format()->goUpDiagonalPen( col, row );
|
||
|
}
|
||
|
|
||
|
const QPen & Cell::effFallDiagonalPen( int col, int row ) const
|
||
|
{
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle()
|
||
|
&& d->extra()->conditions->matchedStyle()->hasFeature( Style::SFallDiagonal, true ) )
|
||
|
return d->extra()->conditions->matchedStyle()->fallDiagonalPen();
|
||
|
|
||
|
return format()->fallDiagonalPen( col, row );
|
||
|
}
|
||
|
|
||
|
uint Cell::effBottomBorderValue( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effBottomBorderValue( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
return d->extra()->conditions->matchedStyle()->bottomPenValue();
|
||
|
|
||
|
return format()->bottomBorderValue( col, row );
|
||
|
}
|
||
|
|
||
|
uint Cell::effRightBorderValue( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effRightBorderValue( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
return d->extra()->conditions->matchedStyle()->rightPenValue();
|
||
|
|
||
|
return format()->rightBorderValue( col, row );
|
||
|
}
|
||
|
|
||
|
uint Cell::effLeftBorderValue( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effLeftBorderValue( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
return d->extra()->conditions->matchedStyle()->leftPenValue();
|
||
|
|
||
|
return format()->leftBorderValue( col, row );
|
||
|
}
|
||
|
|
||
|
uint Cell::effTopBorderValue( int col, int row ) const
|
||
|
{
|
||
|
if ( isPartOfMerged() )
|
||
|
{
|
||
|
Cell * cell = d->extra()->obscuringCells.first();
|
||
|
return cell->effTopBorderValue( cell->column(), cell->row() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions
|
||
|
&& d->extra()->conditions->matchedStyle() )
|
||
|
return d->extra()->conditions->matchedStyle()->topPenValue();
|
||
|
|
||
|
return format()->topBorderValue( col, row );
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////
|
||
|
//
|
||
|
// Precision
|
||
|
//
|
||
|
///////////////////////////////////////////
|
||
|
|
||
|
void Cell::incPrecision()
|
||
|
{
|
||
|
//TODO: This is ugly. Why not simply regenerate the text to display? Tomas
|
||
|
|
||
|
if ( !value().isNumber() )
|
||
|
return;
|
||
|
int tmpPreci = format()->precision( column(), row() );
|
||
|
|
||
|
if ( tmpPreci == -1 )
|
||
|
{
|
||
|
int pos = d->strOutText.find(decimal_point);
|
||
|
if ( pos == -1 )
|
||
|
pos = d->strOutText.find('.');
|
||
|
if ( pos == -1 )
|
||
|
format()->setPrecision(1);
|
||
|
else
|
||
|
{
|
||
|
int start = 0;
|
||
|
if ( d->strOutText.find('%') != -1 )
|
||
|
start = 2;
|
||
|
else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
|
||
|
start = locale()->currencySymbol().length() + 1;
|
||
|
else if ( (start=d->strOutText.find('E')) != -1 )
|
||
|
start = d->strOutText.length() - start;
|
||
|
|
||
|
//kdDebug(36001) << "start=" << start << " pos=" << pos << " length=" << d->strOutText.length() << endl;
|
||
|
format()->setPrecision( QMAX( 0, (int)d->strOutText.length() - start - pos ) );
|
||
|
}
|
||
|
}
|
||
|
else if ( tmpPreci < 10 )
|
||
|
{
|
||
|
format()->setPrecision( ++tmpPreci );
|
||
|
}
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
}
|
||
|
|
||
|
void Cell::decPrecision()
|
||
|
{
|
||
|
//TODO: This is ugly. Why not simply regenerate the text to display? Tomas
|
||
|
|
||
|
if ( !value().isNumber() )
|
||
|
return;
|
||
|
int preciTmp = format()->precision( column(), row() );
|
||
|
// kdDebug(36001) << "decPrecision: tmpPreci = " << tmpPreci << endl;
|
||
|
if ( format()->precision(column(),row()) == -1 )
|
||
|
{
|
||
|
int pos = d->strOutText.find( decimal_point );
|
||
|
int start = 0;
|
||
|
if ( d->strOutText.find('%') != -1 )
|
||
|
start = 2;
|
||
|
else if ( d->strOutText.find(locale()->currencySymbol()) == ((int)(d->strOutText.length()-locale()->currencySymbol().length())) )
|
||
|
start = locale()->currencySymbol().length() + 1;
|
||
|
else if ( (start = d->strOutText.find('E')) != -1 )
|
||
|
start = d->strOutText.length() - start;
|
||
|
else
|
||
|
start = 0;
|
||
|
|
||
|
if ( pos == -1 )
|
||
|
return;
|
||
|
|
||
|
format()->setPrecision(d->strOutText.length() - pos - 2 - start);
|
||
|
// if ( preciTmp < 0 )
|
||
|
// format()->setPrecision( preciTmp );
|
||
|
}
|
||
|
else if ( preciTmp > 0 )
|
||
|
{
|
||
|
format()->setPrecision( --preciTmp );
|
||
|
}
|
||
|
setFlag( Flag_LayoutDirty );
|
||
|
}
|
||
|
|
||
|
//set numerical value
|
||
|
//used in Sheet::setSeries (nowhere else yet)
|
||
|
void Cell::setNumber( double number )
|
||
|
{
|
||
|
setValue( Value( number ) );
|
||
|
|
||
|
d->strText.setNum( number );
|
||
|
setDisplayText(d->strText);
|
||
|
checkNumberFormat();
|
||
|
}
|
||
|
|
||
|
void Cell::setCellText( const QString& _text, bool asText )
|
||
|
{
|
||
|
// QString ctext = _text;
|
||
|
|
||
|
// (Tomas) is this trim necessary for anything ?
|
||
|
// if( ctext.length() > 5000 )
|
||
|
// ctext = ctext.left( 5000 );
|
||
|
|
||
|
// empty string ?
|
||
|
if (_text.length() == 0) {
|
||
|
d->strOutText = d->strText = "";
|
||
|
setValue (Value::empty());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// as text ?
|
||
|
if (asText) {
|
||
|
d->strOutText = _text;
|
||
|
d->strText = _text;
|
||
|
setValue (Value (_text));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QString oldText = d->strText;
|
||
|
setDisplayText( _text );
|
||
|
if(!format()->sheet()->isLoading() && !testValidity() )
|
||
|
{
|
||
|
//reapply old value if action == stop
|
||
|
setDisplayText( oldText );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Cell::setDisplayText( const QString& _text )
|
||
|
{
|
||
|
bool isLoading = format()->sheet()->isLoading();
|
||
|
|
||
|
if (!isLoading)
|
||
|
format()->sheet()->doc()->emitBeginOperation( false );
|
||
|
|
||
|
d->strText = _text;
|
||
|
|
||
|
/**
|
||
|
* A real formula "=A1+A2*3" was entered.
|
||
|
*/
|
||
|
if ( !d->strText.isEmpty() && d->strText[0] == '=' )
|
||
|
{
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
setFlag(Flag_TextFormatDirty);
|
||
|
|
||
|
if ( !makeFormula() )
|
||
|
kdError(36001) << "ERROR: Syntax ERROR" << endl;
|
||
|
setCalcDirtyFlag ();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Some numeric value or a string.
|
||
|
*/
|
||
|
else
|
||
|
{
|
||
|
// Find out what data type it is
|
||
|
checkTextInput();
|
||
|
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
setFlag(Flag_TextFormatDirty);
|
||
|
}
|
||
|
|
||
|
if ( !isLoading )
|
||
|
format()->sheet()->doc()->emitEndOperation( QRect( d->column, d->row, 1, 1 ) );
|
||
|
}
|
||
|
|
||
|
void Cell::setLink( const QString& link )
|
||
|
{
|
||
|
d->extra()->link = link;
|
||
|
|
||
|
if( !link.isEmpty() && d->strText.isEmpty() )
|
||
|
setCellText( link );
|
||
|
}
|
||
|
|
||
|
QString Cell::link() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->link : QString::null;
|
||
|
}
|
||
|
|
||
|
void Cell::update()
|
||
|
{
|
||
|
/* those obscuring us need to redo their layout cause they can't obscure us
|
||
|
now that we've got text.
|
||
|
This includes cells obscuring cells that we are obscuring
|
||
|
*/
|
||
|
for (int x = d->column; x <= d->column + extraXCells(); x++)
|
||
|
{
|
||
|
for (int y = d->row; y <= d->row + extraYCells(); y++)
|
||
|
{
|
||
|
Cell* cell = format()->sheet()->cellAt(x,y);
|
||
|
cell->setLayoutDirtyFlag();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setCalcDirtyFlag();
|
||
|
|
||
|
/* TODO - is this a good place for this? */
|
||
|
updateChart(true);
|
||
|
}
|
||
|
|
||
|
bool Cell::testValidity() const
|
||
|
{
|
||
|
bool valid = false;
|
||
|
if( d->hasExtra() && d->extra()->validity && d->extra()->validity->m_restriction != Restriction::None )
|
||
|
{
|
||
|
//fixme
|
||
|
if ( d->extra()->validity->allowEmptyCell && d->strText.isEmpty() )
|
||
|
return true;
|
||
|
|
||
|
if( value().isNumber() &&
|
||
|
(d->extra()->validity->m_restriction == Restriction::Number ||
|
||
|
(d->extra()->validity->m_restriction == Restriction::Integer &&
|
||
|
value().asFloat() == ceil(value().asFloat()))))
|
||
|
{
|
||
|
switch( d->extra()->validity->m_cond)
|
||
|
{
|
||
|
case Conditional::Equal:
|
||
|
valid = ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
|
||
|
&& value().asFloat() - d->extra()->validity->valMin >
|
||
|
(0.0 - DBL_EPSILON));
|
||
|
break;
|
||
|
case Conditional::DifferentTo:
|
||
|
valid = !( ( value().asFloat() - d->extra()->validity->valMin < DBL_EPSILON
|
||
|
&& value().asFloat() - d->extra()->validity->valMin >
|
||
|
(0.0 - DBL_EPSILON)) );
|
||
|
break;
|
||
|
case Conditional::Superior:
|
||
|
valid = ( value().asFloat() > d->extra()->validity->valMin);
|
||
|
break;
|
||
|
case Conditional::Inferior:
|
||
|
valid = ( value().asFloat() <d->extra()->validity->valMin);
|
||
|
break;
|
||
|
case Conditional::SuperiorEqual:
|
||
|
valid = ( value().asFloat() >= d->extra()->validity->valMin);
|
||
|
break;
|
||
|
case Conditional::InferiorEqual:
|
||
|
valid = (value().asFloat() <= d->extra()->validity->valMin);
|
||
|
break;
|
||
|
case Conditional::Between:
|
||
|
valid = ( value().asFloat() >= d->extra()->validity->valMin &&
|
||
|
value().asFloat() <= d->extra()->validity->valMax);
|
||
|
break;
|
||
|
case Conditional::Different:
|
||
|
valid = (value().asFloat() < d->extra()->validity->valMin ||
|
||
|
value().asFloat() > d->extra()->validity->valMax);
|
||
|
break;
|
||
|
default :
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if(d->extra()->validity->m_restriction==Restriction::Text)
|
||
|
{
|
||
|
valid = value().isString();
|
||
|
}
|
||
|
else if ( d->extra()->validity->m_restriction == Restriction::List )
|
||
|
{
|
||
|
//test int value
|
||
|
if ( value().isString() && d->extra()->validity->listValidity.contains( value().asString() ) )
|
||
|
valid = true;
|
||
|
}
|
||
|
else if(d->extra()->validity->m_restriction==Restriction::TextLength)
|
||
|
{
|
||
|
if( value().isString() )
|
||
|
{
|
||
|
int len = d->strOutText.length();
|
||
|
switch( d->extra()->validity->m_cond)
|
||
|
{
|
||
|
case Conditional::Equal:
|
||
|
if (len == d->extra()->validity->valMin)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::DifferentTo:
|
||
|
if (len != d->extra()->validity->valMin)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::Superior:
|
||
|
if(len > d->extra()->validity->valMin)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::Inferior:
|
||
|
if(len < d->extra()->validity->valMin)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::SuperiorEqual:
|
||
|
if(len >= d->extra()->validity->valMin)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::InferiorEqual:
|
||
|
if(len <= d->extra()->validity->valMin)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::Between:
|
||
|
if(len >= d->extra()->validity->valMin && len <= d->extra()->validity->valMax)
|
||
|
valid = true;
|
||
|
break;
|
||
|
case Conditional::Different:
|
||
|
if(len <d->extra()->validity->valMin || len >d->extra()->validity->valMax)
|
||
|
valid = true;
|
||
|
break;
|
||
|
default :
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if(d->extra()->validity->m_restriction == Restriction::Time && isTime())
|
||
|
{
|
||
|
switch( d->extra()->validity->m_cond)
|
||
|
{
|
||
|
case Conditional::Equal:
|
||
|
valid = (value().asTime() == d->extra()->validity->timeMin);
|
||
|
break;
|
||
|
case Conditional::DifferentTo:
|
||
|
valid = (value().asTime() != d->extra()->validity->timeMin);
|
||
|
break;
|
||
|
case Conditional::Superior:
|
||
|
valid = (value().asTime() > d->extra()->validity->timeMin);
|
||
|
break;
|
||
|
case Conditional::Inferior:
|
||
|
valid = (value().asTime() < d->extra()->validity->timeMin);
|
||
|
break;
|
||
|
case Conditional::SuperiorEqual:
|
||
|
valid = (value().asTime() >= d->extra()->validity->timeMin);
|
||
|
break;
|
||
|
case Conditional::InferiorEqual:
|
||
|
valid = (value().asTime() <= d->extra()->validity->timeMin);
|
||
|
break;
|
||
|
case Conditional::Between:
|
||
|
valid = (value().asTime() >= d->extra()->validity->timeMin &&
|
||
|
value().asTime() <= d->extra()->validity->timeMax);
|
||
|
break;
|
||
|
case Conditional::Different:
|
||
|
valid = (value().asTime() < d->extra()->validity->timeMin ||
|
||
|
value().asTime() > d->extra()->validity->timeMax);
|
||
|
break;
|
||
|
default :
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else if(d->extra()->validity->m_restriction == Restriction::Date && isDate())
|
||
|
{
|
||
|
switch( d->extra()->validity->m_cond)
|
||
|
{
|
||
|
case Conditional::Equal:
|
||
|
valid = (value().asDate() == d->extra()->validity->dateMin);
|
||
|
break;
|
||
|
case Conditional::DifferentTo:
|
||
|
valid = (value().asDate() != d->extra()->validity->dateMin);
|
||
|
break;
|
||
|
case Conditional::Superior:
|
||
|
valid = (value().asDate() > d->extra()->validity->dateMin);
|
||
|
break;
|
||
|
case Conditional::Inferior:
|
||
|
valid = (value().asDate() < d->extra()->validity->dateMin);
|
||
|
break;
|
||
|
case Conditional::SuperiorEqual:
|
||
|
valid = (value().asDate() >= d->extra()->validity->dateMin);
|
||
|
break;
|
||
|
case Conditional::InferiorEqual:
|
||
|
valid = (value().asDate() <= d->extra()->validity->dateMin);
|
||
|
break;
|
||
|
case Conditional::Between:
|
||
|
valid = (value().asDate() >= d->extra()->validity->dateMin &&
|
||
|
value().asDate() <= d->extra()->validity->dateMax);
|
||
|
break;
|
||
|
case Conditional::Different:
|
||
|
valid = (value().asDate() < d->extra()->validity->dateMin ||
|
||
|
value().asDate() > d->extra()->validity->dateMax);
|
||
|
break;
|
||
|
default :
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
valid= true;
|
||
|
}
|
||
|
|
||
|
if(!valid &&d->extra()->validity != NULL && d->extra()->validity->displayMessage)
|
||
|
{
|
||
|
switch (d->extra()->validity->m_action )
|
||
|
{
|
||
|
case Action::Stop:
|
||
|
KMessageBox::error((QWidget*)0L, d->extra()->validity->message,
|
||
|
d->extra()->validity->title);
|
||
|
break;
|
||
|
case Action::Warning:
|
||
|
KMessageBox::warningYesNo((QWidget*)0L, d->extra()->validity->message,
|
||
|
d->extra()->validity->title);
|
||
|
break;
|
||
|
case Action::Information:
|
||
|
KMessageBox::information((QWidget*)0L, d->extra()->validity->message,
|
||
|
d->extra()->validity->title);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!d->hasExtra())
|
||
|
return true; //okay if there's no validity
|
||
|
return (valid || d->extra()->validity == NULL || d->extra()->validity->m_action != Action::Stop);
|
||
|
}
|
||
|
|
||
|
FormatType Cell::formatType() const
|
||
|
{
|
||
|
return format()->getFormatType( d->column, d->row );
|
||
|
}
|
||
|
|
||
|
double Cell::textWidth() const
|
||
|
{
|
||
|
return d->textWidth;
|
||
|
}
|
||
|
|
||
|
double Cell::textHeight() const
|
||
|
{
|
||
|
return d->textHeight;
|
||
|
}
|
||
|
|
||
|
int Cell::mergedXCells() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->mergedXCells : 0;
|
||
|
}
|
||
|
|
||
|
int Cell::mergedYCells() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->mergedYCells : 0;
|
||
|
}
|
||
|
|
||
|
int Cell::extraXCells() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
}
|
||
|
|
||
|
int Cell::extraYCells() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->extraYCells : 0;
|
||
|
}
|
||
|
|
||
|
double Cell::extraWidth() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->extraWidth : 0;
|
||
|
}
|
||
|
|
||
|
double Cell::extraHeight() const
|
||
|
{
|
||
|
return d->hasExtra() ? d->extra()->extraHeight : 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Cell::isDate() const
|
||
|
{
|
||
|
FormatType ft = formatType();
|
||
|
|
||
|
return (formatIsDate (ft) || ((ft == Generic_format) &&
|
||
|
(value().format() == Value::fmt_Date)));
|
||
|
}
|
||
|
|
||
|
bool Cell::isTime() const
|
||
|
{
|
||
|
FormatType ft = formatType();
|
||
|
|
||
|
return (formatIsTime (ft) || ((ft == Generic_format) &&
|
||
|
(value().format() == Value::fmt_Time)));
|
||
|
}
|
||
|
|
||
|
void Cell::setCalcDirtyFlag()
|
||
|
{
|
||
|
if ( !isFormula() )
|
||
|
{
|
||
|
//don't set the flag if we don't hold a formula
|
||
|
clearFlag(Flag_CalcDirty);
|
||
|
return;
|
||
|
}
|
||
|
setFlag(Flag_CalcDirty);
|
||
|
format()->sheet()->setRegionPaintDirty(cellRect());
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Cell::updateChart(bool refresh)
|
||
|
{
|
||
|
// Update a chart for example if it depends on this cell.
|
||
|
if ( d->row != 0 && d->column != 0 )
|
||
|
{
|
||
|
CellBinding *bind;
|
||
|
for ( bind = format()->sheet()->firstCellBinding(); bind != 0L; bind = format()->sheet()->nextCellBinding() )
|
||
|
{
|
||
|
if ( bind->contains( d->column, d->row ) )
|
||
|
{
|
||
|
if (!refresh)
|
||
|
return true;
|
||
|
|
||
|
bind->cellChanged( this );
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
double Cell::getDouble ()
|
||
|
{
|
||
|
if (isDefault())
|
||
|
return 0.0;
|
||
|
//(Tomas) umm can't we simply call value().asFloat() ?
|
||
|
if (isDate())
|
||
|
{
|
||
|
QDate date = value().asDate();
|
||
|
QDate dummy (1900, 1, 1);
|
||
|
return (dummy.daysTo (date) + 1);
|
||
|
}
|
||
|
if (isTime())
|
||
|
{
|
||
|
QTime time = value().asTime();
|
||
|
QTime dummy;
|
||
|
return dummy.secsTo( time );
|
||
|
}
|
||
|
if (value().isNumber())
|
||
|
return value().asFloat();
|
||
|
|
||
|
return 0.0;
|
||
|
}
|
||
|
|
||
|
void Cell::convertToDouble ()
|
||
|
{
|
||
|
if (isDefault())
|
||
|
return;
|
||
|
|
||
|
setValue (getDouble ());
|
||
|
}
|
||
|
|
||
|
void Cell::convertToPercent ()
|
||
|
{
|
||
|
if (isDefault())
|
||
|
return;
|
||
|
|
||
|
setValue (getDouble ());
|
||
|
d->value.setFormat (Value::fmt_Percent);
|
||
|
}
|
||
|
|
||
|
void Cell::convertToMoney ()
|
||
|
{
|
||
|
if (isDefault())
|
||
|
return;
|
||
|
|
||
|
setValue (getDouble ());
|
||
|
d->value.setFormat (Value::fmt_Money);
|
||
|
format()->setPrecision (locale()->fracDigits());
|
||
|
}
|
||
|
|
||
|
void Cell::convertToTime ()
|
||
|
{
|
||
|
//(Tomas) This is weird. And I mean *REALLY* weird. First, we
|
||
|
//generate a time (QTime), then we convert it to text, then
|
||
|
//we give the text to the cell and ask it to parse it. Weird...
|
||
|
|
||
|
if (isDefault() || isEmpty())
|
||
|
return;
|
||
|
|
||
|
setValue (getDouble ());
|
||
|
QTime time = value().asDateTime().time();
|
||
|
int msec = (int) ( (value().asFloat() - (int) value().asFloat()) * 1000 );
|
||
|
time = time.addMSecs( msec );
|
||
|
setCellText( time.toString() );
|
||
|
}
|
||
|
|
||
|
void Cell::convertToDate ()
|
||
|
{
|
||
|
//(Tomas) This is weird. And I mean *REALLY* weird. First, we
|
||
|
//generate a date (QDate), then we convert it to text, then
|
||
|
//we give the text to the cell and ask it to parse it. Weird...
|
||
|
|
||
|
if (isDefault() || isEmpty())
|
||
|
return;
|
||
|
|
||
|
setValue (getDouble ());
|
||
|
|
||
|
//TODO: why did we call setValue(), when we override it here?
|
||
|
QDate date(1900, 1, 1);
|
||
|
date = date.addDays( (int) value().asFloat() - 1 );
|
||
|
date = value().asDateTime().date();
|
||
|
setCellText (locale()->formatDate (date, true));
|
||
|
}
|
||
|
|
||
|
void Cell::checkTextInput()
|
||
|
{
|
||
|
// Goal of this method: determine the value of the cell
|
||
|
clearAllErrors();
|
||
|
|
||
|
d->value = Value::empty();
|
||
|
|
||
|
// Get the text from that cell
|
||
|
QString str = d->strText;
|
||
|
|
||
|
sheet()->doc()->parser()->parse (str, this);
|
||
|
|
||
|
// Parsing as time acts like an autoformat: we even change d->strText
|
||
|
// [h]:mm:ss -> might get set by ValueParser
|
||
|
if (isTime() && (formatType() != Time_format7))
|
||
|
d->strText = locale()->formatTime( value().asDateTime().time(), true);
|
||
|
|
||
|
// convert first letter to uppercase ?
|
||
|
if (format()->sheet()->getFirstLetterUpper() && value().isString() &&
|
||
|
(!d->strText.isEmpty()))
|
||
|
{
|
||
|
QString str = value().asString();
|
||
|
setValue( Value( str[0].upper() + str.right( str.length()-1 ) ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//used in calc, setNumber, ValueParser
|
||
|
void Cell::checkNumberFormat()
|
||
|
{
|
||
|
if ( formatType() == Number_format && value().isNumber() )
|
||
|
{
|
||
|
if ( value().asFloat() > 1e+10 )
|
||
|
format()->setFormatType( Scientific_format );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// ================================================================
|
||
|
// Saving and loading
|
||
|
|
||
|
|
||
|
QDomElement Cell::save( QDomDocument& doc,
|
||
|
int _x_offset, int _y_offset,
|
||
|
bool force, bool copy, bool era )
|
||
|
{
|
||
|
// Save the position of this cell
|
||
|
QDomElement cell = doc.createElement( "cell" );
|
||
|
cell.setAttribute( "row", d->row - _y_offset );
|
||
|
cell.setAttribute( "column", d->column - _x_offset );
|
||
|
//
|
||
|
// Save the formatting information
|
||
|
//
|
||
|
QDomElement formatElement = format()->save( doc, d->column, d->row, force, copy );
|
||
|
if ( formatElement.hasChildNodes() || formatElement.attributes().length() ) // don't save empty tags
|
||
|
cell.appendChild( formatElement );
|
||
|
|
||
|
if ( doesMergeCells() )
|
||
|
{
|
||
|
if ( extraXCells() )
|
||
|
formatElement.setAttribute( "colspan", extraXCells() );
|
||
|
if ( extraYCells() )
|
||
|
formatElement.setAttribute( "rowspan", extraYCells() );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions )
|
||
|
{
|
||
|
QDomElement conditionElement = d->extra()->conditions->saveConditions( doc );
|
||
|
|
||
|
if ( !conditionElement.isNull() )
|
||
|
cell.appendChild( conditionElement );
|
||
|
}
|
||
|
|
||
|
if ( d->hasExtra() && (d->extra()->validity != 0) )
|
||
|
{
|
||
|
QDomElement validity = doc.createElement("validity");
|
||
|
|
||
|
QDomElement param=doc.createElement("param");
|
||
|
param.setAttribute("cond",(int)d->extra()->validity->m_cond);
|
||
|
param.setAttribute("action",(int)d->extra()->validity->m_action);
|
||
|
param.setAttribute("allow",(int)d->extra()->validity->m_restriction);
|
||
|
param.setAttribute("valmin",d->extra()->validity->valMin);
|
||
|
param.setAttribute("valmax",d->extra()->validity->valMax);
|
||
|
param.setAttribute("displaymessage",d->extra()->validity->displayMessage);
|
||
|
param.setAttribute("displayvalidationinformation",d->extra()->validity->displayValidationInformation);
|
||
|
param.setAttribute("allowemptycell",d->extra()->validity->allowEmptyCell);
|
||
|
if ( !d->extra()->validity->listValidity.isEmpty() )
|
||
|
param.setAttribute( "listvalidity", d->extra()->validity->listValidity.join( ";" ) );
|
||
|
validity.appendChild(param);
|
||
|
QDomElement title = doc.createElement( "title" );
|
||
|
title.appendChild( doc.createTextNode( d->extra()->validity->title ) );
|
||
|
validity.appendChild( title );
|
||
|
QDomElement message = doc.createElement( "message" );
|
||
|
message.appendChild( doc.createCDATASection( d->extra()->validity->message ) );
|
||
|
validity.appendChild( message );
|
||
|
|
||
|
QDomElement inputTitle = doc.createElement( "inputtitle" );
|
||
|
inputTitle.appendChild( doc.createTextNode( d->extra()->validity->titleInfo ) );
|
||
|
validity.appendChild( inputTitle );
|
||
|
|
||
|
QDomElement inputMessage = doc.createElement( "inputmessage" );
|
||
|
inputMessage.appendChild( doc.createTextNode( d->extra()->validity->messageInfo ) );
|
||
|
validity.appendChild( inputMessage );
|
||
|
|
||
|
|
||
|
|
||
|
QString tmp;
|
||
|
if ( d->extra()->validity->timeMin.isValid() )
|
||
|
{
|
||
|
QDomElement timeMin = doc.createElement( "timemin" );
|
||
|
tmp=d->extra()->validity->timeMin.toString();
|
||
|
timeMin.appendChild( doc.createTextNode( tmp ) );
|
||
|
validity.appendChild( timeMin );
|
||
|
}
|
||
|
if ( d->extra()->validity->timeMax.isValid() )
|
||
|
{
|
||
|
QDomElement timeMax = doc.createElement( "timemax" );
|
||
|
tmp=d->extra()->validity->timeMax.toString();
|
||
|
timeMax.appendChild( doc.createTextNode( tmp ) );
|
||
|
validity.appendChild( timeMax );
|
||
|
}
|
||
|
|
||
|
if ( d->extra()->validity->dateMin.isValid() )
|
||
|
{
|
||
|
QDomElement dateMin = doc.createElement( "datemin" );
|
||
|
QString tmp("%1/%2/%3");
|
||
|
tmp = tmp.arg(d->extra()->validity->dateMin.year()).arg(d->extra()->validity->dateMin.month()).arg(d->extra()->validity->dateMin.day());
|
||
|
dateMin.appendChild( doc.createTextNode( tmp ) );
|
||
|
validity.appendChild( dateMin );
|
||
|
}
|
||
|
if ( d->extra()->validity->dateMax.isValid() )
|
||
|
{
|
||
|
QDomElement dateMax = doc.createElement( "datemax" );
|
||
|
QString tmp("%1/%2/%3");
|
||
|
tmp = tmp.arg(d->extra()->validity->dateMax.year()).arg(d->extra()->validity->dateMax.month()).arg(d->extra()->validity->dateMax.day());
|
||
|
dateMax.appendChild( doc.createTextNode( tmp ) );
|
||
|
validity.appendChild( dateMax );
|
||
|
}
|
||
|
|
||
|
cell.appendChild( validity );
|
||
|
}
|
||
|
|
||
|
if ( format()->comment() )
|
||
|
{
|
||
|
QDomElement comment = doc.createElement( "comment" );
|
||
|
comment.appendChild( doc.createCDATASection( *format()->comment() ) );
|
||
|
cell.appendChild( comment );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Save the text
|
||
|
//
|
||
|
if ( !d->strText.isEmpty() )
|
||
|
{
|
||
|
// Formulas need to be encoded to ensure that they
|
||
|
// are position independent.
|
||
|
if ( isFormula() )
|
||
|
{
|
||
|
QDomElement text = doc.createElement( "text" );
|
||
|
// if we are cutting to the clipboard, relative references need to be encoded absolutely
|
||
|
text.appendChild( doc.createTextNode( encodeFormula( era ) ) );
|
||
|
cell.appendChild( text );
|
||
|
|
||
|
/* we still want to save the results of the formula */
|
||
|
QDomElement formulaResult = doc.createElement( "result" );
|
||
|
saveCellResult( doc, formulaResult, d->strOutText );
|
||
|
cell.appendChild( formulaResult );
|
||
|
|
||
|
}
|
||
|
else if ( !link().isEmpty() )
|
||
|
{
|
||
|
// KSpread pre 1.4 saves link as rich text, marked with first char '
|
||
|
// Have to be saved in some CDATA section because of too many special charatcers.
|
||
|
QDomElement text = doc.createElement( "text" );
|
||
|
QString qml = "!<a href=\"" + link() + "\">" + d->strText + "</a>";
|
||
|
text.appendChild( doc.createCDATASection( qml ) );
|
||
|
cell.appendChild( text );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Save the cell contents (in a locale-independent way)
|
||
|
QDomElement text = doc.createElement( "text" );
|
||
|
saveCellResult( doc, text, d->strText );
|
||
|
cell.appendChild( text );
|
||
|
}
|
||
|
}
|
||
|
if ( cell.hasChildNodes() || cell.attributes().length() > 2 ) // don't save empty tags
|
||
|
// (the >2 is due to "row" and "column" attributes)
|
||
|
return cell;
|
||
|
else
|
||
|
return QDomElement();
|
||
|
}
|
||
|
|
||
|
bool Cell::saveCellResult( QDomDocument& doc, QDomElement& result,
|
||
|
QString str )
|
||
|
{
|
||
|
QString dataType = "Other"; // fallback
|
||
|
|
||
|
if ( value().isNumber() )
|
||
|
{
|
||
|
if ( isDate() )
|
||
|
{
|
||
|
// serial number of date
|
||
|
QDate dd = value().asDateTime().date();
|
||
|
dataType = "Date";
|
||
|
str = "%1/%2/%3";
|
||
|
str = str.arg(dd.year()).arg(dd.month()).arg(dd.day());
|
||
|
}
|
||
|
else if( isTime() )
|
||
|
{
|
||
|
// serial number of time
|
||
|
dataType = "Time";
|
||
|
str = value().asDateTime().time().toString();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// real number
|
||
|
dataType = "Num";
|
||
|
if (value().isInteger())
|
||
|
str = QString::number(value().asInteger());
|
||
|
else
|
||
|
str = QString::number(value().asFloat(), 'g', DBL_DIG);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( value().isBoolean() )
|
||
|
{
|
||
|
dataType = "Bool";
|
||
|
str = value().asBoolean() ? "true" : "false";
|
||
|
}
|
||
|
|
||
|
if ( value().isString() )
|
||
|
{
|
||
|
dataType = "Str";
|
||
|
str = value().asString();
|
||
|
}
|
||
|
|
||
|
result.setAttribute( "dataType", dataType );
|
||
|
if ( !d->strOutText.isEmpty() )
|
||
|
result.setAttribute( "outStr", d->strOutText );
|
||
|
result.appendChild( doc.createTextNode( str ) );
|
||
|
|
||
|
return true; /* really isn't much of a way for this function to fail */
|
||
|
}
|
||
|
|
||
|
void Cell::saveOasisAnnotation( KoXmlWriter &xmlwriter )
|
||
|
{
|
||
|
if ( format()->comment() )
|
||
|
{
|
||
|
//<office:annotation draw:style-name="gr1" draw:text-style-name="P1" svg:width="2.899cm" svg:height="2.691cm" svg:x="2.858cm" svg:y="0.001cm" draw:caption-point-x="-2.858cm" draw:caption-point-y="-0.001cm">
|
||
|
xmlwriter.startElement( "office:annotation" );
|
||
|
QStringList text = QStringList::split( "\n", *format()->comment() );
|
||
|
for ( QStringList::Iterator it = text.begin(); it != text.end(); ++it ) {
|
||
|
xmlwriter.startElement( "text:p" );
|
||
|
xmlwriter.addTextNode( *it );
|
||
|
xmlwriter.endElement();
|
||
|
}
|
||
|
xmlwriter.endElement();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
QString Cell::saveOasisCellStyle( KoGenStyle ¤tCellStyle, KoGenStyles &mainStyles )
|
||
|
{
|
||
|
if ( d->hasExtra() && d->extra()->conditions )
|
||
|
{
|
||
|
// this has to be an automatic style
|
||
|
currentCellStyle = KoGenStyle( Doc::STYLE_CELL_AUTO, "table-cell" );
|
||
|
d->extra()->conditions->saveOasisConditions( currentCellStyle );
|
||
|
}
|
||
|
return format()->saveOasisCellStyle( currentCellStyle, mainStyles );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Cell::saveOasis( KoXmlWriter& xmlwriter, KoGenStyles &mainStyles,
|
||
|
int row, int column, int &repeated,
|
||
|
GenValidationStyles &valStyle )
|
||
|
{
|
||
|
if ( !isPartOfMerged() )
|
||
|
xmlwriter.startElement( "table:table-cell" );
|
||
|
else
|
||
|
xmlwriter.startElement( "table:covered-table-cell" );
|
||
|
#if 0
|
||
|
//add font style
|
||
|
QFont font;
|
||
|
Value const value( cell->value() );
|
||
|
if ( !cell->isDefault() )
|
||
|
{
|
||
|
font = cell->format()->textFont( i, row );
|
||
|
m_styles.addFont( font );
|
||
|
|
||
|
if ( cell->format()->hasProperty( Format::PComment ) )
|
||
|
hasComment = true;
|
||
|
}
|
||
|
#endif
|
||
|
// NOTE save the value before the style as long as the Formatter does not work correctly
|
||
|
if ( link().isEmpty() )
|
||
|
saveOasisValue (xmlwriter);
|
||
|
|
||
|
KoGenStyle currentCellStyle; // the type determined in saveOasisCellStyle
|
||
|
saveOasisCellStyle( currentCellStyle,mainStyles );
|
||
|
// skip 'table:style-name' attribute for the default style
|
||
|
if ( !currentCellStyle.isDefaultStyle() )
|
||
|
xmlwriter.addAttribute( "table:style-name", mainStyles.styles()[currentCellStyle] );
|
||
|
|
||
|
// group empty cells with the same style
|
||
|
if ( isEmpty() && !format()->hasProperty( Format::PComment ) &&
|
||
|
!isPartOfMerged() && !doesMergeCells() )
|
||
|
{
|
||
|
bool refCellIsDefault = isDefault();
|
||
|
int j = column + 1;
|
||
|
Cell *nextCell = format()->sheet()->getNextCellRight( column, row );
|
||
|
while ( nextCell )
|
||
|
{
|
||
|
// if
|
||
|
// the next cell is not the adjacent one
|
||
|
// or
|
||
|
// the next cell is not empty
|
||
|
if ( nextCell->column() != j || !nextCell->isEmpty() )
|
||
|
{
|
||
|
if ( refCellIsDefault )
|
||
|
{
|
||
|
// if the origin cell was a default cell,
|
||
|
// we count the default cells
|
||
|
repeated = nextCell->column() - j + 1;
|
||
|
}
|
||
|
// otherwise we just stop here to process the adjacent
|
||
|
// cell in the next iteration of the outer loop
|
||
|
// (in Sheet::saveOasisCells)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
KoGenStyle nextCellStyle; // the type is determined in saveOasisCellStyle
|
||
|
nextCell->saveOasisCellStyle( nextCellStyle,mainStyles );
|
||
|
|
||
|
if ( nextCell->isPartOfMerged() || nextCell->doesMergeCells() ||
|
||
|
nextCell->format()->hasProperty( Format::PComment ) ||
|
||
|
!(nextCellStyle == currentCellStyle) )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
++repeated;
|
||
|
// get the next cell and set the index to the adjacent cell
|
||
|
nextCell = format()->sheet()->getNextCellRight( j++, row );
|
||
|
}
|
||
|
kdDebug() << "Cell::saveOasis: empty cell in column " << column << " "
|
||
|
<< "repeated " << repeated << " time(s)" << endl;
|
||
|
|
||
|
if ( repeated > 1 )
|
||
|
xmlwriter.addAttribute( "table:number-columns-repeated", QString::number( repeated ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
if (d->hasExtra() && d->extra()->validity)
|
||
|
{
|
||
|
GenValidationStyle styleVal(d->extra()->validity);
|
||
|
xmlwriter.addAttribute( "table:validation-name", valStyle.lookup( styleVal ) );
|
||
|
}
|
||
|
if ( isFormula() )
|
||
|
{
|
||
|
//kdDebug() << "Formula found" << endl;
|
||
|
QString formula( convertFormulaToOasisFormat( text() ) );
|
||
|
xmlwriter.addAttribute( "table:formula", formula );
|
||
|
}
|
||
|
else if ( !link().isEmpty() )
|
||
|
{
|
||
|
//kdDebug()<<"Link found \n";
|
||
|
xmlwriter.startElement( "text:p" );
|
||
|
xmlwriter.startElement( "text:a" );
|
||
|
//Reference cell is started by "#"
|
||
|
if ( localReferenceAnchor( link() ) )
|
||
|
xmlwriter.addAttribute( " xlink:href", ( "#"+link() ) );
|
||
|
else
|
||
|
xmlwriter.addAttribute( " xlink:href", link() );
|
||
|
xmlwriter.addTextNode( text() );
|
||
|
xmlwriter.endElement();
|
||
|
xmlwriter.endElement();
|
||
|
}
|
||
|
|
||
|
if ( doesMergeCells() )
|
||
|
{
|
||
|
int colSpan = mergedXCells() + 1;
|
||
|
int rowSpan = mergedYCells() + 1;
|
||
|
|
||
|
if ( colSpan > 1 )
|
||
|
xmlwriter.addAttribute( "table:number-columns-spanned", QString::number( colSpan ) );
|
||
|
|
||
|
if ( rowSpan > 1 )
|
||
|
xmlwriter.addAttribute( "table:number-rows-spanned", QString::number( rowSpan ) );
|
||
|
}
|
||
|
|
||
|
if ( !isEmpty() && link().isEmpty() )
|
||
|
{
|
||
|
xmlwriter.startElement( "text:p" );
|
||
|
xmlwriter.addTextNode( strOutText().utf8() );
|
||
|
xmlwriter.endElement();
|
||
|
}
|
||
|
|
||
|
saveOasisAnnotation( xmlwriter );
|
||
|
|
||
|
xmlwriter.endElement();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Cell::saveOasisValue (KoXmlWriter &xmlWriter)
|
||
|
{
|
||
|
switch (value().format())
|
||
|
{
|
||
|
case Value::fmt_None: break; //NOTHING HERE
|
||
|
case Value::fmt_Boolean:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "boolean" );
|
||
|
xmlWriter.addAttribute( "office:boolean-value", ( value().asBoolean() ?
|
||
|
"true" : "false" ) );
|
||
|
break;
|
||
|
}
|
||
|
case Value::fmt_Number:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "float" );
|
||
|
if (value().isInteger())
|
||
|
xmlWriter.addAttribute( "office:value", QString::number( value().asInteger() ) );
|
||
|
else
|
||
|
xmlWriter.addAttribute( "office:value", QString::number( value().asFloat(), 'g', DBL_DIG ) );
|
||
|
break;
|
||
|
}
|
||
|
case Value::fmt_Percent:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "percentage" );
|
||
|
xmlWriter.addAttribute( "office:value",
|
||
|
QString::number( value().asFloat() ) );
|
||
|
break;
|
||
|
}
|
||
|
case Value::fmt_Money:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "currency" );
|
||
|
Format::Currency currency;
|
||
|
if (format()->currencyInfo(currency))
|
||
|
xmlWriter.addAttribute( "office:currency", Currency::getCurrencyCode(currency.type) );
|
||
|
xmlWriter.addAttribute( "office:value",
|
||
|
QString::number( value().asFloat() ) );
|
||
|
break;
|
||
|
}
|
||
|
case Value::fmt_DateTime: break; //NOTHING HERE
|
||
|
case Value::fmt_Date:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "date" );
|
||
|
xmlWriter.addAttribute( "office:date-value",
|
||
|
value().asDate().toString( Qt::ISODate ) );
|
||
|
break;
|
||
|
}
|
||
|
case Value::fmt_Time:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "time" );
|
||
|
xmlWriter.addAttribute( "office:time-value",
|
||
|
value().asTime().toString( "PThhHmmMssS" ) );
|
||
|
break;
|
||
|
}
|
||
|
case Value::fmt_String:
|
||
|
{
|
||
|
xmlWriter.addAttribute( "office:value-type", "string" );
|
||
|
xmlWriter.addAttribute( "office:string-value", value().asString() );
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
QString Cell::convertFormulaToOasisFormat( const QString & formula ) const
|
||
|
{
|
||
|
QString s;
|
||
|
QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
|
||
|
int n = exp.search( formula, 0 );
|
||
|
kdDebug() << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
|
||
|
<< ", Matched length: " << exp.matchedLength() << endl;
|
||
|
|
||
|
bool inQuote1 = false;
|
||
|
bool inQuote2 = false;
|
||
|
int i = 0;
|
||
|
int l = (int) formula.length();
|
||
|
if ( l <= 0 )
|
||
|
return formula;
|
||
|
while ( i < l )
|
||
|
{
|
||
|
if ( ( n != -1 ) && ( n < i ) )
|
||
|
{
|
||
|
n = exp.search( formula, i );
|
||
|
kdDebug() << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
|
||
|
}
|
||
|
if ( formula[i] == '"' )
|
||
|
{
|
||
|
inQuote1 = !inQuote1;
|
||
|
s += formula[i];
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( formula[i] == '\'' )
|
||
|
{
|
||
|
// named area
|
||
|
inQuote2 = !inQuote2;
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( inQuote1 || inQuote2 )
|
||
|
{
|
||
|
s += formula[i];
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
|
||
|
{
|
||
|
s += '=';
|
||
|
++i;++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( formula[i] == '!' )
|
||
|
{
|
||
|
insertBracket( s );
|
||
|
s += '.';
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( formula[i] == ',' )
|
||
|
{
|
||
|
s += '.';
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( n == i )
|
||
|
{
|
||
|
int ml = exp.matchedLength();
|
||
|
if ( formula[ i + ml ] == '!' )
|
||
|
{
|
||
|
kdDebug() << "No cell ref but sheet name" << endl;
|
||
|
s += formula[i];
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
|
||
|
s += "[.";
|
||
|
for ( int j = 0; j < ml; ++j )
|
||
|
{
|
||
|
s += formula[i];
|
||
|
++i;
|
||
|
}
|
||
|
s += ']';
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
s += formula[i];
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
void Cell::loadOasisConditional( QDomElement * style )
|
||
|
{
|
||
|
if ( style )//safe
|
||
|
{
|
||
|
QDomElement e;
|
||
|
forEachElement( e, style->toElement() )
|
||
|
{
|
||
|
if ( e.localName() == "map" && e.namespaceURI() == KoXmlNS::style )
|
||
|
{
|
||
|
if (d->hasExtra())
|
||
|
delete d->extra()->conditions;
|
||
|
d->extra()->conditions = new Conditions( this );
|
||
|
d->extra()->conditions->loadOasisConditions( e );
|
||
|
d->extra()->conditions->checkMatches();
|
||
|
// break here
|
||
|
// Conditions::loadOasisConditions finishes the iteration
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Cell::loadOasis( const QDomElement& element , KoOasisLoadingContext& oasisContext , Style* style )
|
||
|
{
|
||
|
kdDebug() << "*** Loading cell properties ***** at " << column() << "," << row () << endl;
|
||
|
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "style-name" ) )
|
||
|
{
|
||
|
kdDebug()<<" table:style-name: "<<element.attributeNS( KoXmlNS::table, "style-name", QString::null )<<endl;
|
||
|
oasisContext.fillStyleStack( element, KoXmlNS::table, "styleName", "table-cell" );
|
||
|
|
||
|
QString str = element.attributeNS( KoXmlNS::table, "style-name", QString::null );
|
||
|
const QDomElement* cellStyle = oasisContext.oasisStyles().findStyle( str, "table-cell" );
|
||
|
|
||
|
if ( cellStyle )
|
||
|
loadOasisConditional( const_cast<QDomElement *>( cellStyle ) );
|
||
|
}
|
||
|
|
||
|
if (style)
|
||
|
{
|
||
|
format()->setStyle( style );
|
||
|
}
|
||
|
|
||
|
//Search and load each paragraph of text. Each paragraph is separated by a line break.
|
||
|
loadOasisCellText( element );
|
||
|
|
||
|
//
|
||
|
// formula
|
||
|
//
|
||
|
bool isFormula = false;
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "formula" ) )
|
||
|
{
|
||
|
kdDebug()<<" formula :"<<element.attributeNS( KoXmlNS::table, "formula", QString::null )<<endl;
|
||
|
isFormula = true;
|
||
|
QString oasisFormula( element.attributeNS( KoXmlNS::table, "formula", QString::null ) );
|
||
|
//necessary to remove it to load formula from oocalc2.0 (use namespace)
|
||
|
if (oasisFormula.startsWith( "oooc:" ) )
|
||
|
oasisFormula= oasisFormula.mid( 5 );
|
||
|
else if (oasisFormula.startsWith( "kspr:" ) )
|
||
|
oasisFormula= oasisFormula.mid( 5 );
|
||
|
// TODO Stefan: merge this into Oasis::decodeFormula
|
||
|
checkForNamedAreas( oasisFormula );
|
||
|
oasisFormula = Oasis::decodeFormula( oasisFormula, locale() );
|
||
|
setCellText( oasisFormula );
|
||
|
}
|
||
|
else if ( d->strText.at(0) == '=' ) //prepend ' to the text to avoid = to be painted
|
||
|
d->strText.prepend('\'');
|
||
|
|
||
|
//
|
||
|
// validation
|
||
|
//
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "validation-name" ) )
|
||
|
{
|
||
|
kdDebug()<<" validation-name: "<<element.attributeNS( KoXmlNS::table, "validation-name", QString::null )<<endl;
|
||
|
loadOasisValidation( element.attributeNS( KoXmlNS::table, "validation-name", QString::null ) );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// value type
|
||
|
//
|
||
|
if( element.hasAttributeNS( KoXmlNS::office, "value-type" ) )
|
||
|
{
|
||
|
QString valuetype = element.attributeNS( KoXmlNS::office, "value-type", QString::null );
|
||
|
kdDebug()<<" value-type: " << valuetype << endl;
|
||
|
if( valuetype == "boolean" )
|
||
|
{
|
||
|
QString val = element.attributeNS( KoXmlNS::office, "boolean-value", QString::null ).lower();
|
||
|
if( ( val == "true" ) || ( val == "false" ) )
|
||
|
{
|
||
|
bool value = val == "true";
|
||
|
setCellValue( value );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// integer and floating-point value
|
||
|
else if( valuetype == "float" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
double value = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
|
||
|
if( ok )
|
||
|
setCellValue( value );
|
||
|
|
||
|
if ( !isFormula && d->strText.isEmpty())
|
||
|
{
|
||
|
QString str = locale()->formatNumber( value, 15 );
|
||
|
setCellText( str );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// currency value
|
||
|
else if( valuetype == "currency" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
double value = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
|
||
|
if( ok )
|
||
|
{
|
||
|
setCellValue( value, Money_format );
|
||
|
|
||
|
if (element.hasAttributeNS( KoXmlNS::office, "currency" ) )
|
||
|
{
|
||
|
Currency currency(element.attributeNS( KoXmlNS::office, "currency", QString::null ) );
|
||
|
format()->setCurrency( currency.getIndex(), currency.getDisplayCode() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if( valuetype == "percentage" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
double v = element.attributeNS( KoXmlNS::office, "value", QString::null ).toDouble( &ok );
|
||
|
if( ok )
|
||
|
{
|
||
|
Value value;
|
||
|
value.setValue (v);
|
||
|
value.setFormat (Value::fmt_Percent);
|
||
|
setCellValue( value );
|
||
|
|
||
|
if ( !isFormula && d->strText.isEmpty())
|
||
|
{
|
||
|
QString str = locale()->formatNumber( v, 15 );
|
||
|
setCellText( str );
|
||
|
}
|
||
|
|
||
|
format()->setFormatType (Percentage_format);
|
||
|
}
|
||
|
}
|
||
|
else if ( valuetype == "date" )
|
||
|
{
|
||
|
QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
|
||
|
if ( value.isEmpty() )
|
||
|
value = element.attributeNS( KoXmlNS::office, "date-value", QString::null );
|
||
|
kdDebug() << "Type: date, value: " << value << endl;
|
||
|
|
||
|
// "1980-10-15"
|
||
|
int year = 0, month = 0, day = 0;
|
||
|
bool ok = false;
|
||
|
|
||
|
int p1 = value.find( '-' );
|
||
|
if ( p1 > 0 )
|
||
|
year = value.left( p1 ).toInt( &ok );
|
||
|
|
||
|
kdDebug() << "year: " << value.left( p1 ) << endl;
|
||
|
|
||
|
int p2 = value.find( '-', ++p1 );
|
||
|
|
||
|
if ( ok )
|
||
|
month = value.mid( p1, p2 - p1 ).toInt( &ok );
|
||
|
|
||
|
kdDebug() << "month: " << value.mid( p1, p2 - p1 ) << endl;
|
||
|
|
||
|
if ( ok )
|
||
|
day = value.right( value.length() - p2 - 1 ).toInt( &ok );
|
||
|
|
||
|
kdDebug() << "day: " << value.right( value.length() - p2 ) << endl;
|
||
|
|
||
|
if ( ok )
|
||
|
{
|
||
|
setCellValue( QDate( year, month, day ) );
|
||
|
if ( style )
|
||
|
format()->setFormatType (style->formatType());
|
||
|
kdDebug() << "Set QDate: " << year << " - " << month << " - " << day << endl;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if ( valuetype == "time" )
|
||
|
{
|
||
|
QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
|
||
|
if ( value.isEmpty() )
|
||
|
value = element.attributeNS( KoXmlNS::office, "time-value", QString::null );
|
||
|
kdDebug() << "Type: time: " << value << endl;
|
||
|
// "PT15H10M12S"
|
||
|
int hours = 0, minutes = 0, seconds = 0;
|
||
|
int l = value.length();
|
||
|
QString num;
|
||
|
bool ok = false;
|
||
|
for ( int i = 0; i < l; ++i )
|
||
|
{
|
||
|
if ( value[i].isNumber() )
|
||
|
{
|
||
|
num += value[i];
|
||
|
continue;
|
||
|
}
|
||
|
else if ( value[i] == 'H' )
|
||
|
hours = num.toInt( &ok );
|
||
|
else if ( value[i] == 'M' )
|
||
|
minutes = num.toInt( &ok );
|
||
|
else if ( value[i] == 'S' )
|
||
|
seconds = num.toInt( &ok );
|
||
|
else
|
||
|
continue;
|
||
|
|
||
|
kdDebug() << "Num: " << num << endl;
|
||
|
|
||
|
num = "";
|
||
|
if ( !ok )
|
||
|
break;
|
||
|
}
|
||
|
kdDebug() << "Hours: " << hours << ", " << minutes << ", " << seconds << endl;
|
||
|
|
||
|
if ( ok )
|
||
|
{
|
||
|
// Value kval( timeToNum( hours, minutes, seconds ) );
|
||
|
// cell->setValue( kval );
|
||
|
setCellValue( QTime( hours % 24, minutes, seconds ) );
|
||
|
if ( style )
|
||
|
format()->setFormatType (style->formatType());
|
||
|
}
|
||
|
}
|
||
|
else if( valuetype == "string" )
|
||
|
{
|
||
|
QString value = element.attributeNS( KoXmlNS::office, "value", QString::null );
|
||
|
if ( value.isEmpty() && element.hasAttributeNS( KoXmlNS::office, "string-value" ))
|
||
|
{
|
||
|
//if there is not string-value entry don't overwrite value stored into <text:p>
|
||
|
value = element.attributeNS( KoXmlNS::office, "string-value", QString::null );
|
||
|
setCellValue( value );
|
||
|
}
|
||
|
format()->setFormatType (Text_format);
|
||
|
}
|
||
|
else
|
||
|
kdDebug()<<" type of value found : "<<valuetype<<endl;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// merged cells ?
|
||
|
//
|
||
|
int colSpan = 1;
|
||
|
int rowSpan = 1;
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "number-columns-spanned" ) )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
int span = element.attributeNS( KoXmlNS::table, "number-columns-spanned", QString::null ).toInt( &ok );
|
||
|
if( ok ) colSpan = span;
|
||
|
}
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "number-rows-spanned" ) )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
int span = element.attributeNS( KoXmlNS::table, "number-rows-spanned", QString::null ).toInt( &ok );
|
||
|
if( ok ) rowSpan = span;
|
||
|
}
|
||
|
if ( colSpan > 1 || rowSpan > 1 )
|
||
|
mergeCells( d->column, d->row, colSpan - 1, rowSpan - 1 );
|
||
|
|
||
|
//
|
||
|
// cell comment/annotation
|
||
|
//
|
||
|
QDomElement annotationElement = KoDom::namedItemNS( element, KoXmlNS::office, "annotation" );
|
||
|
if ( !annotationElement.isNull() )
|
||
|
{
|
||
|
QString comment;
|
||
|
QDomNode node = annotationElement.firstChild();
|
||
|
while( !node.isNull() )
|
||
|
{
|
||
|
QDomElement commentElement = node.toElement();
|
||
|
if( !commentElement.isNull() )
|
||
|
if( commentElement.localName() == "p" && commentElement.namespaceURI() == KoXmlNS::text )
|
||
|
{
|
||
|
if( !comment.isEmpty() ) comment.append( '\n' );
|
||
|
comment.append( commentElement.text() );
|
||
|
}
|
||
|
|
||
|
node = node.nextSibling();
|
||
|
}
|
||
|
|
||
|
if( !comment.isEmpty() )
|
||
|
format()->setComment( comment );
|
||
|
}
|
||
|
|
||
|
QDomElement frame = KoDom::namedItemNS( element, KoXmlNS::draw, "frame" );
|
||
|
if ( !frame.isNull() )
|
||
|
loadOasisObjects( frame, oasisContext );
|
||
|
|
||
|
if (isFormula)
|
||
|
setCalcDirtyFlag (); // formulas must be recalculated
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Cell::loadOasisCellText( const QDomElement& parent )
|
||
|
{
|
||
|
//Search and load each paragraph of text. Each paragraph is separated by a line break
|
||
|
QDomElement textParagraphElement;
|
||
|
QString cellText;
|
||
|
|
||
|
bool multipleTextParagraphsFound=false;
|
||
|
|
||
|
forEachElement( textParagraphElement , parent )
|
||
|
{
|
||
|
if ( textParagraphElement.localName()=="p" &&
|
||
|
textParagraphElement.namespaceURI()== KoXmlNS::text )
|
||
|
{
|
||
|
// our text, could contain formating for value or result of formul
|
||
|
if (cellText.isEmpty())
|
||
|
cellText = textParagraphElement.text();
|
||
|
else
|
||
|
{
|
||
|
cellText += "\n"+textParagraphElement.text();
|
||
|
multipleTextParagraphsFound=true;
|
||
|
}
|
||
|
|
||
|
QDomElement textA = KoDom::namedItemNS( textParagraphElement, KoXmlNS::text, "a" );
|
||
|
if( !textA.isNull() )
|
||
|
{
|
||
|
if ( textA.hasAttributeNS( KoXmlNS::xlink, "href" ) )
|
||
|
{
|
||
|
QString link = textA.attributeNS( KoXmlNS::xlink, "href", QString::null );
|
||
|
cellText = textA.text();
|
||
|
setCellText( cellText );
|
||
|
setValue( cellText );
|
||
|
if ( link[0]=='#' )
|
||
|
link=link.remove( 0, 1 );
|
||
|
setLink( link );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!cellText.isNull())
|
||
|
{
|
||
|
setCellText( cellText );
|
||
|
setValue( cellText );
|
||
|
}
|
||
|
|
||
|
//Enable word wrapping if multiple lines of text have been found.
|
||
|
if ( multipleTextParagraphsFound )
|
||
|
{
|
||
|
format()->setMultiRow(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Cell::loadOasisObjects( const QDomElement &parent, KoOasisLoadingContext& oasisContext )
|
||
|
{
|
||
|
for( QDomElement e = parent; !e.isNull(); e = e.nextSibling().toElement() )
|
||
|
{
|
||
|
if ( e.localName() == "frame" && e.namespaceURI() == KoXmlNS::draw )
|
||
|
{
|
||
|
EmbeddedObject *obj = 0;
|
||
|
QDomNode object = KoDom::namedItemNS( e, KoXmlNS::draw, "object" );
|
||
|
if ( !object.isNull() )
|
||
|
{
|
||
|
if ( !object.toElement().attributeNS( KoXmlNS::draw, "notify-on-update-of-ranges", QString::null).isNull() )
|
||
|
obj = new EmbeddedChart( sheet()->doc(), sheet() );
|
||
|
else
|
||
|
obj = new EmbeddedKOfficeObject( sheet()->doc(), sheet() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
QDomNode image = KoDom::namedItemNS( e, KoXmlNS::draw, "image" );
|
||
|
if ( !image.isNull() )
|
||
|
obj = new EmbeddedPictureObject( sheet(), sheet()->doc()->pictureCollection() );
|
||
|
else
|
||
|
kdDebug() << "Object type wasn't loaded!" << endl;
|
||
|
}
|
||
|
|
||
|
if ( obj )
|
||
|
{
|
||
|
obj->loadOasis( e, oasisContext );
|
||
|
sheet()->doc()->insertObject( obj );
|
||
|
|
||
|
QString ref = e.attributeNS( KoXmlNS::table, "end-cell-address", QString::null );
|
||
|
if ( ref.isNull() )
|
||
|
continue;
|
||
|
|
||
|
ref = Oasis::decodeFormula( ref );
|
||
|
Point point( ref );
|
||
|
if ( !point.isValid() )
|
||
|
continue;
|
||
|
|
||
|
KoRect geometry = obj->geometry();
|
||
|
geometry.setLeft( geometry.left() + sheet()->columnPos( d->column, 0 ) );
|
||
|
geometry.setTop( geometry.top() + sheet()->rowPos( d->row, 0 ) );
|
||
|
|
||
|
QString str = e.attributeNS( KoXmlNS::table, "end-x", QString::null );
|
||
|
if ( !str.isNull() )
|
||
|
{
|
||
|
uint end_x = (uint) KoUnit::parseValue( str );
|
||
|
geometry.setRight( sheet()->columnPos( point.column(), 0) + end_x );
|
||
|
}
|
||
|
|
||
|
str = e.attributeNS( KoXmlNS::table, "end-y", QString::null );
|
||
|
if ( !str.isNull() )
|
||
|
{
|
||
|
uint end_y = (uint) KoUnit::parseValue( str );
|
||
|
geometry.setBottom( sheet()->rowPos( point.row(), 0) + end_y );
|
||
|
}
|
||
|
|
||
|
obj->setGeometry( geometry );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Cell::loadOasisValidation( const QString& validationName )
|
||
|
{
|
||
|
QDomElement element = sheet()->doc()->loadingInfo()->validation( validationName);
|
||
|
if (d->hasExtra())
|
||
|
delete d->extra()->validity;
|
||
|
d->extra()->validity = new Validity;
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "condition" ) )
|
||
|
{
|
||
|
QString valExpression = element.attributeNS( KoXmlNS::table, "condition", QString::null );
|
||
|
kdDebug()<<" element.attribute( table:condition ) "<<valExpression<<endl;
|
||
|
//Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition
|
||
|
//TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
|
||
|
//ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value
|
||
|
//TrueCondition ::= GetFunction | cell-content() Operator Value
|
||
|
//GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
|
||
|
//ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value)
|
||
|
//Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!='
|
||
|
//Value ::= NumberValue | String | Formula
|
||
|
//A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information.
|
||
|
//A String comprises one or more characters surrounded by quotation marks.
|
||
|
//A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater.
|
||
|
|
||
|
//ExtendedTrueCondition
|
||
|
if ( valExpression.contains( "cell-content-text-length()" ) )
|
||
|
{
|
||
|
//"cell-content-text-length()>45"
|
||
|
valExpression = valExpression.remove("oooc:cell-content-text-length()" );
|
||
|
kdDebug()<<" valExpression = :"<<valExpression<<endl;
|
||
|
d->extra()->validity->m_restriction = Restriction::TextLength;
|
||
|
|
||
|
loadOasisValidationCondition( valExpression );
|
||
|
}
|
||
|
else if ( valExpression.contains( "cell-content-is-text()" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::Text;
|
||
|
}
|
||
|
//cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) | cell-content-is-in-list( StringList )
|
||
|
else if ( valExpression.contains( "cell-content-text-length-is-between" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::TextLength;
|
||
|
d->extra()->validity->m_cond = Conditional::Between;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-text-length-is-between(" );
|
||
|
kdDebug()<<" valExpression :"<<valExpression<<endl;
|
||
|
valExpression = valExpression.remove( ")" );
|
||
|
QStringList listVal = QStringList::split( ",", valExpression );
|
||
|
loadOasisValidationValue( listVal );
|
||
|
}
|
||
|
else if ( valExpression.contains( "cell-content-text-length-is-not-between" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::TextLength;
|
||
|
d->extra()->validity->m_cond = Conditional::Different;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-text-length-is-not-between(" );
|
||
|
kdDebug()<<" valExpression :"<<valExpression<<endl;
|
||
|
valExpression = valExpression.remove( ")" );
|
||
|
kdDebug()<<" valExpression :"<<valExpression<<endl;
|
||
|
QStringList listVal = QStringList::split( ",", valExpression );
|
||
|
loadOasisValidationValue( listVal );
|
||
|
}
|
||
|
else if ( valExpression.contains( "cell-content-is-in-list(" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::List;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-is-in-list(" );
|
||
|
kdDebug()<<" valExpression :"<<valExpression<<endl;
|
||
|
valExpression = valExpression.remove( ")" );
|
||
|
d->extra()->validity->listValidity = QStringList::split( ";", valExpression );
|
||
|
|
||
|
}
|
||
|
//TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time()
|
||
|
else
|
||
|
{
|
||
|
if (valExpression.contains( "cell-content-is-whole-number()" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::Number;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-is-whole-number() and " );
|
||
|
}
|
||
|
else if (valExpression.contains( "cell-content-is-decimal-number()" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::Integer;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-is-decimal-number() and " );
|
||
|
}
|
||
|
else if (valExpression.contains( "cell-content-is-date()" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::Date;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-is-date() and " );
|
||
|
}
|
||
|
else if (valExpression.contains( "cell-content-is-time()" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = Restriction::Time;
|
||
|
valExpression = valExpression.remove( "oooc:cell-content-is-time() and " );
|
||
|
}
|
||
|
kdDebug()<<"valExpression :"<<valExpression<<endl;
|
||
|
|
||
|
if ( valExpression.contains( "cell-content()" ) )
|
||
|
{
|
||
|
valExpression = valExpression.remove( "cell-content()" );
|
||
|
loadOasisValidationCondition( valExpression );
|
||
|
}
|
||
|
//GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
|
||
|
//for the moment we support just int/double value, not text/date/time :(
|
||
|
if ( valExpression.contains( "cell-content-is-between(" ) )
|
||
|
{
|
||
|
valExpression = valExpression.remove( "cell-content-is-between(" );
|
||
|
valExpression = valExpression.remove( ")" );
|
||
|
QStringList listVal = QStringList::split( "," , valExpression );
|
||
|
loadOasisValidationValue( listVal );
|
||
|
d->extra()->validity->m_cond = Conditional::Between;
|
||
|
}
|
||
|
if ( valExpression.contains( "cell-content-is-not-between(" ) )
|
||
|
{
|
||
|
valExpression = valExpression.remove( "cell-content-is-not-between(" );
|
||
|
valExpression = valExpression.remove( ")" );
|
||
|
QStringList listVal = QStringList::split( ",", valExpression );
|
||
|
loadOasisValidationValue( listVal );
|
||
|
d->extra()->validity->m_cond = Conditional::Different;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" ) )
|
||
|
{
|
||
|
kdDebug()<<" element.hasAttribute( table:allow-empty-cell ) :"<<element.hasAttributeNS( KoXmlNS::table, "allow-empty-cell" )<<endl;
|
||
|
d->extra()->validity->allowEmptyCell = ( ( element.attributeNS( KoXmlNS::table, "allow-empty-cell", QString::null )=="true" ) ? true : false );
|
||
|
}
|
||
|
if ( element.hasAttributeNS( KoXmlNS::table, "base-cell-address" ) )
|
||
|
{
|
||
|
//todo what is it ?
|
||
|
}
|
||
|
|
||
|
QDomElement help = KoDom::namedItemNS( element, KoXmlNS::table, "help-message" );
|
||
|
if ( !help.isNull() )
|
||
|
{
|
||
|
if ( help.hasAttributeNS( KoXmlNS::table, "title" ) )
|
||
|
{
|
||
|
kdDebug()<<"help.attribute( table:title ) :"<<help.attributeNS( KoXmlNS::table, "title", QString::null )<<endl;
|
||
|
d->extra()->validity->titleInfo = help.attributeNS( KoXmlNS::table, "title", QString::null );
|
||
|
}
|
||
|
if ( help.hasAttributeNS( KoXmlNS::table, "display" ) )
|
||
|
{
|
||
|
kdDebug()<<"help.attribute( table:display ) :"<<help.attributeNS( KoXmlNS::table, "display", QString::null )<<endl;
|
||
|
d->extra()->validity->displayValidationInformation = ( ( help.attributeNS( KoXmlNS::table, "display", QString::null )=="true" ) ? true : false );
|
||
|
}
|
||
|
QDomElement attrText = KoDom::namedItemNS( help, KoXmlNS::text, "p" );
|
||
|
if ( !attrText.isNull() )
|
||
|
{
|
||
|
kdDebug()<<"help text :"<<attrText.text()<<endl;
|
||
|
d->extra()->validity->messageInfo = attrText.text();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QDomElement error = KoDom::namedItemNS( element, KoXmlNS::table, "error-message" );
|
||
|
if ( !error.isNull() )
|
||
|
{
|
||
|
if ( error.hasAttributeNS( KoXmlNS::table, "title" ) )
|
||
|
d->extra()->validity->title = error.attributeNS( KoXmlNS::table, "title", QString::null );
|
||
|
if ( error.hasAttributeNS( KoXmlNS::table, "message-type" ) )
|
||
|
{
|
||
|
QString str = error.attributeNS( KoXmlNS::table, "message-type", QString::null );
|
||
|
if ( str == "warning" )
|
||
|
d->extra()->validity->m_action = Action::Warning;
|
||
|
else if ( str == "information" )
|
||
|
d->extra()->validity->m_action = Action::Information;
|
||
|
else if ( str == "stop" )
|
||
|
d->extra()->validity->m_action = Action::Stop;
|
||
|
else
|
||
|
kdDebug()<<"validation : message type unknown :"<<str<<endl;
|
||
|
}
|
||
|
|
||
|
if ( error.hasAttributeNS( KoXmlNS::table, "display" ) )
|
||
|
{
|
||
|
kdDebug()<<" display message :"<<error.attributeNS( KoXmlNS::table, "display", QString::null )<<endl;
|
||
|
d->extra()->validity->displayMessage = (error.attributeNS( KoXmlNS::table, "display", QString::null )=="true");
|
||
|
}
|
||
|
QDomElement attrText = KoDom::namedItemNS( error, KoXmlNS::text, "p" );
|
||
|
if ( !attrText.isNull() )
|
||
|
d->extra()->validity->message = attrText.text();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Cell::loadOasisValidationValue( const QStringList &listVal )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
|
||
|
|
||
|
if ( d->extra()->validity->m_restriction == Restriction::Date )
|
||
|
{
|
||
|
d->extra()->validity->dateMin = QDate::fromString( listVal[0] );
|
||
|
d->extra()->validity->dateMax = QDate::fromString( listVal[1] );
|
||
|
}
|
||
|
else if ( d->extra()->validity->m_restriction == Restriction::Time )
|
||
|
{
|
||
|
d->extra()->validity->timeMin = QTime::fromString( listVal[0] );
|
||
|
d->extra()->validity->timeMax = QTime::fromString( listVal[1] );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->extra()->validity->valMin = listVal[0].toDouble(&ok);
|
||
|
if ( !ok )
|
||
|
{
|
||
|
d->extra()->validity->valMin = listVal[0].toInt(&ok);
|
||
|
if ( !ok )
|
||
|
kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
|
||
|
|
||
|
#if 0
|
||
|
if ( !ok )
|
||
|
d->extra()->validity->valMin = listVal[0];
|
||
|
#endif
|
||
|
}
|
||
|
ok=false;
|
||
|
d->extra()->validity->valMax = listVal[1].toDouble(&ok);
|
||
|
if ( !ok )
|
||
|
{
|
||
|
d->extra()->validity->valMax = listVal[1].toInt(&ok);
|
||
|
if ( !ok )
|
||
|
kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
|
||
|
|
||
|
#if 0
|
||
|
if ( !ok )
|
||
|
d->extra()->validity->valMax = listVal[1];
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Cell::loadOasisValidationCondition( QString &valExpression )
|
||
|
{
|
||
|
QString value;
|
||
|
if (valExpression.find( "<=" )==0 )
|
||
|
{
|
||
|
value = valExpression.remove( 0,2 );
|
||
|
d->extra()->validity->m_cond = Conditional::InferiorEqual;
|
||
|
}
|
||
|
else if (valExpression.find( ">=" )==0 )
|
||
|
{
|
||
|
value = valExpression.remove( 0,2 );
|
||
|
d->extra()->validity->m_cond = Conditional::SuperiorEqual;
|
||
|
}
|
||
|
else if (valExpression.find( "!=" )==0 )
|
||
|
{
|
||
|
//add Differentto attribute
|
||
|
value = valExpression.remove( 0,2 );
|
||
|
d->extra()->validity->m_cond = Conditional::DifferentTo;
|
||
|
}
|
||
|
else if ( valExpression.find( "<" )==0 )
|
||
|
{
|
||
|
value = valExpression.remove( 0,1 );
|
||
|
d->extra()->validity->m_cond = Conditional::Inferior;
|
||
|
}
|
||
|
else if(valExpression.find( ">" )==0 )
|
||
|
{
|
||
|
value = valExpression.remove( 0,1 );
|
||
|
d->extra()->validity->m_cond = Conditional::Superior;
|
||
|
}
|
||
|
else if (valExpression.find( "=" )==0 )
|
||
|
{
|
||
|
value = valExpression.remove( 0,1 );
|
||
|
d->extra()->validity->m_cond = Conditional::Equal;
|
||
|
}
|
||
|
else
|
||
|
kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
|
||
|
if ( d->extra()->validity->m_restriction == Restriction::Date )
|
||
|
{
|
||
|
d->extra()->validity->dateMin = QDate::fromString( value );
|
||
|
}
|
||
|
else if (d->extra()->validity->m_restriction == Restriction::Date )
|
||
|
{
|
||
|
d->extra()->validity->timeMin = QTime::fromString( value );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool ok = false;
|
||
|
d->extra()->validity->valMin = value.toDouble(&ok);
|
||
|
if ( !ok )
|
||
|
{
|
||
|
d->extra()->validity->valMin = value.toInt(&ok);
|
||
|
if ( !ok )
|
||
|
kdDebug()<<" Try to parse this value :"<<value<<endl;
|
||
|
|
||
|
#if 0
|
||
|
if ( !ok )
|
||
|
d->extra()->validity->valMin = value;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Cell::load( const QDomElement & cell, int _xshift, int _yshift,
|
||
|
Paste::Mode pm, Paste::Operation op, bool paste )
|
||
|
{
|
||
|
bool ok;
|
||
|
|
||
|
//
|
||
|
// First of all determine in which row and column this
|
||
|
// cell belongs.
|
||
|
//
|
||
|
d->row = cell.attribute( "row" ).toInt( &ok ) + _yshift;
|
||
|
if ( !ok ) return false;
|
||
|
d->column = cell.attribute( "column" ).toInt( &ok ) + _xshift;
|
||
|
if ( !ok ) return false;
|
||
|
|
||
|
// Validation
|
||
|
if ( d->row < 1 || d->row > KS_rowMax )
|
||
|
{
|
||
|
kdDebug(36001) << "Cell::load: Value out of Range Cell:row=" << d->row << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if ( d->column < 1 || d->column > KS_colMax )
|
||
|
{
|
||
|
kdDebug(36001) << "Cell::load: Value out of Range Cell:column=" << d->column << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Load formatting information.
|
||
|
//
|
||
|
QDomElement f = cell.namedItem( "format" ).toElement();
|
||
|
if ( !f.isNull()
|
||
|
&& ( (pm == Paste::Normal) || (pm == Paste::Format) || (pm == Paste::NoBorder) ) )
|
||
|
{
|
||
|
// send pm parameter. Didn't load Borders if pm==NoBorder
|
||
|
|
||
|
if ( !format()->load( f, pm, paste ) )
|
||
|
return false;
|
||
|
|
||
|
if ( f.hasAttribute( "colspan" ) )
|
||
|
{
|
||
|
int i = f.attribute("colspan").toInt( &ok );
|
||
|
if ( !ok ) return false;
|
||
|
// Validation
|
||
|
if ( i < 0 || i > KS_spanMax )
|
||
|
{
|
||
|
kdDebug(36001) << "Value out of range Cell::colspan=" << i << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if (i || d->hasExtra())
|
||
|
d->extra()->extraXCells = i;
|
||
|
if ( i > 0 )
|
||
|
{
|
||
|
setFlag(Flag_Merged);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( f.hasAttribute( "rowspan" ) )
|
||
|
{
|
||
|
int i = f.attribute("rowspan").toInt( &ok );
|
||
|
if ( !ok ) return false;
|
||
|
// Validation
|
||
|
if ( i < 0 || i > KS_spanMax )
|
||
|
{
|
||
|
kdDebug(36001) << "Value out of range Cell::rowspan=" << i << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if (i || d->hasExtra())
|
||
|
d->extra()->extraYCells = i;
|
||
|
if ( i > 0 )
|
||
|
{
|
||
|
setFlag(Flag_Merged);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( testFlag( Flag_Merged ) )
|
||
|
{
|
||
|
if (d->hasExtra())
|
||
|
mergeCells( d->column, d->row, d->extra()->extraXCells, d->extra()->extraYCells );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Load the condition section of a cell.
|
||
|
//
|
||
|
QDomElement conditionsElement = cell.namedItem( "condition" ).toElement();
|
||
|
if ( !conditionsElement.isNull())
|
||
|
{
|
||
|
if (d->hasExtra())
|
||
|
delete d->extra()->conditions;
|
||
|
d->extra()->conditions = new Conditions( this );
|
||
|
d->extra()->conditions->loadConditions( conditionsElement );
|
||
|
d->extra()->conditions->checkMatches();
|
||
|
}
|
||
|
else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
|
||
|
{
|
||
|
//clear the conditional formatting
|
||
|
if (d->hasExtra())
|
||
|
{
|
||
|
delete d->extra()->conditions;
|
||
|
d->extra()->conditions = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QDomElement validity = cell.namedItem( "validity" ).toElement();
|
||
|
if ( !validity.isNull())
|
||
|
{
|
||
|
QDomElement param = validity.namedItem( "param" ).toElement();
|
||
|
if(!param.isNull())
|
||
|
{
|
||
|
d->extra()->validity = new Validity;
|
||
|
if ( param.hasAttribute( "cond" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_cond = (Conditional::Type) param.attribute("cond").toInt( &ok );
|
||
|
if ( !ok )
|
||
|
return false;
|
||
|
}
|
||
|
if ( param.hasAttribute( "action" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_action = (Action::Type) param.attribute("action").toInt( &ok );
|
||
|
if ( !ok )
|
||
|
return false;
|
||
|
}
|
||
|
if ( param.hasAttribute( "allow" ) )
|
||
|
{
|
||
|
d->extra()->validity->m_restriction = (Restriction::Type) param.attribute("allow").toInt( &ok );
|
||
|
if ( !ok )
|
||
|
return false;
|
||
|
}
|
||
|
if ( param.hasAttribute( "valmin" ) )
|
||
|
{
|
||
|
d->extra()->validity->valMin = param.attribute("valmin").toDouble( &ok );
|
||
|
if ( !ok )
|
||
|
return false;
|
||
|
}
|
||
|
if ( param.hasAttribute( "valmax" ) )
|
||
|
{
|
||
|
d->extra()->validity->valMax = param.attribute("valmax").toDouble( &ok );
|
||
|
if ( !ok )
|
||
|
return false;
|
||
|
}
|
||
|
if ( param.hasAttribute( "displaymessage" ) )
|
||
|
{
|
||
|
d->extra()->validity->displayMessage = ( bool )param.attribute("displaymessage").toInt();
|
||
|
}
|
||
|
if ( param.hasAttribute( "displayvalidationinformation" ) )
|
||
|
{
|
||
|
d->extra()->validity->displayValidationInformation = ( bool )param.attribute("displayvalidationinformation").toInt();
|
||
|
}
|
||
|
if ( param.hasAttribute( "allowemptycell" ) )
|
||
|
{
|
||
|
d->extra()->validity->allowEmptyCell = ( bool )param.attribute("allowemptycell").toInt();
|
||
|
}
|
||
|
if ( param.hasAttribute("listvalidity") )
|
||
|
{
|
||
|
d->extra()->validity->listValidity=QStringList::split(";", param.attribute("listvalidity") );
|
||
|
}
|
||
|
}
|
||
|
QDomElement inputTitle = validity.namedItem( "inputtitle" ).toElement();
|
||
|
if (!inputTitle.isNull())
|
||
|
{
|
||
|
d->extra()->validity->titleInfo = inputTitle.text();
|
||
|
}
|
||
|
QDomElement inputMessage = validity.namedItem( "inputmessage" ).toElement();
|
||
|
if (!inputMessage.isNull())
|
||
|
{
|
||
|
d->extra()->validity->messageInfo = inputMessage.text();
|
||
|
}
|
||
|
|
||
|
QDomElement title = validity.namedItem( "title" ).toElement();
|
||
|
if (!title.isNull())
|
||
|
{
|
||
|
d->extra()->validity->title = title.text();
|
||
|
}
|
||
|
QDomElement message = validity.namedItem( "message" ).toElement();
|
||
|
if (!message.isNull())
|
||
|
{
|
||
|
d->extra()->validity->message = message.text();
|
||
|
}
|
||
|
QDomElement timeMin = validity.namedItem( "timemin" ).toElement();
|
||
|
if ( !timeMin.isNull() )
|
||
|
{
|
||
|
d->extra()->validity->timeMin = toTime(timeMin);
|
||
|
}
|
||
|
QDomElement timeMax = validity.namedItem( "timemax" ).toElement();
|
||
|
if ( !timeMax.isNull() )
|
||
|
{
|
||
|
d->extra()->validity->timeMax = toTime(timeMax);
|
||
|
}
|
||
|
QDomElement dateMin = validity.namedItem( "datemin" ).toElement();
|
||
|
if ( !dateMin.isNull() )
|
||
|
{
|
||
|
d->extra()->validity->dateMin = toDate(dateMin);
|
||
|
}
|
||
|
QDomElement dateMax = validity.namedItem( "datemax" ).toElement();
|
||
|
if ( !dateMax.isNull() )
|
||
|
{
|
||
|
d->extra()->validity->dateMax = toDate(dateMax);
|
||
|
}
|
||
|
}
|
||
|
else if ((pm == Paste::Normal) || (pm == Paste::NoBorder))
|
||
|
{
|
||
|
// clear the validity
|
||
|
removeValidity();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Load the comment
|
||
|
//
|
||
|
QDomElement comment = cell.namedItem( "comment" ).toElement();
|
||
|
if ( !comment.isNull() && ( pm == Paste::Normal || pm == Paste::Comment || pm == Paste::NoBorder ))
|
||
|
{
|
||
|
QString t = comment.text();
|
||
|
//t = t.stripWhiteSpace();
|
||
|
format()->setComment( t );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The real content of the cell is loaded here. It is stored in
|
||
|
// the "text" tag, which contains either a text or a CDATA section.
|
||
|
//
|
||
|
// TODO: make this suck less. We set data twice, in loadCellData, and
|
||
|
// also here. Not good.
|
||
|
QDomElement text = cell.namedItem( "text" ).toElement();
|
||
|
|
||
|
if ( !text.isNull() &&
|
||
|
( pm == Paste::Normal || pm == Paste::Text || pm == Paste::NoBorder || pm == Paste::Result ) )
|
||
|
{
|
||
|
/* older versions mistakenly put the datatype attribute on the cell
|
||
|
instead of the text. Just move it over in case we're parsing
|
||
|
an old document */
|
||
|
if ( cell.hasAttribute( "dataType" ) ) // new docs
|
||
|
text.setAttribute( "dataType", cell.attribute( "dataType" ) );
|
||
|
|
||
|
QDomElement result = cell.namedItem( "result" ).toElement();
|
||
|
QString txt = text.text();
|
||
|
if ((pm == Paste::Result) && (txt[0] == '='))
|
||
|
// paste text of the element, if we want to paste result
|
||
|
// and the source cell contains a formula
|
||
|
// note that we mustn't use setCellValue after this, or else we lose
|
||
|
// all the formulas ...
|
||
|
d->strText = result.text();
|
||
|
else
|
||
|
//otherwise copy everything
|
||
|
loadCellData(text, op);
|
||
|
|
||
|
if ( !result.isNull() )
|
||
|
{
|
||
|
QString dataType;
|
||
|
QString t = result.text();
|
||
|
|
||
|
if ( result.hasAttribute( "dataType" ) )
|
||
|
dataType = result.attribute( "dataType" );
|
||
|
if ( result.hasAttribute( "outStr" ) )
|
||
|
{
|
||
|
d->strOutText = result.attribute( "outStr" );
|
||
|
if ( !d->strOutText.isEmpty() )
|
||
|
clearFlag( Flag_TextFormatDirty );
|
||
|
}
|
||
|
|
||
|
bool clear = true;
|
||
|
// boolean ?
|
||
|
if( dataType == "Bool" )
|
||
|
{
|
||
|
if ( t == "false" )
|
||
|
setValue( false );
|
||
|
else if ( t == "true" )
|
||
|
setValue( true );
|
||
|
else
|
||
|
clear = false;
|
||
|
}
|
||
|
else if( dataType == "Num" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
double dd = t.toDouble( &ok );
|
||
|
if ( ok )
|
||
|
setValue ( dd );
|
||
|
else
|
||
|
clear = false;
|
||
|
}
|
||
|
else if( dataType == "Date" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
double dd = t.toDouble( &ok );
|
||
|
if ( ok )
|
||
|
setValue ( dd );
|
||
|
else
|
||
|
{
|
||
|
int pos = t.find( '/' );
|
||
|
int year = t.mid( 0, pos ).toInt();
|
||
|
int pos1 = t.find( '/', pos + 1 );
|
||
|
int month = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
|
||
|
int day = t.right( t.length() - pos1 - 1 ).toInt();
|
||
|
QDate date( year, month, day );
|
||
|
if ( date.isValid() )
|
||
|
setValue( date );
|
||
|
else
|
||
|
clear = false;
|
||
|
}
|
||
|
}
|
||
|
else if( dataType == "Time" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
double dd = t.toDouble( &ok );
|
||
|
if ( ok )
|
||
|
setCellValue( dd );
|
||
|
else
|
||
|
{
|
||
|
int hours = -1;
|
||
|
int minutes = -1;
|
||
|
int second = -1;
|
||
|
int pos, pos1;
|
||
|
pos = t.find( ':' );
|
||
|
hours = t.mid( 0, pos ).toInt();
|
||
|
pos1 = t.find( ':', pos + 1 );
|
||
|
minutes = t.mid( pos + 1, ( ( pos1 - 1 ) - pos ) ).toInt();
|
||
|
second = t.right( t.length() - pos1 - 1 ).toInt();
|
||
|
QTime time( hours, minutes, second );
|
||
|
if ( time.isValid() )
|
||
|
setValue( time );
|
||
|
else
|
||
|
clear = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
setValue( t );
|
||
|
}
|
||
|
|
||
|
// if ( clear )
|
||
|
// clearFlag( Flag_CalcDirty );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool Cell::loadCellData(const QDomElement & text, Paste::Operation op )
|
||
|
{
|
||
|
//TODO: use converter()->asString() to generate strText
|
||
|
|
||
|
QString t = text.text();
|
||
|
t = t.stripWhiteSpace();
|
||
|
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
setFlag(Flag_TextFormatDirty);
|
||
|
|
||
|
// A formula like =A1+A2 ?
|
||
|
if( t[0] == '=' )
|
||
|
{
|
||
|
t = decodeFormula( t, d->column, d->row );
|
||
|
setCellText (pasteOperation( t, d->strText, op ));
|
||
|
|
||
|
setFlag(Flag_CalcDirty);
|
||
|
clearAllErrors();
|
||
|
|
||
|
if ( !makeFormula() )
|
||
|
kdError(36001) << "ERROR: Syntax ERROR" << endl;
|
||
|
}
|
||
|
// rich text ?
|
||
|
else if (t[0] == '!' )
|
||
|
{
|
||
|
// KSpread pre 1.4 stores hyperlink as rich text (first char is '!')
|
||
|
// extract the link and the correspoding text
|
||
|
// This is a rather dirty hack, but enough for KSpread generated XML
|
||
|
bool inside_tag = false;
|
||
|
QString qml_text;
|
||
|
QString tag;
|
||
|
QString qml_link;
|
||
|
|
||
|
for( unsigned i = 1; i < t.length(); i++ )
|
||
|
{
|
||
|
QChar ch = t[i];
|
||
|
if( ch == '<' )
|
||
|
{
|
||
|
if( !inside_tag )
|
||
|
{
|
||
|
inside_tag = true;
|
||
|
tag = QString::null;
|
||
|
}
|
||
|
}
|
||
|
else if( ch == '>' )
|
||
|
{
|
||
|
if( inside_tag )
|
||
|
{
|
||
|
inside_tag = false;
|
||
|
if( tag.startsWith( "a href=\"", true ) )
|
||
|
if( tag.endsWith( "\"" ) )
|
||
|
qml_link = tag.mid( 8, tag.length()-9 );
|
||
|
tag = QString::null;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( !inside_tag )
|
||
|
qml_text += ch;
|
||
|
else
|
||
|
tag += ch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( !qml_link.isEmpty() )
|
||
|
d->extra()->link = qml_link;
|
||
|
d->strText = qml_text;
|
||
|
setValue( d->strText );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool newStyleLoading = true;
|
||
|
QString dataType;
|
||
|
|
||
|
if ( text.hasAttribute( "dataType" ) ) // new docs
|
||
|
{
|
||
|
dataType = text.attribute( "dataType" );
|
||
|
}
|
||
|
else // old docs: do the ugly solution of calling checkTextInput to parse the text
|
||
|
{
|
||
|
// ...except for date/time
|
||
|
if (isDate() && ( t.contains('/') == 2 ))
|
||
|
dataType = "Date";
|
||
|
else if (isTime() && ( t.contains(':') == 2 ) )
|
||
|
dataType = "Time";
|
||
|
else
|
||
|
{
|
||
|
d->strText = pasteOperation( t, d->strText, op );
|
||
|
checkTextInput();
|
||
|
//kdDebug(36001) << "Cell::load called checkTextInput, got dataType=" << dataType << " t=" << t << endl;
|
||
|
newStyleLoading = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( newStyleLoading )
|
||
|
{
|
||
|
d->value = Value::empty();
|
||
|
clearAllErrors();
|
||
|
|
||
|
// boolean ?
|
||
|
if( dataType == "Bool" )
|
||
|
{
|
||
|
bool val = (t.lower() == "true");
|
||
|
setCellValue (val);
|
||
|
}
|
||
|
|
||
|
// number ?
|
||
|
else if( dataType == "Num" )
|
||
|
{
|
||
|
bool ok = false;
|
||
|
if (t.contains('.'))
|
||
|
setValue ( Value( t.toDouble(&ok) ) ); // We save in non-localized format
|
||
|
else
|
||
|
setValue ( Value( t.toLong(&ok) ) );
|
||
|
if ( !ok )
|
||
|
{
|
||
|
kdWarning(36001) << "Couldn't parse '" << t << "' as number." << endl;
|
||
|
}
|
||
|
/* We will need to localize the text version of the number */
|
||
|
KLocale* locale = format()->sheet()->doc()->locale();
|
||
|
|
||
|
/* KLocale::formatNumber requires the precision we want to return.
|
||
|
*/
|
||
|
int precision = t.length() - t.find('.') - 1;
|
||
|
|
||
|
if ( formatType() == Percentage_format )
|
||
|
{
|
||
|
if (value().isInteger())
|
||
|
t = locale->formatNumber( value().asInteger() * 100 );
|
||
|
else
|
||
|
t = locale->formatNumber( value().asFloat() * 100.0, precision );
|
||
|
d->strText = pasteOperation( t, d->strText, op );
|
||
|
d->strText += '%';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (value().isInteger())
|
||
|
t = locale->formatLong(value().asInteger());
|
||
|
else
|
||
|
t = locale->formatNumber(value().asFloat(), precision);
|
||
|
d->strText = pasteOperation( t, d->strText, op );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// date ?
|
||
|
else if( dataType == "Date" )
|
||
|
{
|
||
|
int pos = t.find('/');
|
||
|
int year = t.mid(0,pos).toInt();
|
||
|
int pos1 = t.find('/',pos+1);
|
||
|
int month = t.mid(pos+1,((pos1-1)-pos)).toInt();
|
||
|
int day = t.right(t.length()-pos1-1).toInt();
|
||
|
setValue( QDate(year,month,day) );
|
||
|
if ( value().asDate().isValid() ) // Should always be the case for new docs
|
||
|
d->strText = locale()->formatDate( value().asDate(), true );
|
||
|
else // This happens with old docs, when format is set wrongly to date
|
||
|
{
|
||
|
d->strText = pasteOperation( t, d->strText, op );
|
||
|
checkTextInput();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// time ?
|
||
|
else if( dataType == "Time" )
|
||
|
{
|
||
|
int hours = -1;
|
||
|
int minutes = -1;
|
||
|
int second = -1;
|
||
|
int pos, pos1;
|
||
|
pos = t.find(':');
|
||
|
hours = t.mid(0,pos).toInt();
|
||
|
pos1 = t.find(':',pos+1);
|
||
|
minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
|
||
|
second = t.right(t.length()-pos1-1).toInt();
|
||
|
setValue( QTime(hours,minutes,second) );
|
||
|
if ( value().asTime().isValid() ) // Should always be the case for new docs
|
||
|
d->strText = locale()->formatTime( value().asTime(), true );
|
||
|
else // This happens with old docs, when format is set wrongly to time
|
||
|
{
|
||
|
d->strText = pasteOperation( t, d->strText, op );
|
||
|
checkTextInput();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else
|
||
|
{
|
||
|
// Set the cell's text
|
||
|
d->strText = pasteOperation( t, d->strText, op );
|
||
|
setValue( d->strText );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( text.hasAttribute( "outStr" ) ) // very new docs
|
||
|
{
|
||
|
d->strOutText = text.attribute( "outStr" );
|
||
|
if ( !d->strOutText.isEmpty() )
|
||
|
clearFlag( Flag_TextFormatDirty );
|
||
|
}
|
||
|
|
||
|
if ( !format()->sheet()->isLoading() )
|
||
|
setCellText( d->strText );
|
||
|
|
||
|
if ( d->hasExtra() && d->extra()->conditions )
|
||
|
d->extra()->conditions->checkMatches();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QTime Cell::toTime(const QDomElement &element)
|
||
|
{
|
||
|
//TODO: can't we use tryParseTime (after modification) instead?
|
||
|
QString t = element.text();
|
||
|
t = t.stripWhiteSpace();
|
||
|
int hours = -1;
|
||
|
int minutes = -1;
|
||
|
int second = -1;
|
||
|
int pos, pos1;
|
||
|
pos = t.find(':');
|
||
|
hours = t.mid(0,pos).toInt();
|
||
|
pos1 = t.find(':',pos+1);
|
||
|
minutes = t.mid(pos+1,((pos1-1)-pos)).toInt();
|
||
|
second = t.right(t.length()-pos1-1).toInt();
|
||
|
setValue( Value( QTime(hours,minutes,second)) );
|
||
|
return value().asTime();
|
||
|
}
|
||
|
|
||
|
QDate Cell::toDate(const QDomElement &element)
|
||
|
{
|
||
|
QString t = element.text();
|
||
|
int pos;
|
||
|
int pos1;
|
||
|
int year = -1;
|
||
|
int month = -1;
|
||
|
int day = -1;
|
||
|
pos = t.find('/');
|
||
|
year = t.mid(0,pos).toInt();
|
||
|
pos1 = t.find('/',pos+1);
|
||
|
month = t.mid(pos+1,((pos1-1)-pos)).toInt();
|
||
|
day = t.right(t.length()-pos1-1).toInt();
|
||
|
setValue( Value( QDate(year,month,day) ) );
|
||
|
return value().asDate();
|
||
|
}
|
||
|
|
||
|
QString Cell::pasteOperation( const QString &new_text, const QString &old_text, Paste::Operation op )
|
||
|
{
|
||
|
if ( op == Paste::OverWrite )
|
||
|
return new_text;
|
||
|
|
||
|
QString tmp_op;
|
||
|
QString tmp;
|
||
|
QString old;
|
||
|
|
||
|
if( !new_text.isEmpty() && new_text[0] == '=' )
|
||
|
{
|
||
|
tmp = new_text.right( new_text.length() - 1 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tmp = new_text;
|
||
|
}
|
||
|
|
||
|
if ( old_text.isEmpty() &&
|
||
|
( op == Paste::Add || op == Paste::Mul || op == Paste::Sub || op == Paste::Div ) )
|
||
|
{
|
||
|
old = "=0";
|
||
|
}
|
||
|
|
||
|
if( !old_text.isEmpty() && old_text[0] == '=' )
|
||
|
{
|
||
|
old = old_text.right( old_text.length() - 1 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
old = old_text;
|
||
|
}
|
||
|
|
||
|
bool b1, b2;
|
||
|
tmp.toDouble( &b1 );
|
||
|
old.toDouble( &b2 );
|
||
|
if (b1 && !b2 && old.length() == 0)
|
||
|
{
|
||
|
old = "0";
|
||
|
b2 = true;
|
||
|
}
|
||
|
|
||
|
if( b1 && b2 )
|
||
|
{
|
||
|
switch( op )
|
||
|
{
|
||
|
case Paste::Add:
|
||
|
tmp_op = QString::number(old.toDouble()+tmp.toDouble());
|
||
|
break;
|
||
|
case Paste::Mul :
|
||
|
tmp_op = QString::number(old.toDouble()*tmp.toDouble());
|
||
|
break;
|
||
|
case Paste::Sub:
|
||
|
tmp_op = QString::number(old.toDouble()-tmp.toDouble());
|
||
|
break;
|
||
|
case Paste::Div:
|
||
|
tmp_op = QString::number(old.toDouble()/tmp.toDouble());
|
||
|
break;
|
||
|
default:
|
||
|
Q_ASSERT( 0 );
|
||
|
}
|
||
|
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
clearAllErrors();
|
||
|
|
||
|
return tmp_op;
|
||
|
}
|
||
|
else if ( ( new_text[0] == '=' && old_text[0] == '=' ) ||
|
||
|
( b1 && old_text[0] == '=' ) || ( new_text[0] == '=' && b2 ) )
|
||
|
{
|
||
|
switch( op )
|
||
|
{
|
||
|
case Paste::Add :
|
||
|
tmp_op="=("+old+")+"+"("+tmp+")";
|
||
|
break;
|
||
|
case Paste::Mul :
|
||
|
tmp_op="=("+old+")*"+"("+tmp+")";
|
||
|
break;
|
||
|
case Paste::Sub:
|
||
|
tmp_op="=("+old+")-"+"("+tmp+")";
|
||
|
break;
|
||
|
case Paste::Div:
|
||
|
tmp_op="=("+old+")/"+"("+tmp+")";
|
||
|
break;
|
||
|
default :
|
||
|
Q_ASSERT( 0 );
|
||
|
}
|
||
|
|
||
|
tmp_op = decodeFormula( tmp_op, d->column, d->row );
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
clearAllErrors();
|
||
|
|
||
|
return tmp_op;
|
||
|
}
|
||
|
|
||
|
tmp = decodeFormula( new_text, d->column, d->row );
|
||
|
setFlag(Flag_LayoutDirty);
|
||
|
clearAllErrors();
|
||
|
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
QString Cell::testAnchor( int x, int y ) const
|
||
|
{
|
||
|
if( link().isEmpty() )
|
||
|
return QString::null;
|
||
|
|
||
|
const Doc* doc = format()->sheet()->doc();
|
||
|
int x1 = doc->zoomItX( d->textX );
|
||
|
int y1 = doc->zoomItX( d->textY - d->textHeight );
|
||
|
int x2 = doc->zoomItX( d->textX + d->textWidth );
|
||
|
int y2 = doc->zoomItX( d->textY );
|
||
|
|
||
|
if( x > x1 ) if( x < x2 )
|
||
|
if( y > y1 ) if( y < y2 )
|
||
|
return link();
|
||
|
|
||
|
return QString::null;
|
||
|
}
|
||
|
|
||
|
void Cell::sheetDies()
|
||
|
{
|
||
|
// Avoid unobscuring the cells in the destructor.
|
||
|
if (d->hasExtra())
|
||
|
{
|
||
|
d->extra()->extraXCells = 0;
|
||
|
d->extra()->extraYCells = 0;
|
||
|
d->extra()->mergedXCells = 0;
|
||
|
d->extra()->mergedYCells = 0;
|
||
|
}
|
||
|
|
||
|
//d->nextCell = 0;
|
||
|
//d->previousCell = 0;
|
||
|
}
|
||
|
|
||
|
Cell::~Cell()
|
||
|
{
|
||
|
if ( d->nextCell )
|
||
|
d->nextCell->setPreviousCell( d->previousCell );
|
||
|
if ( d->previousCell )
|
||
|
d->previousCell->setNextCell( d->nextCell );
|
||
|
|
||
|
if (d->hasExtra())
|
||
|
{
|
||
|
delete d->extra()->validity;
|
||
|
}
|
||
|
|
||
|
// Unobscure cells.
|
||
|
int extraXCells = d->hasExtra() ? d->extra()->extraXCells : 0;
|
||
|
int extraYCells = d->hasExtra() ? d->extra()->extraYCells : 0;
|
||
|
for( int x = 0; x <= extraXCells; ++x )
|
||
|
for( int y = (x == 0) ? 1 : 0; // avoid looking at (+0,+0)
|
||
|
y <= extraYCells; ++y )
|
||
|
{
|
||
|
Cell* cell = format()->sheet()->cellAt( d->column + x, d->row + y );
|
||
|
if ( cell )
|
||
|
cell->unobscure(this);
|
||
|
}
|
||
|
|
||
|
d->value = Value::empty();
|
||
|
|
||
|
if (!isDefault())
|
||
|
valueChanged (); //our value has been changed (is now null), but only if we aren't default
|
||
|
|
||
|
delete d->format;
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
bool Cell::operator > ( const Cell & cell ) const
|
||
|
{
|
||
|
if ( value().isNumber() ) // ### what about bools ?
|
||
|
{
|
||
|
if ( cell.value().isNumber() )
|
||
|
return value().asFloat() > cell.value().asFloat();
|
||
|
else
|
||
|
return false; // numbers are always < than texts
|
||
|
}
|
||
|
else if(isDate())
|
||
|
{
|
||
|
if( cell.isDate() )
|
||
|
return value().asDate() > cell.value().asDate();
|
||
|
else if (cell.value().isNumber())
|
||
|
return true;
|
||
|
else
|
||
|
return false; //date are always < than texts and time
|
||
|
}
|
||
|
else if(isTime())
|
||
|
{
|
||
|
if( cell.isTime() )
|
||
|
return value().asTime() > cell.value().asTime();
|
||
|
else if( cell.isDate())
|
||
|
return true; //time are always > than date
|
||
|
else if( cell.value().isNumber())
|
||
|
return true;
|
||
|
else
|
||
|
return false; //time are always < than texts
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( Map::respectCase )
|
||
|
return value().asString().compare(cell.value().asString()) > 0;
|
||
|
else
|
||
|
return ( value().asString() ).lower().compare(cell.value().asString().lower()) > 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Cell::operator < ( const Cell & cell ) const
|
||
|
{
|
||
|
if ( value().isNumber() )
|
||
|
{
|
||
|
if ( cell.value().isNumber() )
|
||
|
return value().asFloat() < cell.value().asFloat();
|
||
|
else
|
||
|
return true; // numbers are always < than texts
|
||
|
}
|
||
|
else if(isDate())
|
||
|
{
|
||
|
if( cell.isDate() )
|
||
|
return value().asDateTime().date() < cell.value().asDateTime().date();
|
||
|
else if( cell.value().isNumber())
|
||
|
return false;
|
||
|
else
|
||
|
return true; //date are always < than texts and time
|
||
|
}
|
||
|
else if(isTime())
|
||
|
{
|
||
|
if( cell.isTime() )
|
||
|
return value().asDateTime().time() < cell.value().asDateTime().time();
|
||
|
else if(cell.isDate())
|
||
|
return false; //time are always > than date
|
||
|
else if( cell.value().isNumber())
|
||
|
return false;
|
||
|
else
|
||
|
return true; //time are always < than texts
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( Map::respectCase )
|
||
|
return value().asString().compare(cell.value().asString()) < 0;
|
||
|
else
|
||
|
return ( value().asString() ).lower().compare(cell.value().asString().lower()) < 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Cell::operator==( const Cell& other ) const
|
||
|
{
|
||
|
if ( d->strText != other.d->strText )
|
||
|
return false;
|
||
|
if ( d->value != other.d->value )
|
||
|
return false;
|
||
|
if ( *d->format != *other.d->format )
|
||
|
return false;
|
||
|
if ( d->hasExtra() )
|
||
|
{
|
||
|
if ( !other.d->hasExtra() )
|
||
|
return false;
|
||
|
if ( d->extra()->link != other.d->extra()->link )
|
||
|
return false;
|
||
|
if ( d->extra()->mergedXCells != other.d->extra()->mergedXCells )
|
||
|
return false;
|
||
|
if ( d->extra()->mergedYCells != other.d->extra()->mergedYCells )
|
||
|
return false;
|
||
|
if ( d->extra()->conditions )
|
||
|
{
|
||
|
if ( !other.d->extra()->conditions )
|
||
|
return false;
|
||
|
if ( *d->extra()->conditions != *other.d->extra()->conditions )
|
||
|
return false;
|
||
|
}
|
||
|
if ( d->extra()->validity )
|
||
|
{
|
||
|
if ( !other.d->extra()->validity )
|
||
|
return false;
|
||
|
if ( *d->extra()->validity != *other.d->extra()->validity )
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QRect Cell::cellRect()
|
||
|
{
|
||
|
Q_ASSERT(!isDefault());
|
||
|
return QRect(QPoint(d->column, d->row), QPoint(d->column, d->row));
|
||
|
}
|
||
|
|
||
|
QValueList<Conditional> Cell::conditionList() const
|
||
|
{
|
||
|
if ( !d->hasExtra() || !d->extra()->conditions )
|
||
|
{
|
||
|
QValueList<Conditional> emptyList;
|
||
|
return emptyList;
|
||
|
}
|
||
|
|
||
|
return d->extra()->conditions->conditionList();
|
||
|
}
|
||
|
|
||
|
void Cell::setConditionList( const QValueList<Conditional> & newList )
|
||
|
{
|
||
|
if (d->hasExtra())
|
||
|
delete d->extra()->conditions;
|
||
|
d->extra()->conditions = new Conditions( this );
|
||
|
d->extra()->conditions->setConditionList( newList );
|
||
|
d->extra()->conditions->checkMatches();
|
||
|
}
|
||
|
|
||
|
bool Cell::hasError() const
|
||
|
{
|
||
|
return ( testFlag(Flag_ParseError) ||
|
||
|
testFlag(Flag_CircularCalculation) ||
|
||
|
testFlag(Flag_DependancyError));
|
||
|
}
|
||
|
|
||
|
void Cell::clearAllErrors()
|
||
|
{
|
||
|
clearFlag( Flag_ParseError );
|
||
|
clearFlag( Flag_CircularCalculation );
|
||
|
clearFlag( Flag_DependancyError );
|
||
|
}
|
||
|
|
||
|
bool Cell::calcDirtyFlag()
|
||
|
{
|
||
|
return isFormula() ? testFlag( Flag_CalcDirty ) : false;
|
||
|
}
|
||
|
|
||
|
bool Cell::layoutDirtyFlag() const
|
||
|
{
|
||
|
return testFlag( Flag_LayoutDirty );
|
||
|
}
|
||
|
|
||
|
void Cell::clearDisplayDirtyFlag()
|
||
|
{
|
||
|
clearFlag( Flag_DisplayDirty );
|
||
|
}
|
||
|
|
||
|
void Cell::setDisplayDirtyFlag()
|
||
|
{
|
||
|
setFlag( Flag_DisplayDirty );
|
||
|
}
|
||
|
|
||
|
bool Cell::doesMergeCells() const
|
||
|
{
|
||
|
return testFlag( Flag_Merged );
|
||
|
}
|
||
|
|
||
|
void Cell::clearFlag( CellFlags flag )
|
||
|
{
|
||
|
d->flags &= ~(Q_UINT32)flag;
|
||
|
}
|
||
|
|
||
|
void Cell::setFlag( CellFlags flag )
|
||
|
{
|
||
|
d->flags |= (Q_UINT32)flag;
|
||
|
}
|
||
|
|
||
|
bool Cell::testFlag( CellFlags flag ) const
|
||
|
{
|
||
|
return ( d->flags & (Q_UINT32)flag );
|
||
|
}
|
||
|
|
||
|
|
||
|
void Cell::checkForNamedAreas( QString & formula ) const
|
||
|
{
|
||
|
KSPLoadingInfo* loadinginfo = sheet()->doc()->loadingInfo();
|
||
|
if(! loadinginfo) {
|
||
|
kdDebug() << "Cell::checkForNamedAreas loadinginfo is NULL" << endl;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int l = formula.length();
|
||
|
int i = 0;
|
||
|
QString word;
|
||
|
int start = 0;
|
||
|
while ( i < l )
|
||
|
{
|
||
|
if ( formula[i].isLetterOrNumber() )
|
||
|
{
|
||
|
word += formula[i];
|
||
|
++i;
|
||
|
continue;
|
||
|
}
|
||
|
if ( !word.isEmpty() )
|
||
|
{
|
||
|
if ( loadinginfo->findWordInAreaList(word) )
|
||
|
{
|
||
|
formula = formula.replace( start, word.length(), "'" + word + "'" );
|
||
|
l = formula.length();
|
||
|
++i;
|
||
|
kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++i;
|
||
|
word = "";
|
||
|
start = i;
|
||
|
}
|
||
|
if ( !word.isEmpty() )
|
||
|
{
|
||
|
if ( loadinginfo->findWordInAreaList(word) )
|
||
|
{
|
||
|
formula = formula.replace( start, word.length(), "'" + word + "'" );
|
||
|
l = formula.length();
|
||
|
++i;
|
||
|
kdDebug() << "Formula: " << formula << ", L: " << l << ", i: " << i + 1 <<endl;
|
||
|
}
|
||
|
}
|
||
|
}
|