/* This file is part of the KDE project
   Copyright (C) 2005-2006 Inge Wallin <inge@lysator.liu.se>
             (C) 2004 Ariya Hidayat <ariya@kde.org>
             (C) 2002-2003 Norbert Andres <nandres@web.de>
             (C) 2000-2002 Laurent Montel <montel@kde.org>
             (C) 2002 John Dailey <dailey@vt.edu>
             (C) 2002 Phillip Mueller <philipp.mueller@gmx.de>
             (C) 2000 Werner Trobin <trobin@kde.org>
             (C) 1999-2000 Simon Hausmann <hausmann@kde.org>
             (C) 1999 David Faure <faure@kde.org>
             (C) 1998-2000 Torben Weis <weis@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 <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>

#include <qapplication.h>
#include <qfileinfo.h>
#include <qfont.h>
#include <qpair.h>

#include <kstandarddirs.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <ksconfig.h>
#include <ktempfile.h>

#include <KoApplication.h>
#include <KoCommandHistory.h>
#include <KoDocumentInfo.h>
#include <KoDom.h>
#include <KoMainWindow.h>
#include <KoOasisSettings.h>
#include <KoOasisStyles.h>
#include <KoStoreDevice.h>
#include <KoTemplateChooseDia.h>
#include <KoVariable.h>
#include <KoXmlNS.h>
#include <KoXmlWriter.h>

#include "kspread_canvas.h"
#include "kspread_locale.h"
#include "kspread_map.h"
#include "kspread_sheet.h"
#include "kspread_sheetprint.h"
#include "kspread_style_manager.h"
#include "kspread_undo.h"
#include "kspread_util.h"
#include "kspread_view.h"

#include "commands.h"
#include "damages.h"
#include "formula.h"
#include "functions.h"
#include "ksploadinginfo.h"
#include "selection.h"
#include "valuecalc.h"
#include "valueconverter.h"
#include "valueformatter.h"
#include "valueparser.h"

#include "KSpreadDocIface.h"

#include "kspread_doc.h"

using namespace std;
using namespace KSpread;

static const int CURRENT_SYNTAX_VERSION = 1;
// Make sure an appropriate DTD is available in www/koffice/DTD if changing this value
static const char * CURRENT_DTD_VERSION = "1.2";

class Doc::Private
{
public:

  Map *map;
  KLocale *locale;
  StyleManager *styleManager;
  ValueParser *parser;
  ValueFormatter *formatter;
  ValueConverter *converter;
  ValueCalc *calc;

  Sheet *activeSheet;
    KSPLoadingInfo *m_loadingInfo;
  static QValueList<Doc*> s_docs;
  static int s_docId;

  DCOPObject* dcop;

  // URL of the this part. This variable is only set if the load() function
  // had been called with an URL as argument.
  QString fileURL;

  // for undo/redo
  int undoLocked;
  KoCommandHistory* commandHistory;

  // true if loading is in process, otherwise false.
  // This flag is used to avoid updates etc. during loading.
  bool isLoading;

  QColor pageBorderColor;

  QPtrList<Plugin> plugins;

  QValueList<Reference> refs;
  KCompletion listCompletion;

  int numOperations;

  QValueList<Damage*> damages;

  // document properties
  int syntaxVersion;
  bool verticalScrollBar:1;
  bool horizontalScrollBar:1;
  bool columnHeader:1;
  bool rowHeader:1;
  QColor gridColor;
  double indentValue;
  bool showStatusBar:1;
  bool showTabBar:1;
  bool showFormulaBar:1;
  bool showError:1;
  KGlobalSettings::Completion completionMode;
  KSpread::MoveTo moveTo;
  MethodOfCalc calcMethod;
  bool delayCalculation:1;
  KSpellConfig *spellConfig;
  bool dontCheckUpperWord:1;
  bool dontCheckTitleCase:1;
    bool configLoadFromFile:1;
  QStringList spellListIgnoreAll;
  /// list of all objects
  QPtrList<EmbeddedObject> m_embeddedObjects;
  KoPictureCollection m_pictureCollection;
  QValueList<KoPictureKey> usedPictures;
  bool m_savingWholeDocument;
};

/*****************************************************************************
 *
 * Doc
 *
 *****************************************************************************/

QValueList<Doc*> Doc::Private::s_docs;
int Doc::Private::s_docId = 0;

Doc::Doc( QWidget *parentWidget, const char *widgetName, QObject* parent, const char* name, bool singleViewMode )
  : KoDocument( parentWidget, widgetName, parent, name, singleViewMode )
{
  d = new Private;
  d->m_loadingInfo = 0L;

  d->map = new Map( this, "Map" );
  d->locale = new Locale;
  d->styleManager = new StyleManager();

  d->parser = new ValueParser( d->locale );
  d->converter = new ValueConverter ( d->parser );
  d->calc = new ValueCalc( d->converter );
  d->calc->setDoc (this);
  d->formatter = new ValueFormatter( d->converter );

  d->activeSheet = 0;

  d->pageBorderColor = Qt::red;
  d->configLoadFromFile = false;

  QFont f( KoGlobal::defaultFont() );
  Format::setGlobalRowHeight( f.pointSizeFloat() + 3 );
  Format::setGlobalColWidth( ( f.pointSizeFloat() + 3 ) * 5 );

  d->plugins.setAutoDelete( false );

  d->delayCalculation = false;

  documents().append( this );

  setInstance( Factory::global(), false );
  setTemplateType( "kspread_template" );

  d->dcop = 0;
  d->isLoading = false;
  d->numOperations = 1; // don't start repainting before the GUI is done...

  d->undoLocked = 0;
  d->commandHistory = new KoCommandHistory( actionCollection() );
  connect( d->commandHistory, SIGNAL( commandExecuted() ), SLOT( commandExecuted() ) );
  connect( d->commandHistory, SIGNAL( documentRestored() ), SLOT( documentRestored() ) );


  // Make us scripsheet if the document has a name
  // Set a name if there is no name specified
  if ( !name )
  {
      QString tmp( "Document%1" );
      tmp = tmp.arg( d->s_docId++ );
      setName( tmp.local8Bit());//tmp.latin1() );
      dcopObject();
  }
  else
      dcopObject();

  // default document properties
  d->syntaxVersion = CURRENT_SYNTAX_VERSION;
  d->verticalScrollBar = true;
  d->horizontalScrollBar = true;
  d->columnHeader = true;
  d->rowHeader = true;
  d->gridColor = Qt::lightGray;
  d->indentValue = 10.0;
  d->showStatusBar = true;
  d->showFormulaBar = true;
  d->showTabBar = true;
  d->showError = false;
  d->calcMethod = SumOfNumber;
  d->moveTo = Bottom;
  d->completionMode = KGlobalSettings::CompletionAuto;
  d->spellConfig = 0;
  d->dontCheckUpperWord = false;
  d->dontCheckTitleCase = false;
}

Doc::~Doc()
{
  //don't save config when kword is embedded into konqueror
  if(isReadWrite())
    saveConfig();

  delete d->dcop;
  d->s_docs.remove( this );

  kdDebug(36001) << "alive 1" << endl;

  delete d->commandHistory;

  delete d->spellConfig;

  delete d->locale;
  delete d->map;
  delete d->styleManager;
  delete d->parser;
  delete d->formatter;
  delete d->converter;
  delete d->calc;

  delete d;
}

QValueList<Doc*> Doc::documents()
{
  return Private::s_docs;
}

bool Doc::initDoc(InitDocFlags flags, QWidget* parentWidget)
{
  //  ElapsedTime et( "      initDoc        " );

    QString f;

    if (flags==KoDocument::InitDocEmpty)
    {
        KConfig *config = Factory::global()->config();
        int _page=1;
        if( config->hasGroup("Parameters" ))
        {
                config->setGroup( "Parameters" );
                _page=config->readNumEntry( "NbPage",1 ) ;
        }

        for( int i=0; i<_page; i++ )
          map()->addNewSheet();

        resetURL();
        setEmpty();
        initConfig();
        styleManager()->createBuiltinStyles();

        return true;
    }

    KoTemplateChooseDia::ReturnType ret;
    KoTemplateChooseDia::DialogType dlgtype;
    if (flags != KoDocument::InitDocFileNew )
            dlgtype = KoTemplateChooseDia::Everything;
    else
            dlgtype = KoTemplateChooseDia::OnlyTemplates;

    ret = KoTemplateChooseDia::choose( Factory::global(), f,
                                       dlgtype, "kspread_template", parentWidget );

    if ( ret == KoTemplateChooseDia::File )
    {
  KURL url( f );

  bool ok=openURL(url);

  while (KoDocument::isLoading())
    kapp->processEvents();

  return ok;


    }

    if ( ret == KoTemplateChooseDia::Empty )
    {
  KConfig *config = Factory::global()->config();
  int _page=1;
  if( config->hasGroup("Parameters" ))
  {
    config->setGroup( "Parameters" );
    _page=config->readNumEntry( "NbPage",1 ) ;
  }

  for( int i=0; i<_page; i++ )
    map()->addNewSheet ();

  resetURL();
  setEmpty();
  initConfig();
        styleManager()->createBuiltinStyles();
  return true;
    }

    if ( ret == KoTemplateChooseDia::Template )
    {
        resetURL();
        d->m_loadingInfo = new KSPLoadingInfo;
        d->m_loadingInfo->setLoadTemplate( true );
        bool ok = loadNativeFormat( f );
        if ( !ok )
        {
            showLoadingErrorDialog();
            deleteLoadingInfo();
        }
        setEmpty();
        initConfig();
        return ok;
    }

    return false;
}

void Doc::openTemplate (const QString& file)
{
    d->m_loadingInfo = new KSPLoadingInfo;
    d->m_loadingInfo->setLoadTemplate( true );
    KoDocument::openTemplate( file );
    deleteLoadingInfo();
    initConfig();
}

void Doc::initEmpty()
{
    KConfig *config = Factory::global()->config();
    int _page=1;
    if( config->hasGroup("Parameters" ))
    {
        config->setGroup( "Parameters" );
        _page=config->readNumEntry( "NbPage",1 ) ;
    }

    for( int i=0; i<_page; i++ )
        map()->addNewSheet();

    resetURL();
    initConfig();
    styleManager()->createBuiltinStyles();

    KoDocument::initEmpty();
}

KLocale *Doc::locale () const
{
  return d->locale;
}

Map *Doc::map () const
{
  return d->map;
}

StyleManager *Doc::styleManager () const
{
  return d->styleManager;
}

ValueParser *Doc::parser () const
{
  return d->parser;
}

ValueFormatter *Doc::formatter () const
{
  return d->formatter;
}

ValueConverter *Doc::converter () const
{
  return d->converter;
}

ValueCalc *Doc::calc () const
{
  return d->calc;
}

void Doc::saveConfig()
{
    if ( isEmbedded() ||!isReadWrite())
        return;
    KConfig *config = Factory::global()->config();
    config->setGroup( "Parameters" );
    config->writeEntry( "Zoom", m_zoom );

}

void Doc::initConfig()
{
    KConfig *config = Factory::global()->config();

    if( config->hasGroup("KSpread Page Layout" ))
    {
      config->setGroup( "KSpread Page Layout" );
      setUnit( (KoUnit::Unit)config->readNumEntry( "Default unit page" ,0));
    }
    if( config->hasGroup("Parameters" ))
    {
        config->setGroup( "Parameters" );
        m_zoom = config->readNumEntry( "Zoom", 100 );
    }
    else
      m_zoom = 100;

    int undo=30;
    if(config->hasGroup("Misc" ) )
    {
        config->setGroup( "Misc" );
        undo=config->readNumEntry("UndoRedo",-1);
    }
    if(undo!=-1)
        setUndoRedoLimit(undo);

    setZoomAndResolution( m_zoom, KoGlobal::dpiX(), KoGlobal::dpiY() );
}

int Doc::syntaxVersion() const
{
  return d->syntaxVersion;
}

bool Doc::isLoading() const
{
  return d->isLoading || KoDocument::isLoading();
}

void Doc::doNotPaint(bool status)
{
  d->isLoading = status;
}


QColor Doc::pageBorderColor() const
{
  return d->pageBorderColor;
}

void Doc::changePageBorderColor( const QColor  & _color)
{
  d->pageBorderColor = _color;
}

const QValueList<Reference>  &Doc::listArea()
{
  return d->refs;
}

KCompletion& Doc::completion()
{
  return d->listCompletion;
}

KoView* Doc::createViewInstance( QWidget* parent, const char* name )
{
    if ( name == 0 )
        name = "View";
    return new View( parent, name, this );
}

bool Doc::saveChildren( KoStore* _store )
{
  return map()->saveChildren( _store );
}

int Doc::supportedSpecialFormats() const
{
    return KoDocument::supportedSpecialFormats();
}

bool Doc::completeSaving( KoStore* _store )
{
    d->m_pictureCollection.saveToStore( KoPictureCollection::CollectionPicture, _store, d->usedPictures );

    return true;
}


QDomDocument Doc::saveXML()
{
    //Terminate current cell edition, if any
    QPtrListIterator<KoView> it( views() );

    /* don't pull focus away from the editor if this is just a background
       autosave */
    if (!isAutosaving())
    {
        for (; it.current(); ++it )
            static_cast<View *>( it.current() )->deleteEditor( true );
    }

    QDomDocument doc = createDomDocument( "spreadsheet", CURRENT_DTD_VERSION );
    QDomElement spread = doc.documentElement();
    spread.setAttribute( "editor", "KSpread" );
    spread.setAttribute( "mime", "application/x-kspread" );
    spread.setAttribute( "syntaxVersion", CURRENT_SYNTAX_VERSION );

    QDomElement dlocale = ((Locale *)locale())->save( doc );
    spread.appendChild( dlocale );

    if (d->refs.count() != 0 )
    {
        QDomElement areaname = saveAreaName( doc );
        spread.appendChild( areaname );
    }

    if( !d->spellListIgnoreAll.isEmpty() )
    {
        QDomElement spellCheckIgnore = doc.createElement( "SPELLCHECKIGNORELIST" );
        spread.appendChild( spellCheckIgnore );
        for ( QStringList::Iterator it = d->spellListIgnoreAll.begin(); it != d->spellListIgnoreAll.end(); ++it )
        {
            QDomElement spellElem = doc.createElement( "SPELLCHECKIGNOREWORD" );
            spellCheckIgnore.appendChild( spellElem );
            spellElem.setAttribute( "word", *it );
        }
    }

    SavedDocParts::const_iterator iter = m_savedDocParts.begin();
    SavedDocParts::const_iterator end  = m_savedDocParts.end();
    while ( iter != end )
    {
      // save data we loaded in the beginning and which has no owner back to file
      spread.appendChild( iter.data() );
      ++iter;
    }

    QDomElement defaults = doc.createElement( "defaults" );
    defaults.setAttribute( "row-height", Format::globalRowHeight() );
    defaults.setAttribute( "col-width", Format::globalColWidth() );
    spread.appendChild( defaults );

    Plugin * plugin = d->plugins.first();
    for ( ; plugin != 0; plugin = d->plugins.next() )
    {
      QDomElement data( plugin->saveXML( doc ) );
      if ( !data.isNull() )
        spread.appendChild( data );
    }

    QDomElement s = styleManager()->save( doc );
    spread.appendChild( s );
    QDomElement e = map()->save( doc );
    spread.appendChild( e );

    setModified( false );

    return doc;
}

bool Doc::loadChildren( KoStore* _store )
{
    return map()->loadChildren( _store );
}

bool Doc::saveOasis( KoStore* store, KoXmlWriter* manifestWriter )
{
  emitBeginOperation(true);
  	bool result=saveOasisHelper( store, manifestWriter, SaveAll );
  emitEndOperation();

  return result;
}

bool Doc::saveOasisHelper( KoStore* store, KoXmlWriter* manifestWriter, SaveFlag saveFlag,
                            QString* /*plainText*/, KoPicture* /*picture*/ )
{
    d->m_pictureCollection.assignUniqueIds();
    //Terminate current cell edition, if any
    QPtrListIterator<KoView> it2( views() );
    d->m_savingWholeDocument = saveFlag == SaveAll ? true : false;

    /* don't pull focus away from the editor if this is just a background
       autosave */
    if (!isAutosaving())
    {
        for (; it2.current(); ++it2 )
            static_cast<View *>( it2.current() )->deleteEditor( true );
    }
    if ( !store->open( "content.xml" ) )
        return false;

    KoStoreDevice dev( store );
    KoXmlWriter* contentWriter = createOasisXmlWriter( &dev, "office:document-content" );
    KoGenStyles mainStyles;//for compile

    KTempFile contentTmpFile;
    //Check that temp file was successfully created
    if (contentTmpFile.status() != 0)
    {
	    qWarning("Creation of temporary file to store document content failed.");
	    return false;
    }

    contentTmpFile.setAutoDelete( true );
    QFile* tmpFile = contentTmpFile.file();
    KoXmlWriter contentTmpWriter( tmpFile, 1 );



    //todo fixme just add a element for testing saving content.xml
    contentTmpWriter.startElement( "office:body" );
    contentTmpWriter.startElement( "office:spreadsheet" );

    int indexObj = 1;
    int partIndexObj = 0;

    // Saving the custom cell styles including the default cell style.
    styleManager()->saveOasis( mainStyles );

    // Saving the default column style
    KoGenStyle defaultColumnStyle( Doc::STYLE_COLUMN_USER, "table-column" );
    defaultColumnStyle.addPropertyPt( "style:column-width", Format::globalColWidth() );
    defaultColumnStyle.setDefaultStyle( true );
    mainStyles.lookup( defaultColumnStyle, "Default", KoGenStyles::DontForceNumbering );

    // Saving the default row style
    KoGenStyle defaultRowStyle( Doc::STYLE_ROW_USER, "table-row" );
    defaultRowStyle.addPropertyPt( "style:row-height", Format::globalRowHeight() );
    defaultRowStyle.setDefaultStyle( true );
    mainStyles.lookup( defaultRowStyle, "Default", KoGenStyles::DontForceNumbering );

    // Saving the map.
    map()->saveOasis( contentTmpWriter, mainStyles, store,  manifestWriter, indexObj, partIndexObj );

    saveOasisAreaName( contentTmpWriter );
    contentTmpWriter.endElement(); ////office:spreadsheet
  contentTmpWriter.endElement(); ////office:body

    // Done with writing out the contents to the tempfile, we can now write out the automatic styles
    contentWriter->startElement( "office:automatic-styles" );

    QValueList<KoGenStyles::NamedStyle> styles = mainStyles.styles( KoGenStyle::STYLE_AUTO );
    QValueList<KoGenStyles::NamedStyle>::const_iterator it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "style:style", (*it).name, "style:paragraph-properties" );
    }

    styles = mainStyles.styles( STYLE_PAGE );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "style:style", (*it).name, "style:table-properties" );
    }

    styles = mainStyles.styles( STYLE_COLUMN_AUTO );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "style:style", (*it).name, "style:table-column-properties" );
    }

    styles = mainStyles.styles( STYLE_ROW_AUTO );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "style:style", (*it).name, "style:table-row-properties" );
    }

    styles = mainStyles.styles( STYLE_CELL_AUTO );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "style:style", (*it).name, "style:table-cell-properties" );
    }

    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_NUMBER );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
      (*it).style->writeStyle( contentWriter, mainStyles, "number:number-style", (*it).name, 0 /*TODO ????*/  );
    }

    //TODO FIXME !!!!
    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_DATE );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "number:date-style", (*it).name, 0 /*TODO ????*/  );
    }

    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_TIME );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "number:time-style", (*it).name, 0 /*TODO ????*/  );
    }

    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_FRACTION );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "number:number-style", (*it).name, 0 /*TODO ????*/  );
    }

    //TODO FIME !!!!!
    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_PERCENTAGE );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "number:percentage-style", (*it).name, 0 /*TODO ????*/  );
    }

    //TODO FIME !!!!!
    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_CURRENCY );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "number:currency-style", (*it).name, 0 /*TODO ????*/  );
    }

    styles = mainStyles.styles( KoGenStyle::STYLE_NUMERIC_SCIENTIFIC );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( contentWriter, mainStyles, "number:number-style", (*it).name, 0 /*TODO ????*/  );
    }


    contentWriter->endElement(); // office:automatic-styles


   // And now we can copy over the contents from the tempfile to the real one
    tmpFile->close();
    contentWriter->addCompleteElement( tmpFile );
    contentTmpFile.close();


    contentWriter->endElement(); // root element
    contentWriter->endDocument();
    delete contentWriter;
    if ( !store->close() )
        return false;
    //add manifest line for content.xml
    manifestWriter->addManifestEntry( "content.xml",  "text/xml" );

    //todo add manifest line for style.xml
    if ( !store->open( "styles.xml" ) )
        return false;

    manifestWriter->addManifestEntry( "styles.xml",  "text/xml" );
    saveOasisDocumentStyles( store, mainStyles );

    if ( !store->close() ) // done with styles.xml
        return false;

    makeUsedPixmapList();
    d->m_pictureCollection.saveOasisToStore( store, d->usedPictures, manifestWriter);

    if(!store->open("settings.xml"))
        return false;

    KoXmlWriter* settingsWriter = createOasisXmlWriter(&dev, "office:document-settings");
    settingsWriter->startElement("office:settings");
    settingsWriter->startElement("config:config-item-set");
    settingsWriter->addAttribute("config:name", "view-settings");

    KoUnit::saveOasis(settingsWriter, unit());

    saveOasisSettings( *settingsWriter );

    settingsWriter->endElement(); // config:config-item-set

    settingsWriter->startElement("config:config-item-set");
    settingsWriter->addAttribute("config:name", "configuration-settings");
    settingsWriter->addConfigItem("SpellCheckerIgnoreList", d->spellListIgnoreAll.join( "," ) );
    settingsWriter->endElement(); // config:config-item-set
    settingsWriter->endElement(); // office:settings
    settingsWriter->endElement(); // Root:element
    settingsWriter->endDocument();
    delete settingsWriter;

    if(!store->close())
        return false;

    manifestWriter->addManifestEntry("settings.xml", "text/xml");


    if ( saveFlag == SaveSelected )
    {
      QPtrListIterator<EmbeddedObject> it(embeddedObjects() );
      for( ; it.current(); ++it )
      {
        if ( it.current()->getType() != OBJECT_CHART  && it.current()->getType() != OBJECT_KOFFICE_PART )
          continue;
        KoDocumentChild *embedded = dynamic_cast<EmbeddedKOfficeObject *>(it.current() )->embeddedObject();
            //NOTE: If an application's .desktop file lies about opendocument support (ie. it indicates that it has
            //a native OASIS mime type, when it doesn't, this causes a crash when trying to reload and paint
            //the object, since it won't have an associated document.
          if ( !embedded->saveOasis( store, manifestWriter ) )
            continue;
      }
    }


    setModified( false );

    return true;
}

void Doc::loadOasisSettings( const QDomDocument&settingsDoc )
{
    KoOasisSettings settings( settingsDoc );
    KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
    if ( !viewSettings.isNull() )
    {
        setUnit(KoUnit::unit(viewSettings.parseConfigItemString("unit")));
    }
    map()->loadOasisSettings( settings );
    loadOasisIgnoreList( settings );
}

void Doc::saveOasisSettings( KoXmlWriter &settingsWriter )
{
    settingsWriter.startElement("config:config-item-map-indexed");
    settingsWriter.addAttribute("config:name", "Views");
    settingsWriter.startElement( "config:config-item-map-entry" );
    map()->saveOasisSettings( settingsWriter );
    settingsWriter.endElement();
    settingsWriter.endElement();
}


void Doc::loadOasisIgnoreList( const KoOasisSettings& settings )
{
    KoOasisSettings::Items configurationSettings = settings.itemSet( "configuration-settings" );
    if ( !configurationSettings.isNull() )
    {
        const QString ignorelist = configurationSettings.parseConfigItemString( "SpellCheckerIgnoreList" );
        //kdDebug()<<" ignorelist :"<<ignorelist<<endl;
        d->spellListIgnoreAll = QStringList::split( ',', ignorelist );
    }
}


void Doc::saveOasisDocumentStyles( KoStore* store, KoGenStyles& mainStyles ) const
{
    KoStoreDevice stylesDev( store );
    KoXmlWriter* stylesWriter = createOasisXmlWriter( &stylesDev, "office:document-styles" );

    stylesWriter->startElement( "office:styles" );
    QValueList<KoGenStyles::NamedStyle> styles = mainStyles.styles( KoGenStyle::STYLE_USER );
    QValueList<KoGenStyles::NamedStyle>::const_iterator it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:style", (*it).name, "style:paragraph-properties" );
    }

    // Writing out the common column styles.
    styles = mainStyles.styles( Doc::STYLE_COLUMN_USER );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
      if ( (*it).style->isDefaultStyle() ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:default-style", (*it).name, "style:table-column-properties" );
      }
      else {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:style", (*it).name, "style:table-column-properties" );
      }
    }

    // Writing out the row column styles.
    styles = mainStyles.styles( Doc::STYLE_ROW_USER );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
      if ( (*it).style->isDefaultStyle() ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:default-style", (*it).name, "style:table-row-properties" );
      }
      else {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:style", (*it).name, "style:table-row-properties" );
      }
    }

    // Writing out the common cell styles.
    styles = mainStyles.styles( Doc::STYLE_CELL_USER );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        if ( (*it).style->isDefaultStyle() ) {
          (*it).style->writeStyle( stylesWriter, mainStyles, "style:default-style", (*it).name, "style:table-cell-properties" );
        }
        else {
          (*it).style->writeStyle( stylesWriter, mainStyles, "style:style", (*it).name, "style:table-cell-properties" );
        }
    }

    styles = mainStyles.styles( KoGenStyle::STYLE_HATCH );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "draw:hatch", (*it).name, "style:graphic-properties" ,  true,  true /*add draw:name*/);
    }
    styles = mainStyles.styles( KoGenStyle::STYLE_GRAPHICAUTO );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:style", (*it).name , "style:graphic-properties"  );
    }

    stylesWriter->endElement(); // office:styles

    stylesWriter->startElement( "office:automatic-styles" );
    styles = mainStyles.styles( KoGenStyle::STYLE_PAGELAYOUT );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:page-layout", (*it).name, "style:page-layout-properties", false /*don't close*/ );
        stylesWriter->endElement();
    }

    stylesWriter->endElement(); // office:automatic-styles
    //code from kword
    stylesWriter->startElement( "office:master-styles" );

    styles = mainStyles.styles( Doc::STYLE_PAGEMASTER );
    it = styles.begin();
    for ( ; it != styles.end() ; ++it ) {
        (*it).style->writeStyle( stylesWriter, mainStyles, "style:master-page", (*it).name, "" );
    }

    stylesWriter->endElement(); // office:master-style


    stylesWriter->endElement(); // root element (office:document-styles)
    stylesWriter->endDocument();
    delete stylesWriter;;
}

bool Doc::loadOasis( const QDomDocument& doc, KoOasisStyles& oasisStyles, const QDomDocument& settings, KoStore* store)
{
    if ( !d->m_loadingInfo )
        d->m_loadingInfo = new KSPLoadingInfo;

    QTime dt;
    dt.start();

    emit sigProgress( 0 );
    d->isLoading = true;
    d->spellListIgnoreAll.clear();

    d->refs.clear();

    QDomElement content = doc.documentElement();
    QDomElement realBody ( KoDom::namedItemNS( content, KoXmlNS::office, "body" ) );
    if ( realBody.isNull() )
    {
        setErrorMessage( i18n( "Invalid OASIS OpenDocument file. No office:body tag found." ));
        deleteLoadingInfo();
        return false;
    }
    QDomElement body = KoDom::namedItemNS( realBody, KoXmlNS::office, "spreadsheet" );

    if ( body.isNull() )
    {
        kdError(32001) << "No office:spreadsheet found!" << endl;
        QDomElement childElem;
        QString localName;
        forEachElement( childElem, realBody ) {
            localName = childElem.localName();
        }
        if ( localName.isEmpty() )
            setErrorMessage( i18n( "Invalid OASIS OpenDocument file. No tag found inside office:body." ) );
        else
            setErrorMessage( i18n( "This document is not a spreadsheet, but %1. Please try opening it with the appropriate application." ).arg( KoDocument::tagNameToDocumentType( localName ) ) );
        deleteLoadingInfo();
        return false;
    }

    KoOasisLoadingContext context( this, oasisStyles, store );

    //load in first
    styleManager()->loadOasisStyleTemplate( oasisStyles );

    // load default column style
    const QDomElement* defaultColumnStyle = oasisStyles.defaultStyle( "table-column" );
    if ( defaultColumnStyle )
    {
//       kDebug() << "style:default-style style:family=\"table-column\"" << endl;
      KoStyleStack styleStack;
      styleStack.push( *defaultColumnStyle );
      styleStack.setTypeProperties( "table-column" );
      if ( styleStack.hasAttributeNS( KoXmlNS::style, "column-width" ) )
      {
        const double width = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::style, "column-width" ), -1.0 );
        if ( width != -1.0 )
        {
//           kDebug() << "\tstyle:column-width: " << width << endl;
          Format::setGlobalColWidth( width );
        }
      }
    }

    // load default row style
    const QDomElement* defaultRowStyle = oasisStyles.defaultStyle( "table-row" );
    if ( defaultRowStyle )
    {
//       kDebug() << "style:default-style style:family=\"table-row\"" << endl;
      KoStyleStack styleStack;
      styleStack.push( *defaultRowStyle );
      styleStack.setTypeProperties( "table-row" );
      if ( styleStack.hasAttributeNS( KoXmlNS::style, "row-height" ) )
      {
        const double height = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::style, "row-height" ), -1.0 );
        if ( height != -1.0 )
        {
//           kDebug() << "\tstyle:row-height: " << height << endl;
          Format::setGlobalRowHeight( height );
        }
      }
    }

    // TODO check versions and mimetypes etc.
    loadOasisAreaName( body );
    loadOasisCellValidation( body );

    // all <sheet:sheet> goes to workbook
    if ( !map()->loadOasis( body, context ) )
    {
        d->isLoading = false;
        deleteLoadingInfo();
        return false;
    }


    if ( !settings.isNull() )
    {
        loadOasisSettings( settings );
    }
    emit sigProgress( 90 );
    initConfig();
    emit sigProgress(-1);

    //display loading time
    kdDebug(36001) << "Loading took " << (float)(dt.elapsed()) / 1000.0 << " seconds" << endl;
    return true;
}

bool Doc::loadXML( QIODevice *, const QDomDocument& doc )
{
  QTime dt;
  dt.start();

  emit sigProgress( 0 );
  d->isLoading = true;
  d->spellListIgnoreAll.clear();
  // <spreadsheet>
  QDomElement spread = doc.documentElement();

  if ( spread.attribute( "mime" ) != "application/x-kspread" && spread.attribute( "mime" ) != "application/vnd.kde.kspread" )
  {
    d->isLoading = false;
    setErrorMessage( i18n( "Invalid document. Expected mimetype application/x-kspread or application/vnd.kde.kspread, got %1" ).arg( spread.attribute("mime") ) );
    return false;
  }

  d->syntaxVersion = Doc::getAttribute( spread, "syntaxVersion", 0 );
  if ( d->syntaxVersion > CURRENT_SYNTAX_VERSION )
  {
      int ret = KMessageBox::warningContinueCancel(
          0, i18n("This document was created with a newer version of KSpread (syntax version: %1)\n"
                  "When you open it with this version of KSpread, some information may be lost.").arg(d->syntaxVersion),
          i18n("File Format Mismatch"), KStdGuiItem::cont() );
      if ( ret == KMessageBox::Cancel )
      {
          setErrorMessage( "USER_CANCELED" );
          return false;
      }
  }

  // <locale>
  QDomElement loc = spread.namedItem( "locale" ).toElement();
  if ( !loc.isNull() )
      ((Locale *) locale())->load( loc );

  emit sigProgress( 5 );

  QDomElement defaults = spread.namedItem( "defaults" ).toElement();
  if ( !defaults.isNull() )
  {
    bool ok = false;
    double d = defaults.attribute( "row-height" ).toDouble( &ok );
    if ( !ok )
      return false;
    Format::setGlobalRowHeight( d );

    d = defaults.attribute( "col-width" ).toDouble( &ok );

    if ( !ok )
      return false;

    Format::setGlobalColWidth( d );
  }

  d->refs.clear();
  //<areaname >
  QDomElement areaname = spread.namedItem( "areaname" ).toElement();
  if ( !areaname.isNull())
    loadAreaName(areaname);

  QDomElement ignoreAll = spread.namedItem( "SPELLCHECKIGNORELIST").toElement();
  if ( !ignoreAll.isNull())
  {
      QDomElement spellWord=spread.namedItem("SPELLCHECKIGNORELIST").toElement();

      spellWord=spellWord.firstChild().toElement();
      while ( !spellWord.isNull() )
      {
          if ( spellWord.tagName()=="SPELLCHECKIGNOREWORD" )
          {
              d->spellListIgnoreAll.append(spellWord.attribute("word"));
          }
          spellWord=spellWord.nextSibling().toElement();
      }
  }

  emit sigProgress( 40 );
  // In case of reload (e.g. from konqueror)
  map()->sheetList().clear(); // it's set to autoDelete

  QDomElement styles = spread.namedItem( "styles" ).toElement();
  if ( !styles.isNull() )
  {
    if ( !styleManager()->loadXML( styles ) )
    {
      setErrorMessage( i18n( "Styles cannot be loaded." ) );
      d->isLoading = false;
      return false;
    }
  }

  // <map>
  QDomElement mymap = spread.namedItem( "map" ).toElement();
  if ( mymap.isNull() )
  {
      setErrorMessage( i18n("Invalid document. No map tag.") );
      d->isLoading = false;
      return false;
  }
  if ( !map()->loadXML( mymap ) )
  {
      d->isLoading = false;
      return false;
  }

  //Backwards compatibility with older versions for paper layout
  if ( d->syntaxVersion < 1 )
  {
    QDomElement paper = spread.namedItem( "paper" ).toElement();
    if ( !paper.isNull() )
    {
      loadPaper( paper );
    }
  }

  emit sigProgress( 85 );

  QDomElement element( spread.firstChild().toElement() );
  while ( !element.isNull() )
  {
    QString tagName( element.tagName() );

    if ( tagName != "locale" && tagName != "map" && tagName != "styles"
         && tagName != "SPELLCHECKIGNORELIST" && tagName != "areaname"
         && tagName != "paper" )
    {
      // belongs to a plugin, load it and save it for later use
      m_savedDocParts[ tagName ] = element;
    }

    element = element.nextSibling().toElement();
  }

  emit sigProgress( 90 );
  initConfig();
  emit sigProgress(-1);

   kdDebug(36001) << "Loading took " << (float)(dt.elapsed()) / 1000.0 << " seconds" << endl;

  emit sig_refreshView();

  return true;
}

void Doc::loadPaper( QDomElement const & paper )
{
  // <paper>
  QString format = paper.attribute( "format" );
  QString orientation = paper.attribute( "orientation" );

  // <borders>
  QDomElement borders = paper.namedItem( "borders" ).toElement();
  if ( !borders.isNull() )
  {
    float left = borders.attribute( "left" ).toFloat();
    float right = borders.attribute( "right" ).toFloat();
    float top = borders.attribute( "top" ).toFloat();
    float bottom = borders.attribute( "bottom" ).toFloat();

    //apply to all sheet
    QPtrListIterator<Sheet> it ( map()->sheetList() );
    for( ; it.current(); ++it )
    {
      it.current()->print()->setPaperLayout( left, top, right, bottom,
                                             format, orientation );
    }
  }

  QString hleft, hright, hcenter;
  QString fleft, fright, fcenter;
  // <head>
  QDomElement head = paper.namedItem( "head" ).toElement();
  if ( !head.isNull() )
  {
    QDomElement left = head.namedItem( "left" ).toElement();
    if ( !left.isNull() )
      hleft = left.text();
    QDomElement center = head.namedItem( "center" ).toElement();
    if ( !center.isNull() )
      hcenter = center.text();
    QDomElement right = head.namedItem( "right" ).toElement();
    if ( !right.isNull() )
      hright = right.text();
  }
  // <foot>
  QDomElement foot = paper.namedItem( "foot" ).toElement();
  if ( !foot.isNull() )
  {
    QDomElement left = foot.namedItem( "left" ).toElement();
    if ( !left.isNull() )
      fleft = left.text();
    QDomElement center = foot.namedItem( "center" ).toElement();
    if ( !center.isNull() )
      fcenter = center.text();
    QDomElement right = foot.namedItem( "right" ).toElement();
    if ( !right.isNull() )
      fright = right.text();
  }
  //The macro "<sheet>" formerly was typed as "<table>"
  hleft   = hleft.replace(   "<table>", "<sheet>" );
  hcenter = hcenter.replace( "<table>", "<sheet>" );
  hright  = hright.replace(  "<table>", "<sheet>" );
  fleft   = fleft.replace(   "<table>", "<sheet>" );
  fcenter = fcenter.replace( "<table>", "<sheet>" );
  fright  = fright.replace(  "<table>", "<sheet>" );

  QPtrListIterator<Sheet> it ( map()->sheetList() );
  for( ; it.current(); ++it )
  {
    it.current()->print()->setHeadFootLine( hleft, hcenter, hright,
                                            fleft, fcenter, fright);
  }
}

bool Doc::completeLoading( KoStore* /* _store */ )
{
  kdDebug(36001) << "------------------------ COMPLETING --------------------" << endl;

  d->isLoading = false;

  //  map()->update();

  QPtrListIterator<KoView> it( views() );
  for (; it.current(); ++it )
    ((View*)it.current())->initialPosition();

  kdDebug(36001) << "------------------------ COMPLETION DONE --------------------" << endl;

  setModified( false );
  return true;
}


void Doc::registerPlugin( Plugin * plugin )
{
  d->plugins.append( plugin );
}

void Doc::deregisterPlugin( Plugin * plugin )
{
  d->plugins.remove( plugin );
}

bool Doc::docData( QString const & xmlTag, QDomElement & data )
{
  SavedDocParts::iterator iter = m_savedDocParts.find( xmlTag );
  if ( iter == m_savedDocParts.end() )
    return false;

  data = iter.data();
  m_savedDocParts.erase( iter );

  return true;
}

void Doc::setShowVerticalScrollBar(bool _show)
{
  d->verticalScrollBar=_show;
}

bool Doc::showVerticalScrollBar()const
{
  return  d->verticalScrollBar;
}

void Doc::setShowHorizontalScrollBar(bool _show)
{
  d->horizontalScrollBar=_show;
}

bool Doc::showHorizontalScrollBar()const
{
  return  d->horizontalScrollBar;
}

KGlobalSettings::Completion Doc::completionMode( ) const
{
  return d->completionMode;
}

void Doc::setShowColumnHeader(bool _show)
{
  d->columnHeader=_show;
}

bool Doc::showColumnHeader() const
{
  return  d->columnHeader;
}

void Doc::setShowRowHeader(bool _show)
{
  d->rowHeader=_show;
}

bool Doc::showRowHeader() const
{
  return  d->rowHeader;
}

void Doc::setGridColor( const QColor& color )
{
  d->gridColor = color;
}

QColor Doc::gridColor() const
{
  return d->gridColor;
}

void Doc::setCompletionMode( KGlobalSettings::Completion complMode)
{
  d->completionMode= complMode;
}

double Doc::indentValue() const
{
  return d->indentValue;
}

void Doc::setIndentValue( double val )
{
  d->indentValue = val;
}

void Doc::setShowStatusBar(bool _statusBar)
{
  d->showStatusBar=_statusBar;
}

bool Doc::showStatusBar() const
{
  return  d->showStatusBar;
}

void Doc::setShowTabBar(bool _tabbar)
{
  d->showTabBar=_tabbar;
}

bool Doc::showTabBar()const
{
  return  d->showTabBar;
}

void Doc::setShowFormulaBar(bool _formulaBar)
{
  d->showFormulaBar=_formulaBar;
}

bool Doc::showFormulaBar() const
{
  return  d->showFormulaBar;
}

void Doc::setShowMessageError(bool _show)
{
  d->showError=_show;
}

bool Doc::showMessageError() const
{
  return  d->showError;
}

KSpread::MoveTo Doc::getMoveToValue() const
{
  return d->moveTo;
}

void Doc::setMoveToValue(KSpread::MoveTo _moveTo)
{
  d->moveTo = _moveTo;
}

void Doc::setTypeOfCalc( MethodOfCalc _calc)
{
  d->calcMethod=_calc;
}

MethodOfCalc Doc::getTypeOfCalc() const
{
  return d->calcMethod;
}

void Doc::setKSpellConfig(KSpellConfig _kspell)
{
  if (d->spellConfig == 0 )
    d->spellConfig = new KSpellConfig();

  d->spellConfig->setNoRootAffix(_kspell.noRootAffix ());
  d->spellConfig->setRunTogether(_kspell.runTogether ());
  d->spellConfig->setDictionary(_kspell.dictionary ());
  d->spellConfig->setDictFromList(_kspell.dictFromList());
  d->spellConfig->setEncoding(_kspell.encoding());
  d->spellConfig->setClient(_kspell.client());
}

KSpellConfig * Doc::getKSpellConfig()
{
    if (!d->spellConfig)
    {
    	KSpellConfig ksconfig;

    	KConfig *config = Factory::global()->config();
    	if( config->hasGroup("KSpell kspread" ) )
    	{
        	config->setGroup( "KSpell kspread" );
        	ksconfig.setNoRootAffix(config->readNumEntry ("KSpell_NoRootAffix", 0));
        	ksconfig.setRunTogether(config->readNumEntry ("KSpell_RunTogether", 0));
        	ksconfig.setDictionary(config->readEntry ("KSpell_Dictionary", ""));
        	ksconfig.setDictFromList(config->readNumEntry ("KSpell_DictFromList", false));
        	ksconfig.setEncoding(config->readNumEntry ("KSpell_Encoding", KS_E_ASCII));
        	ksconfig.setClient(config->readNumEntry ("KSpell_Client", KS_CLIENT_ISPELL));
        	setKSpellConfig(ksconfig);

        	setDontCheckUpperWord(config->readBoolEntry("KSpell_IgnoreUppercaseWords", false));
        	setDontCheckTitleCase(config->readBoolEntry("KSpell_IgnoreTitleCaseWords", false));
    	}
    }
  return d->spellConfig;
}

bool Doc::dontCheckUpperWord() const
{
  return d->dontCheckUpperWord;
}

void Doc::setDontCheckUpperWord( bool b )
{
  d->dontCheckUpperWord = b;
}

bool Doc::dontCheckTitleCase() const
{
  return  d->dontCheckTitleCase;
}

void Doc::setDontCheckTitleCase( bool b )
{
  d->dontCheckTitleCase = b;
}

QString Doc::unitName() const
{
  return KoUnit::unitName( unit() );
}

void Doc::increaseNumOperation()
{
  ++d->numOperations;
}

void Doc::decreaseNumOperation()
{
  --d->numOperations;
}

void Doc::addIgnoreWordAllList( const QStringList & _lst)
{
  d->spellListIgnoreAll = _lst;
}

QStringList Doc::spellListIgnoreAll() const
{
  return d->spellListIgnoreAll;
}

void Doc::setZoomAndResolution( int zoom, int dpiX, int dpiY )
{
    KoZoomHandler::setZoomAndResolution( zoom, dpiX, dpiY );
}

void Doc::newZoomAndResolution( bool updateViews, bool /*forPrint*/ )
{
/*    layout();
    updateAllFrames();*/
    if ( updateViews )
    {
        emit sig_refreshView();
    }
}

void Doc::addCommand( KCommand* command )
{
  if (undoLocked()) return;
  d->commandHistory->addCommand( command, false );
  setModified( true );
}

void Doc::addCommand( UndoAction* undo )
{
  if (undoLocked()) return;
  UndoWrapperCommand* command = new UndoWrapperCommand( undo );
  addCommand( command );
  setModified( true );
}

void Doc::undo()
{
  undoLock ();
  d->commandHistory->undo();
  undoUnlock ();
}

void Doc::redo()
{
  undoLock ();
  d->commandHistory->redo();
  undoUnlock ();
}

void Doc::commandExecuted()
{
  setModified( true );
}

void Doc::documentRestored()
{
  setModified( false );
}

void Doc::undoLock()
{
  d->undoLocked++;
}

void Doc::undoUnlock()
{
  d->undoLocked--;
}

bool Doc::undoLocked() const
{
  return (d->undoLocked > 0);
}

KoCommandHistory* Doc::commandHistory()
{
  return d->commandHistory;
}

void Doc::enableUndo( bool _b )
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
      static_cast<View *>( it.current() )->enableUndo( _b );
}

void Doc::enableRedo( bool _b )
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
      static_cast<View *>( it.current() )->enableRedo( _b );
}

void Doc::paintContent( QPainter& painter, const QRect& rect,
                               bool transparent, double zoomX, double /*zoomY*/ )
{
    kdDebug(36001) << "paintContent() called on " << rect << endl;

  //  ElapsedTime et( "Doc::paintContent1" );
    //kdDebug(36001) << "Doc::paintContent m_zoom=" << m_zoom << " zoomX=" << zoomX << " zoomY=" << zoomY << " transparent=" << transparent << endl;

    // save current zoom
    int oldZoom = m_zoom;

    // choose sheet: the first or the active
    Sheet* sheet = 0L;
    if ( !d->activeSheet )
        sheet = map()->firstSheet();
    else
        sheet = d->activeSheet;
    if ( !sheet )
        return;

    // only one zoom is supported
    double d_zoom = 1.0;
    setZoomAndResolution( 100, KoGlobal::dpiX(), KoGlobal::dpiY() );
    if ( m_zoomedResolutionX != zoomX )
        d_zoom *= ( zoomX / m_zoomedResolutionX );

    // KSpread support zoom, therefore no need to scale with worldMatrix
    QWMatrix matrix = painter.worldMatrix();
    matrix.setMatrix( 1, 0, 0, 1, matrix.dx(), matrix.dy() );
    QRect prect = rect;
    prect.setWidth( (int) (prect.width() * painter.worldMatrix().m11()) );
    prect.setHeight( (int) (prect.height() * painter.worldMatrix().m22()) );
    setZoomAndResolution( (int) ( d_zoom * 100 ), KoGlobal::dpiX(), KoGlobal::dpiY() );
    // paint the content, now zoom is correctly set
    kdDebug(36001)<<"paintContent-------------------------------------\n";
    painter.save();
    painter.setWorldMatrix( matrix );
    paintContent( painter, prect, transparent, sheet, false );
    painter.restore();

    // restore zoom
    m_zoom = oldZoom;
    setZoomAndResolution( oldZoom, KoGlobal::dpiX(), KoGlobal::dpiY() );
}

void Doc::paintContent( QPainter& painter, const QRect& rect, bool /*transparent*/, Sheet* sheet, bool drawCursor )
{
    if ( isLoading() )
        return;
    //    ElapsedTime et( "Doc::paintContent2" );

    // if ( !transparent )
    // painter.eraseRect( rect );

    double xpos;
    double ypos;
    int left_col   = sheet->leftColumn( unzoomItX( rect.x() ), xpos );
    int right_col  = sheet->rightColumn( unzoomItX( rect.right() ) );
    int top_row    = sheet->topRow( unzoomItY( rect.y() ), ypos );
    int bottom_row = sheet->bottomRow( unzoomItY( rect.bottom() ) );
    QPen pen;
    pen.setWidth( 1 );
    painter.setPen( pen );

    /* Update the entire visible area. */
    QValueList<QRect>  cellAreaList;
    cellAreaList.append( QRect( left_col,
                                top_row,
                                right_col - left_col + 1,
                                bottom_row - top_row + 1) );

    paintCellRegions(painter, rect, NULL, cellAreaList, sheet, drawCursor);
}

void Doc::paintUpdates()
{
  //  ElapsedTime et( "Doc::paintUpdates" );

  QPtrListIterator<KoView> it( views() );
  View  * view  = NULL;
  Sheet * sheet = NULL;

  for (; it.current(); ++it )
  {
    view = static_cast<View *>( it.current() );
    view->paintUpdates();
  }

  for (sheet = map()->firstSheet(); sheet != NULL;
       sheet = map()->nextSheet())
  {
    sheet->clearPaintDirtyData();
  }
}

void Doc::paintCellRegions(QPainter& painter, const QRect &viewRect,
			   View* view,
			   QValueList<QRect> cellRegions,
			   const Sheet* sheet, bool /*drawCursor*/)
{
  //
  // Clip away children
  //

  QRegion rgn = painter.clipRegion();
  if ( rgn.isEmpty() )
    rgn = QRegion( QRect( 0, 0, viewRect.width(), viewRect.height() ) );

  QWMatrix matrix;
  if ( view ) {
    matrix.scale( zoomedResolutionX(),
                  zoomedResolutionY() );
    matrix.translate( - view->canvasWidget()->xOffset(),
                      - view->canvasWidget()->yOffset() );
  }
  else {
    matrix = painter.worldMatrix();
  }

//   QPtrListIterator<KoDocumentChild> it( children() );
//   for( ; it.current(); ++it ) {
//     // if ( ((Child*)it.current())->sheet() == sheet &&
//     //    !m_pView->hasDocumentInWindow( it.current()->document() ) )
//     if ( ((Child*)it.current())->sheet() == sheet)
//       rgn -= it.current()->region( matrix );
//   }
  painter.setClipRegion( rgn );

  QPen pen;
  pen.setWidth( 1 );
  painter.setPen( pen );

  QRect cellRegion;
  KoRect unzoomedViewRect = unzoomRect( viewRect );

  for (unsigned int i=0; i < cellRegions.size(); i++) {
    cellRegion = cellRegions[i];

    PaintRegion(painter, unzoomedViewRect, view, cellRegion, sheet);
  }
}


void Doc::PaintRegion(QPainter &painter, const KoRect &viewRegion,
		      View* view, const QRect &paintRegion,
		      const Sheet* sheet)
{
  // Paint region has cell coordinates (col,row) while viewRegion has
  // world coordinates.  paintRegion is the cells to update and
  // viewRegion is the area actually onscreen.

  if ( paintRegion.left() <= 0 || paintRegion.top() <= 0 )
    return;

  // Get the world coordinates of the upper left corner of the
  // paintRegion The view is NULL, when paintRegion is called from
  // paintContent, which itself is only called, when we should paint
  // the output for INACTIVE embedded view.  If inactive embedded,
  // then there is no view and we alwas start at top/left, so the
  // offset is 0.
  //
  KoPoint  dblCorner;
  if ( view == 0L ) //Most propably we are embedded and inactive, so no offset
    dblCorner = KoPoint( sheet->dblColumnPos( paintRegion.left() ),
			 sheet->dblRowPos( paintRegion.top() ) );
  else
    dblCorner = KoPoint( sheet->dblColumnPos( paintRegion.left() )
			 - view->canvasWidget()->xOffset(),
			 sheet->dblRowPos( paintRegion.top() )
			 - view->canvasWidget()->yOffset() );
  KoPoint dblCurrentCellPos( dblCorner );

  int regionBottom = paintRegion.bottom();
  int regionRight  = paintRegion.right();
  int regionLeft   = paintRegion.left();
  int regionTop    = paintRegion.top();

  QValueList<QPoint>  mergedCellsPainted;
  for ( int y = regionTop;
        y <= regionBottom && dblCurrentCellPos.y() <= viewRegion.bottom();
        ++y )
  {
    const RowFormat * row_lay = sheet->rowFormat( y );
    dblCurrentCellPos.setX( dblCorner.x() );

    for ( int x = regionLeft;
          x <= regionRight && dblCurrentCellPos.x() <= viewRegion.right();
          ++x )
    {
      const ColumnFormat *col_lay = sheet->columnFormat( x );
      Cell* cell = sheet->cellAt( x, y );

      QPoint cellRef( x, y );

#if 0
      bool paintBordersBottom = false;
      bool paintBordersRight  = false;
      bool paintBordersLeft   = false;
      bool paintBordersTop    = false;
#endif
      int paintBorder = Cell::Border_None;

      QPen rightPen( cell->effRightBorderPen( x, y ) );
      QPen leftPen( cell->effLeftBorderPen( x, y ) );
      QPen topPen( cell->effTopBorderPen( x, y ) );
      QPen bottomPen( cell->effBottomBorderPen( x, y ) );

      // Paint border if outermost cell or if the pen is more "worth"
      // than the border pen of the cell on the other side of the
      // border or if the cell on the other side is not painted. In
      // the latter case get the pen that is of more "worth"

      // right border:
      if ( x >= KS_colMax )
        //paintBordersRight = true;
        paintBorder |= Cell::Border_Right;
      else if ( x == regionRight ) {
	paintBorder |= Cell::Border_Right;
	if ( cell->effRightBorderValue( x, y )
	     < sheet->cellAt( x + 1, y )->effLeftBorderValue( x + 1, y ) )
	  rightPen = sheet->cellAt( x + 1, y )->effLeftBorderPen( x + 1, y );
      }
      else {
	paintBorder |= Cell::Border_Right;
	if ( cell->effRightBorderValue( x, y )
	     < sheet->cellAt( x + 1, y )->effLeftBorderValue( x + 1, y ) )
	  rightPen = sheet->cellAt( x + 1, y )->effLeftBorderPen( x + 1, y );
      }

      // Similiar for other borders...
      // bottom border:
      if ( y >= KS_rowMax )
        paintBorder |= Cell::Border_Bottom;
      else if ( y == regionBottom ) {
	paintBorder |= Cell::Border_Bottom;
	if ( cell->effBottomBorderValue( x, y )
	     < sheet->cellAt( x, y + 1 )->effTopBorderValue( x, y + 1) )
	  bottomPen = sheet->cellAt( x, y + 1 )->effTopBorderPen( x, y + 1 );
      }
      else {
        paintBorder |= Cell::Border_Bottom;
        if ( cell->effBottomBorderValue( x, y )
	     < sheet->cellAt( x, y + 1 )->effTopBorderValue( x, y + 1) )
          bottomPen = sheet->cellAt( x, y + 1 )->effTopBorderPen( x, y + 1 );
      }

      // left border:
      if ( x == 1 )
        paintBorder |= Cell::Border_Left;
      else if ( x == regionLeft ) {
	paintBorder |= Cell::Border_Left;
	if ( cell->effLeftBorderValue( x, y )
	     < sheet->cellAt( x - 1, y )->effRightBorderValue( x - 1, y ) )
	  leftPen = sheet->cellAt( x - 1, y )->effRightBorderPen( x - 1, y );
      }
      else {
        paintBorder |= Cell::Border_Left;
        if ( cell->effLeftBorderValue( x, y )
	     < sheet->cellAt( x - 1, y )->effRightBorderValue( x - 1, y ) )
          leftPen = sheet->cellAt( x - 1, y )->effRightBorderPen( x - 1, y );
      }

      // top border:
      if ( y == 1 )
        paintBorder |= Cell::Border_Top;
      else if ( y == regionTop ) {
	paintBorder |= Cell::Border_Top;
	if ( cell->effTopBorderValue( x, y )
	     < sheet->cellAt( x, y - 1 )->effBottomBorderValue( x, y - 1 ) )
	  topPen = sheet->cellAt( x, y - 1 )->effBottomBorderPen( x, y - 1 );
      }
      else {
        paintBorder |= Cell::Border_Top;
        if ( cell->effTopBorderValue( x, y )
	     < sheet->cellAt( x, y - 1 )->effBottomBorderValue( x, y - 1 ) )
          topPen = sheet->cellAt( x, y - 1 )->effBottomBorderPen( x, y - 1 );
      }

#if 0
      cell->paintCell( viewRegion, painter, view, dblCurrentCellPos, cellRef,
		       paintBordersRight, paintBordersBottom,
		       paintBordersLeft, paintBordersTop,
		       rightPen, bottomPen, leftPen, topPen,
		       mergedCellsPainted, false );

      Cell::BorderSides highlightBorder=Cell::Border_None;
      QPen highlightPen;
#endif

      cell->paintCell( viewRegion, painter, view, dblCurrentCellPos, cellRef,
		       paintBorder,
		       rightPen, bottomPen, leftPen, topPen,
		       mergedCellsPainted, false );


      dblCurrentCellPos.setX( dblCurrentCellPos.x() + col_lay->dblWidth() );
    }
    dblCurrentCellPos.setY( dblCurrentCellPos.y() + row_lay->dblHeight() );
  }
}


DCOPObject* Doc::dcopObject()
{
    if ( !d->dcop )
        d->dcop = new DocIface( this );

    return d->dcop;
}

void Doc::addAreaName(const QRect &_rect,const QString & name,const QString & sheetName)
{
  setModified( true );
  Reference tmp;
  tmp.rect = _rect;
  tmp.sheet_name = sheetName;
  tmp.ref_name = name;
  d->refs.append( tmp);
  emit sig_addAreaName( name );
}

void Doc::removeArea( const QString & name)
{
    QValueList<Reference>::Iterator it2;
    for ( it2 = d->refs.begin(); it2 != d->refs.end(); ++it2 )
    {
        if((*it2).ref_name==name)
        {
            d->refs.remove(it2);
            emit sig_removeAreaName( name );
            return;
        }
    }
}

void Doc::changeAreaSheetName(const QString & oldName,const QString & sheetName)
{
  QValueList<Reference>::Iterator it2;
  for ( it2 = d->refs.begin(); it2 != d->refs.end(); ++it2 )
        {
        if((*it2).sheet_name==oldName)
                   (*it2).sheet_name=sheetName;
        }
}

QRect Doc::getRectArea(const QString  &_sheetName)
{
  QValueList<Reference>::Iterator it2;
  for ( it2 = d->refs.begin(); it2 != d->refs.end(); ++it2 )
        {
        if((*it2).ref_name==_sheetName)
                {
                return (*it2).rect;
                }
        }
  return QRect(-1,-1,-1,-1);
}

QDomElement Doc::saveAreaName( QDomDocument& doc )
{
   QDomElement element = doc.createElement( "areaname" );
   QValueList<Reference>::Iterator it2;
   for ( it2 = d->refs.begin(); it2 != d->refs.end(); ++it2 )
   {
        QDomElement e = doc.createElement("reference");
        QDomElement tabname = doc.createElement( "tabname" );
        tabname.appendChild( doc.createTextNode( (*it2).sheet_name ) );
        e.appendChild( tabname );

        QDomElement refname = doc.createElement( "refname" );
        refname.appendChild( doc.createTextNode( (*it2).ref_name ) );
        e.appendChild( refname );

        QDomElement rect = doc.createElement( "rect" );
        rect.setAttribute( "left-rect", ((*it2).rect).left() );
        rect.setAttribute( "right-rect",((*it2).rect).right() );
        rect.setAttribute( "top-rect", ((*it2).rect).top() );
        rect.setAttribute( "bottom-rect", ((*it2).rect).bottom() );
        e.appendChild( rect );
        element.appendChild(e);
   }
   return element;
}

void Doc::loadOasisCellValidation( const QDomElement&body )
{
    QDomNode validation = KoDom::namedItemNS( body, KoXmlNS::table, "content-validations" );
    kdDebug()<<"void Doc::loadOasisCellValidation( const QDomElement&body ) \n";
    kdDebug()<<"validation.isNull ? "<<validation.isNull()<<endl;
    if ( !validation.isNull() )
    {
        QDomNode n = validation.firstChild();
        for( ; !n.isNull(); n = n.nextSibling() )
        {
            if ( n.isElement() )
            {
                QDomElement element = n.toElement();
                //kdDebug()<<" loadOasisCellValidation element.tagName() :"<<element.tagName()<<endl;
                if ( element.tagName() ==  "content-validation" && element.namespaceURI() == KoXmlNS::table ) {
                    d->m_loadingInfo->appendValidation(element.attributeNS( KoXmlNS::table, "name", QString::null ), element );
                    kdDebug()<<" validation found :"<<element.attributeNS( KoXmlNS::table, "name", QString::null )<<endl;
                }
                else {
                    kdDebug()<<" Tag not recognize :"<<element.tagName()<<endl;
                }
            }
        }
    }
}

void Doc::saveOasisAreaName( KoXmlWriter & xmlWriter )
{
    if ( listArea().count()>0 )
    {
        xmlWriter.startElement( "table:named-expressions" );
        QValueList<Reference>::Iterator it;
        for ( it = d->refs.begin(); it != d->refs.end(); ++it )
        {
            xmlWriter.startElement( "table:named-range" );

            xmlWriter.addAttribute( "table:name", ( *it ).ref_name );
            xmlWriter.addAttribute( "table:base-cell-address", convertRefToBase( ( *it ).sheet_name, ( *it ).rect ) );
            xmlWriter.addAttribute( "table:cell-range-address", convertRefToRange( ( *it ).sheet_name, ( *it ).rect ) );

            xmlWriter.endElement();
        }
        xmlWriter.endElement();
    }
}

void Doc::loadOasisAreaName( const QDomElement& body )
{
    kdDebug()<<"void Doc::loadOasisAreaName( const QDomElement& body ) \n";
    QDomNode namedAreas = KoDom::namedItemNS( body, KoXmlNS::table, "named-expressions" );
    if ( !namedAreas.isNull() )
    {
        kdDebug()<<" area name exist \n";
        QDomNode area = namedAreas.firstChild();
        while ( !area.isNull() )
        {
            QDomElement e = area.toElement();

            if ( e.localName() == "named-range" )
            {
                if ( !e.hasAttributeNS( KoXmlNS::table, "name" ) || !e.hasAttributeNS( KoXmlNS::table, "cell-range-address" ) )
                {
                    kdDebug() << "Reading in named area failed" << endl;
                    area = area.nextSibling();
                    continue;
                }

                // TODO: what is: sheet:base-cell-address
                QString name  = e.attributeNS( KoXmlNS::table, "name", QString::null );
                QString range = e.attributeNS( KoXmlNS::table, "cell-range-address", QString::null );
                kdDebug()<<"area name : "<<name<<" range :"<<range<<endl;
                d->m_loadingInfo->addWordInAreaList( name );
                kdDebug() << "Reading in named area, name: " << name << ", area: " << range << endl;

                range = Oasis::decodeFormula( range );

                if ( range.find( ':' ) == -1 )
                {
                    Point p( range );

                    int n = range.find( '!' );
                    if ( n > 0 )
                        range = range + ":" + range.right( range.length() - n - 1);

                    kdDebug() << "=> Area: " << range << endl;
                }

                if ( range.contains( '!' ) && range[0] == '$' )
                {
                    // cut absolute sheet indicator
                    range.remove( 0, 1 );
                }

                Range p( range );

                addAreaName( p.range(), name, p.sheetName() );
                kdDebug() << "Area range: " << p.toString() << endl;
            }
            else if ( e.localName() == "named-expression" )
            {
                kdDebug() << "Named expression found." << endl;
                // TODO
            }

            area = area.nextSibling();
        }
    }
}

void Doc::loadAreaName( const QDomElement& element )
{
  QDomElement tmp=element.firstChild().toElement();
  for( ; !tmp.isNull(); tmp=tmp.nextSibling().toElement()  )
  {
    if ( tmp.tagName() == "reference" )
    {
        QString tabname;
        QString refname;
        int left=0;
        int right=0;
        int top=0;
        int bottom=0;
        QDomElement sheetName = tmp.namedItem( "tabname" ).toElement();
        if ( !sheetName.isNull() )
        {
          tabname=sheetName.text();
        }
        QDomElement referenceName = tmp.namedItem( "refname" ).toElement();
        if ( !referenceName.isNull() )
        {
          refname=referenceName.text();
        }
        QDomElement rect =tmp.namedItem( "rect" ).toElement();
        if (!rect.isNull())
        {
          bool ok;
          if ( rect.hasAttribute( "left-rect" ) )
            left=rect.attribute("left-rect").toInt( &ok );
          if ( rect.hasAttribute( "right-rect" ) )
            right=rect.attribute("right-rect").toInt( &ok );
          if ( rect.hasAttribute( "top-rect" ) )
            top=rect.attribute("top-rect").toInt( &ok );
          if ( rect.hasAttribute( "bottom-rect" ) )
            bottom=rect.attribute("bottom-rect").toInt( &ok );
        }
        QRect _rect;
        _rect.setCoords(left,top,right,bottom);
        addAreaName(_rect,refname,tabname);
    }
  }
}

void Doc::addStringCompletion(const QString &stringCompletion)
{
  if ( d->listCompletion.items().contains(stringCompletion) == 0 )
    d->listCompletion.addItem( stringCompletion );
}

void Doc::refreshInterface()
{
  emit sig_refreshView();
}

void Doc::refreshLocale()
{
    emit sig_refreshLocale();
}


void Doc::emitBeginOperation(bool waitCursor)
{
    //If an emitBeginOperation occurs with waitCursor enabled, then the waiting cursor is set
    //until all operations have been completed.
    //
    //The reason being that any operations started before the first one with waitCursor set
    //are expected to be completed in a short time anyway.
    QCursor* activeOverride = QApplication::overrideCursor();

    if (waitCursor && ( (!activeOverride) || (activeOverride->shape() != Qt::waitCursor.shape()) )  )
    {
        QApplication::setOverrideCursor(Qt::waitCursor);
    }

//    /* just duplicate the current cursor on the stack, then */
//  else if (QApplication::overrideCursor() != NULL)
//    {
//        QApplication::setOverrideCursor(QApplication::overrideCursor()->shape());
//    }

    KoDocument::emitBeginOperation();
    d->delayCalculation = true;
    d->numOperations++;
}

void Doc::emitBeginOperation(void)
{
  emitBeginOperation(true);
}


void Doc::emitEndOperation()
{
   d->numOperations--;

   if (d->numOperations <= 0)
   {
     d->numOperations = 0;
     d->delayCalculation = false;
   }

   KoDocument::emitEndOperation();

   if (d->numOperations == 0)
   {
   	QApplication::restoreOverrideCursor();

     /* do this after the parent class emitEndOperation because that allows updates
        on the view again
     */
     paintUpdates();
   }
}

void Doc::emitEndOperation( const Region& /*region*/ )
{
  d->numOperations--;

  if ( d->numOperations > 0 || !d->activeSheet )
  {
    KoDocument::emitEndOperation();
    return;
  }

  d->numOperations = 0;
  d->delayCalculation = false;

  KoDocument::emitEndOperation();

  QApplication::restoreOverrideCursor();

    /* do this after the parent class emitEndOperation because that allows updates
    on the view again
    */
  paintUpdates();
}

bool Doc::delayCalculation() const
{
   return d->delayCalculation;
}

void Doc::updateBorderButton()
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
      static_cast<View *>( it.current() )->updateBorderButton();
}

void Doc::insertSheet( Sheet * sheet )
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
  ((View*)it.current())->insertSheet( sheet );
}

void Doc::takeSheet( Sheet * sheet )
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
  ((View*)it.current())->removeSheet( sheet );
}

void Doc::addIgnoreWordAll( const QString & word)
{
    if( d->spellListIgnoreAll.findIndex( word )==-1)
        d->spellListIgnoreAll.append( word );
}

void Doc::clearIgnoreWordAll( )
{
    d->spellListIgnoreAll.clear();
}

void Doc::setDisplaySheet(Sheet *_sheet )
{
    d->activeSheet = _sheet;
}

KSPLoadingInfo * Doc::loadingInfo() const
{
    return d->m_loadingInfo;
}

void Doc::deleteLoadingInfo()
{
    delete d->m_loadingInfo;
    d->m_loadingInfo = 0;
}

Sheet * Doc::displaySheet() const
{
    return d->activeSheet;
}

void Doc::addView( KoView *_view )
{
    KoDocument::addView( _view );
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
  ((View*)it.current())->closeEditor();
}

void Doc::addDamage( Damage* damage )
{
    d->damages.append( damage );

    if( d->damages.count() == 1 )
        QTimer::singleShot( 0, this, SLOT( flushDamages() ) );
}

void Doc::flushDamages()
{
    emit damagesFlushed( d->damages );
    QValueList<Damage*>::Iterator it;
    for( it = d->damages.begin(); it != d->damages.end(); ++it )
      delete *it;
    d->damages.clear();
}

void Doc::loadConfigFromFile()
{
    d->configLoadFromFile = true;
}

bool Doc::configLoadFromFile() const
{
    return d->configLoadFromFile;
}


void Doc::insertObject( EmbeddedObject * obj )
{
  switch ( obj->getType() )
  {
    case OBJECT_KOFFICE_PART: case OBJECT_CHART:
    {
      KoDocument::insertChild( dynamic_cast<EmbeddedKOfficeObject*>(obj)->embeddedObject() );
      break;
    }
    default:
      ;
  }
  d->m_embeddedObjects.append( obj );
}

QPtrList<EmbeddedObject>& Doc::embeddedObjects()
{
    return d->m_embeddedObjects;
}

KoPictureCollection *Doc::pictureCollection()
{
  return &d->m_pictureCollection;
}

void Doc::repaint( const QRect& rect )
{
  QRect r;
  QPtrListIterator<KoView> it( views() );
  for( ; it.current(); ++it )
  {
    r = rect;
    Canvas* canvas = ((View*)it.current())->canvasWidget();
    r.moveTopLeft( QPoint( r.x() - (int) canvas->xOffset(),
                           r.y() - (int) canvas->yOffset() ) );
    canvas->update( r );
  }
}

void Doc::repaint( EmbeddedObject *obj )
{
  QPtrListIterator<KoView> it( views() );
  for( ; it.current(); ++it )
  {
    Canvas* canvas = ((View*)it.current())->canvasWidget();
    if ( obj->sheet() == canvas->activeSheet() )
        canvas->repaintObject( obj );
  }
}

void Doc::repaint( const KoRect& rect )
{
  QRect r;
  QPtrListIterator<KoView> it( views() );
  for( ; it.current(); ++it )
  {
    Canvas* canvas = ((View*)it.current())->canvasWidget();

    r = zoomRect( rect );
    r.moveBy( (int)( -canvas->xOffset()*zoomedResolutionX() ) ,
                        (int)( -canvas->yOffset() *zoomedResolutionY()) );
    canvas->update( r );
  }
}

void Doc::addShell( KoMainWindow *shell )
{
  connect( shell, SIGNAL( documentSaved() ), d->commandHistory, SLOT( documentSaved() ) );
  KoDocument::addShell( shell );
}

int Doc::undoRedoLimit() const
{
  return d->commandHistory->undoLimit();
}

void Doc::setUndoRedoLimit(int val)
{
  d->commandHistory->setUndoLimit(val);
  d->commandHistory->setRedoLimit(val);
}

void Doc::insertPixmapKey( KoPictureKey key )
{
    if ( !d->usedPictures.contains( key ) )
        d->usedPictures.append( key );
}

void Doc::makeUsedPixmapList()
{
    d->usedPictures.clear();
    QPtrListIterator<EmbeddedObject> it( d->m_embeddedObjects );
    for ( ; it.current() ; ++it )
    {
        if( it.current()->getType() == OBJECT_PICTURE && ( d->m_savingWholeDocument || it.current()->isSelected() ) )
            insertPixmapKey( static_cast<EmbeddedPictureObject*>( it.current() )->getKey() );
    }
}

bool Doc::savingWholeDocument()
{
    return d->m_savingWholeDocument;
}

#include "kspread_doc.moc"

