kspread

kspread_map.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <stdlib.h>
00021 #include <time.h>
00022 
00023 #include <qfile.h>
00024 
00025 #include <kmdcodec.h>
00026 #include <ktempfile.h>
00027 
00028 #include <KoDom.h>
00029 #include <KoGenStyles.h>
00030 #include <KoOasisSettings.h>
00031 #include <KoXmlNS.h>
00032 #include <KoXmlWriter.h>
00033 
00034 #include "kspread_canvas.h"
00035 #include "kspread_doc.h"
00036 #include "kspread_genvalidationstyle.h"
00037 #include "kspread_locale.h"
00038 #include "kspread_sheet.h"
00039 #include "kspread_view.h"
00040 #include "KSpreadMapIface.h"
00041 
00042 #include "kspread_map.h"
00043 
00044 using namespace KSpread;
00045 
00046 bool Map::respectCase = true;
00047 
00048 Map::Map ( Doc* doc, const char* name)
00049   : QObject( doc, name ),
00050     m_doc( doc ),
00051     m_initialActiveSheet( 0 ),
00052     m_initialMarkerColumn( 0 ),
00053     m_initialMarkerRow( 0 ),
00054     m_initialXOffset(0.0),
00055     m_initialYOffset(0.0),
00056     tableId (1),
00057     m_dcop( 0 )
00058 {
00059   m_lstSheets.setAutoDelete( true );
00060 }
00061 
00062 Map::~Map()
00063 {
00064     delete m_dcop;
00065 }
00066 
00067 Doc* Map::doc() const
00068 {
00069   return m_doc;
00070 }
00071 
00072 void Map::setProtected( QCString const & passwd )
00073 {
00074   m_strPassword = passwd;
00075 }
00076 
00077 Sheet* Map::createSheet()
00078 {
00079   QString s( i18n("Sheet%1") );
00080   s = s.arg( tableId++ );
00081   Sheet *t = new Sheet ( this, s , s.utf8());
00082   t->setSheetName( s, true ); // huh? (Werner)
00083   return t;
00084 }
00085 
00086 void Map::addSheet( Sheet *_sheet )
00087 {
00088   m_lstSheets.append( _sheet );
00089 
00090   m_doc->setModified( true );
00091 
00092   emit sig_addSheet( _sheet );
00093 }
00094 
00095 Sheet *Map::addNewSheet ()
00096 {
00097   Sheet *t = createSheet ();
00098   addSheet (t);
00099   return t;
00100 }
00101 
00102 void Map::moveSheet( const QString & _from, const QString & _to, bool _before )
00103 {
00104   Sheet* sheetfrom = findSheet( _from );
00105   Sheet* sheetto = findSheet( _to );
00106 
00107   int from = m_lstSheets.find( sheetfrom ) ;
00108   int to = m_lstSheets.find( sheetto ) ;
00109   if ( !_before )
00110   ++to;
00111 
00112   if ( to > (int)m_lstSheets.count() )
00113   {
00114     m_lstSheets.append( sheetfrom );
00115     m_lstSheets.take( from );
00116   }
00117   else if ( from < to )
00118   {
00119     m_lstSheets.insert( to, sheetfrom );
00120     m_lstSheets.take( from );
00121   }
00122   else
00123   {
00124     m_lstSheets.take( from );
00125     m_lstSheets.insert( to, sheetfrom );
00126   }
00127 }
00128 
00129 void Map::loadOasisSettings( KoOasisSettings &settings )
00130 {
00131     KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
00132     KoOasisSettings::IndexedMap viewMap = viewSettings.indexedMap( "Views" );
00133     KoOasisSettings::Items firstView = viewMap.entry( 0 );
00134 
00135     KoOasisSettings::NamedMap sheetsMap = firstView.namedMap( "Tables" );
00136     kdDebug()<<" loadOasisSettings( KoOasisSettings &settings ) exist : "<< !sheetsMap.isNull() <<endl;
00137     if ( !sheetsMap.isNull() )
00138     {
00139         QPtrListIterator<Sheet> it( m_lstSheets );
00140         for( ; it.current(); ++it )
00141         {
00142             it.current()->loadOasisSettings( sheetsMap );
00143         }
00144     }
00145 
00146     QString activeSheet = firstView.parseConfigItemString( "ActiveTable" );
00147     kdDebug()<<" loadOasisSettings( KoOasisSettings &settings ) activeSheet :"<<activeSheet<<endl;
00148 
00149     if (!activeSheet.isEmpty())
00150     {
00151         // Used by View's constructor
00152         m_initialActiveSheet = findSheet( activeSheet );
00153     }
00154 
00155 }
00156 
00157 void Map::saveOasisSettings( KoXmlWriter &settingsWriter )
00158 {
00159     settingsWriter.addConfigItem( "ViewId", QString::fromLatin1( "View1" ) );
00160     // Save visual info for the first view, such as active sheet and active cell
00161     // It looks like a hack, but reopening a document creates only one view anyway (David)
00162     View * view = static_cast<View*>( m_doc->views().getFirst());
00163     if ( view ) // no view if embedded document
00164     {
00165         // save current sheet selection before to save marker, otherwise current pos is not saved
00166         view->saveCurrentSheetSelection();
00167         //<config:config-item config:name="ActiveTable" config:type="string">Feuille1</config:config-item>
00168         settingsWriter.addConfigItem( "ActiveTable",  view->activeSheet()->sheetName() );
00169     }
00170 
00171     //<config:config-item-map-named config:name="Tables">
00172     settingsWriter.startElement("config:config-item-map-named" );
00173     settingsWriter.addAttribute("config:name","Tables" );
00174     QPtrListIterator<Sheet> it( m_lstSheets );
00175     for( ; it.current(); ++it )
00176     {
00177         settingsWriter.startElement( "config:config-item-map-entry" );
00178         settingsWriter.addAttribute( "config:name", ( *it )->sheetName() );
00179         if ( view )
00180         {
00181           QPoint marker = view->markerFromSheet( *it );
00182           KoPoint offset = view->offsetFromSheet( *it );
00183           settingsWriter.addConfigItem( "CursorPositionX", marker.x() );
00184           settingsWriter.addConfigItem( "CursorPositionY", marker.y() );
00185           settingsWriter.addConfigItem( "xOffset", offset.x() );
00186           settingsWriter.addConfigItem( "yOffset", offset.y() );
00187         }
00188         it.current()->saveOasisSettings( settingsWriter );
00189         settingsWriter.endElement();
00190     }
00191     settingsWriter.endElement();
00192 }
00193 
00194 
00195 bool Map::saveOasis( KoXmlWriter & xmlWriter, KoGenStyles & mainStyles, KoStore *store, KoXmlWriter* manifestWriter, int &_indexObj, int &_partIndexObj )
00196 {
00197     if ( !m_strPassword.isEmpty() )
00198     {
00199         xmlWriter.addAttribute("table:structure-protected", "true" );
00200         QCString str = KCodecs::base64Encode( m_strPassword );
00201         xmlWriter.addAttribute("table:protection-key", QString( str.data() ) );/* FIXME !!!!*/
00202     }
00203 
00204     GenValidationStyles valStyle;
00205 
00206     KTempFile bodyTmpFile;
00207     //Check that creation of temp file was successful
00208     if (bodyTmpFile.status() != 0)
00209     {
00210         qWarning("Creation of temporary file to store document body failed.");
00211         return false;
00212     }
00213 
00214     bodyTmpFile.setAutoDelete( true );
00215     QFile* tmpFile = bodyTmpFile.file();
00216     KoXmlWriter bodyTmpWriter( tmpFile );
00217 
00218 
00219     QPtrListIterator<Sheet> it( m_lstSheets );
00220     for( ; it.current(); ++it )
00221     {
00222         it.current()->saveOasis( bodyTmpWriter, mainStyles, valStyle, store, manifestWriter, _indexObj, _partIndexObj );
00223     }
00224 
00225     valStyle.writeStyle( xmlWriter );
00226 
00227 
00228     tmpFile->close();
00229     xmlWriter.addCompleteElement( tmpFile );
00230     bodyTmpFile.close();
00231 
00232     return true;
00233 }
00234 
00235 QDomElement Map::save( QDomDocument& doc )
00236 {
00237     QDomElement mymap = doc.createElement( "map" );
00238   // Save visual info for the first view, such as active sheet and active cell
00239   // It looks like a hack, but reopening a document creates only one view anyway (David)
00240   View * view = static_cast<View*>(m_doc->views().getFirst());
00241   if ( view ) // no view if embedded document
00242   {
00243     Canvas * canvas = view->canvasWidget();
00244     mymap.setAttribute( "activeTable",  canvas->activeSheet()->sheetName() );
00245     mymap.setAttribute( "markerColumn", canvas->markerColumn() );
00246     mymap.setAttribute( "markerRow",    canvas->markerRow() );
00247     mymap.setAttribute( "xOffset",      canvas->xOffset() );
00248     mymap.setAttribute( "yOffset",      canvas->yOffset() );
00249   }
00250 
00251   if ( !m_strPassword.isNull() )
00252   {
00253     if ( m_strPassword.size() > 0 )
00254     {
00255       QCString str = KCodecs::base64Encode( m_strPassword );
00256       mymap.setAttribute( "protected", QString( str.data() ) );
00257     }
00258     else
00259       mymap.setAttribute( "protected", "" );
00260   }
00261 
00262   QPtrListIterator<Sheet> it( m_lstSheets );
00263   for( ; it.current(); ++it )
00264   {
00265     QDomElement e = it.current()->saveXML( doc );
00266     if ( e.isNull() )
00267       return e;
00268     mymap.appendChild( e );
00269   }
00270   return mymap;
00271 }
00272 
00273 bool Map::loadOasis( const QDomElement& body, KoOasisLoadingContext& oasisContext, QDict<Style>& styleMap )
00274 {
00275     if ( body.hasAttributeNS( KoXmlNS::table, "structure-protected" ) )
00276     {
00277         QCString passwd( "" );
00278         if ( body.hasAttributeNS( KoXmlNS::table, "protection-key" ) )
00279         {
00280             QString p = body.attributeNS( KoXmlNS::table, "protection-key", QString::null );
00281             QCString str( p.latin1() );
00282             passwd = KCodecs::base64Decode( str );
00283         }
00284         m_strPassword = passwd;
00285     }
00286     QDomNode sheetNode = KoDom::namedItemNS( body, KoXmlNS::table, "table" );
00287 
00288     // sanity check
00289     if ( sheetNode.isNull() ) return false;
00290 
00291     while ( !sheetNode.isNull() )
00292     {
00293         QDomElement sheetElement = sheetNode.toElement();
00294         if( !sheetElement.isNull() )
00295         {
00296             //kdDebug()<<"  Map::loadOasis tableElement is not null \n";
00297             //kdDebug()<<"tableElement.nodeName() :"<<sheetElement.nodeName()<<endl;
00298             if( sheetElement.nodeName() == "table:table" )
00299             {
00300                 if( !sheetElement.attributeNS( KoXmlNS::table, "name", QString::null ).isEmpty() )
00301                 {
00302                     Sheet* sheet = addNewSheet();
00303                     sheet->setSheetName( sheetElement.attributeNS( KoXmlNS::table, "name", QString::null ), true, false );
00304                 }
00305             }
00306         }
00307         sheetNode = sheetNode.nextSibling();
00308     }
00309 
00310     // load the sheet
00311     sheetNode = body.firstChild();
00312     while ( !sheetNode.isNull() )
00313     {
00314         QDomElement sheetElement = sheetNode.toElement();
00315         if( !sheetElement.isNull() )
00316         {
00317             kdDebug()<<"tableElement.nodeName() bis :"<<sheetElement.nodeName()<<endl;
00318             if( sheetElement.nodeName() == "table:table" )
00319             {
00320                 if( !sheetElement.attributeNS( KoXmlNS::table, "name", QString::null ).isEmpty() )
00321                 {
00322                     QString name = sheetElement.attributeNS( KoXmlNS::table, "name", QString::null );
00323                     Sheet* sheet = findSheet( name );
00324                     if( sheet )
00325                         sheet->loadOasis( sheetElement , oasisContext , styleMap);
00326                 }
00327             }
00328         }
00329         sheetNode = sheetNode.nextSibling();
00330     }
00331 
00332     return true;
00333 }
00334 
00335 
00336 bool Map::loadXML( const QDomElement& mymap )
00337 {
00338   QString activeSheet   = mymap.attribute( "activeTable" );
00339   m_initialMarkerColumn = mymap.attribute( "markerColumn" ).toInt();
00340   m_initialMarkerRow    = mymap.attribute( "markerRow" ).toInt();
00341   m_initialXOffset      = mymap.attribute( "xOffset" ).toDouble();
00342   m_initialYOffset      = mymap.attribute( "yOffset" ).toDouble();
00343 
00344   QDomNode n = mymap.firstChild();
00345   if ( n.isNull() )
00346   {
00347       // We need at least one sheet !
00348       doc()->setErrorMessage( i18n("This document has no sheets (tables).") );
00349       return false;
00350   }
00351   while( !n.isNull() )
00352   {
00353     QDomElement e = n.toElement();
00354     if ( !e.isNull() && e.tagName() == "table" )
00355     {
00356       Sheet *t = addNewSheet();
00357       if ( !t->loadXML( e ) )
00358         return false;
00359     }
00360     n = n.nextSibling();
00361   }
00362 
00363   if ( mymap.hasAttribute( "protected" ) )
00364   {
00365     QString passwd = mymap.attribute( "protected" );
00366 
00367     if ( passwd.length() > 0 )
00368     {
00369       QCString str( passwd.latin1() );
00370       m_strPassword = KCodecs::base64Decode( str );
00371     }
00372     else
00373       m_strPassword = QCString( "" );
00374   }
00375 
00376   if (!activeSheet.isEmpty())
00377   {
00378     // Used by View's constructor
00379     m_initialActiveSheet = findSheet( activeSheet );
00380   }
00381 
00382   return true;
00383 }
00384 
00385 void Map::update()
00386 {
00387   QPtrListIterator<Sheet> it( m_lstSheets );
00388   for( ; it.current(); ++it )
00389     it.current()->recalc();
00390 }
00391 
00392 Sheet* Map::findSheet( const QString & _name )
00393 {
00394     Sheet * t;
00395 
00396     for ( t = m_lstSheets.first(); t != 0L; t = m_lstSheets.next() )
00397     {
00398         if ( _name.lower() == t->sheetName().lower() )
00399             return t;
00400     }
00401 
00402     return 0L;
00403 }
00404 
00405 Sheet * Map::nextSheet( Sheet * currentSheet )
00406 {
00407     Sheet * t;
00408 
00409     if( currentSheet == m_lstSheets.last())
00410       return currentSheet;
00411 
00412     for ( t = m_lstSheets.first(); t != 0L; t = m_lstSheets.next() )
00413     {
00414         if ( t  == currentSheet )
00415             return m_lstSheets.next();
00416     }
00417 
00418     return 0L;
00419 }
00420 
00421 Sheet * Map::previousSheet( Sheet * currentSheet )
00422 {
00423     Sheet * t;
00424 
00425     if( currentSheet == m_lstSheets.first())
00426       return currentSheet;
00427 
00428     for ( t = m_lstSheets.first(); t != 0L; t = m_lstSheets.next() )
00429     {
00430         if ( t  == currentSheet )
00431             return m_lstSheets.prev();
00432     }
00433 
00434     return 0L;
00435 }
00436 
00437 bool Map::saveChildren( KoStore * _store )
00438 {
00439   QPtrListIterator<Sheet> it( m_lstSheets );
00440   for( ; it.current(); ++it )
00441   {
00442     // set the child document's url to an internal url (ex: "tar:/0/1")
00443     if ( !it.current()->saveChildren( _store, it.current()->sheetName() ) )
00444       return false;
00445   }
00446   return true;
00447 }
00448 
00449 bool Map::loadChildren( KoStore * _store )
00450 {
00451   QPtrListIterator<Sheet> it( m_lstSheets );
00452   for( ; it.current(); ++it )
00453     if ( !it.current()->loadChildren( _store ) )
00454       return false;
00455 
00456   return true;
00457 }
00458 
00459 DCOPObject * Map::dcopObject()
00460 {
00461     if ( !m_dcop )
00462         m_dcop = new MapIface( this );
00463 
00464     return m_dcop;
00465 }
00466 
00467 void Map::takeSheet( Sheet * sheet )
00468 {
00469     int pos = m_lstSheets.findRef( sheet );
00470     m_lstSheets.take( pos );
00471     m_lstDeletedSheets.append( sheet );
00472 }
00473 
00474 void Map::insertSheet( Sheet * sheet )
00475 {
00476     int pos = m_lstDeletedSheets.findRef( sheet );
00477     if ( pos != -1 )
00478         m_lstDeletedSheets.take( pos );
00479     m_lstSheets.append(sheet);
00480 }
00481 
00482 // FIXME cache this for faster operation
00483 QStringList Map::visibleSheets() const
00484 {
00485     QStringList result;
00486 
00487     QPtrListIterator<Sheet> it( m_lstSheets );
00488     for( ; it; ++it )
00489     {
00490         Sheet* sheet = it.current();
00491         if( !sheet->isHidden() )
00492             result.append( sheet->sheetName() );
00493     }
00494 
00495     return result;
00496 }
00497 
00498 // FIXME cache this for faster operation
00499 QStringList Map::hiddenSheets() const
00500 {
00501     QStringList result;
00502 
00503     QPtrListIterator<Sheet> it( m_lstSheets );
00504     for( ; it; ++it )
00505     {
00506         Sheet* sheet = it.current();
00507         if( sheet->isHidden() )
00508             result.append( sheet->sheetName() );
00509     }
00510 
00511     return result;
00512 }
00513 
00514 #include "kspread_map.moc"
00515 
KDE Home | KDE Accessibility Home | Description of Access Keys