kspread

kspread_map.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include <stdlib.h>
21#include <time.h>
22
23#include <tqfile.h>
24
25#include <kmdcodec.h>
26#include <tdetempfile.h>
27
28#include <KoDom.h>
29#include <KoGenStyles.h>
30#include <KoOasisSettings.h>
31#include <KoOasisStyles.h>
32#include <KoXmlNS.h>
33#include <KoXmlWriter.h>
34
35#include "kspread_canvas.h"
36#include "kspread_doc.h"
37#include "kspread_genvalidationstyle.h"
38#include "kspread_locale.h"
39#include "kspread_sheet.h"
40#include "kspread_style.h"
41#include "kspread_style_manager.h"
42#include "kspread_view.h"
43#include "KSpreadMapIface.h"
44
45#include "kspread_map.h"
46
47using namespace KSpread;
48
49bool Map::respectCase = true;
50
51Map::Map ( Doc* doc, const char* name)
52 : TQObject( doc, name ),
53 m_doc( doc ),
54 m_initialActiveSheet( 0 ),
55 m_initialMarkerColumn( 0 ),
56 m_initialMarkerRow( 0 ),
57 m_initialXOffset(0.0),
58 m_initialYOffset(0.0),
59 tableId (1),
60 m_dcop( 0 )
61{
62 m_lstSheets.setAutoDelete( true );
63}
64
66{
67 delete m_dcop;
68}
69
70Doc* Map::doc() const
71{
72 return m_doc;
73}
74
75void Map::setProtected( TQCString const & passwd )
76{
77 m_strPassword = passwd;
78}
79
81{
82 TQString s( i18n("Sheet%1") );
83 s = s.arg( tableId++ );
84 Sheet *t = new Sheet ( this, s , s.utf8());
85 t->setSheetName( s, true ); // huh? (Werner)
86 return t;
87}
88
89void Map::addSheet( Sheet *_sheet )
90{
91 m_lstSheets.append( _sheet );
92
93 m_doc->setModified( true );
94
95 emit sig_addSheet( _sheet );
96}
97
99{
100 Sheet *t = createSheet ();
101 addSheet (t);
102 return t;
103}
104
105void Map::moveSheet( const TQString & _from, const TQString & _to, bool _before )
106{
107 Sheet* sheetfrom = findSheet( _from );
108 Sheet* sheetto = findSheet( _to );
109
110 int from = m_lstSheets.find( sheetfrom ) ;
111 int to = m_lstSheets.find( sheetto ) ;
112 if ( !_before )
113 ++to;
114
115 if ( to > (int)m_lstSheets.count() )
116 {
117 m_lstSheets.append( sheetfrom );
118 m_lstSheets.take( from );
119 }
120 else if ( from < to )
121 {
122 m_lstSheets.insert( to, sheetfrom );
123 m_lstSheets.take( from );
124 }
125 else
126 {
127 m_lstSheets.take( from );
128 m_lstSheets.insert( to, sheetfrom );
129 }
130}
131
132void Map::loadOasisSettings( KoOasisSettings &settings )
133{
134 KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
135 KoOasisSettings::IndexedMap viewMap = viewSettings.indexedMap( "Views" );
136 KoOasisSettings::Items firstView = viewMap.entry( 0 );
137
138 KoOasisSettings::NamedMap sheetsMap = firstView.namedMap( "Tables" );
139 kdDebug()<<" loadOasisSettings( KoOasisSettings &settings ) exist : "<< !sheetsMap.isNull() <<endl;
140 if ( !sheetsMap.isNull() )
141 {
142 TQPtrListIterator<Sheet> it( m_lstSheets );
143 for( ; it.current(); ++it )
144 {
145 it.current()->loadOasisSettings( sheetsMap );
146 }
147 }
148
149 TQString activeSheet = firstView.parseConfigItemString( "ActiveTable" );
150 kdDebug()<<" loadOasisSettings( KoOasisSettings &settings ) activeSheet :"<<activeSheet<<endl;
151
152 if (!activeSheet.isEmpty())
153 {
154 // Used by View's constructor
155 m_initialActiveSheet = findSheet( activeSheet );
156 }
157
158}
159
160void Map::saveOasisSettings( KoXmlWriter &settingsWriter )
161{
162 settingsWriter.addConfigItem( "ViewId", TQString::fromLatin1( "View1" ) );
163 // Save visual info for the first view, such as active sheet and active cell
164 // It looks like a hack, but reopening a document creates only one view anyway (David)
165 View * view = m_doc->views().isEmpty() ? 0 : dynamic_cast<View*>(m_doc->views().getFirst());
166 if ( view ) // no view if embedded document
167 {
168 // save current sheet selection before to save marker, otherwise current pos is not saved
170 //<config:config-item config:name="ActiveTable" config:type="string">Feuille1</config:config-item>
171 settingsWriter.addConfigItem( "ActiveTable", view->activeSheet()->sheetName() );
172 }
173
174 //<config:config-item-map-named config:name="Tables">
175 settingsWriter.startElement("config:config-item-map-named" );
176 settingsWriter.addAttribute("config:name","Tables" );
177 TQPtrListIterator<Sheet> it( m_lstSheets );
178 for( ; it.current(); ++it )
179 {
180 settingsWriter.startElement( "config:config-item-map-entry" );
181 settingsWriter.addAttribute( "config:name", ( *it )->sheetName() );
182 if ( view )
183 {
184 TQPoint marker = view->markerFromSheet( *it );
185 KoPoint offset = view->offsetFromSheet( *it );
186 settingsWriter.addConfigItem( "CursorPositionX", marker.x() );
187 settingsWriter.addConfigItem( "CursorPositionY", marker.y() );
188 settingsWriter.addConfigItem( "xOffset", offset.x() );
189 settingsWriter.addConfigItem( "yOffset", offset.y() );
190 }
191 it.current()->saveOasisSettings( settingsWriter );
192 settingsWriter.endElement();
193 }
194 settingsWriter.endElement();
195}
196
197
198bool Map::saveOasis( KoXmlWriter & xmlWriter, KoGenStyles & mainStyles, KoStore *store, KoXmlWriter* manifestWriter, int &_indexObj, int &_partIndexObj )
199{
200 if ( !m_strPassword.isEmpty() )
201 {
202 xmlWriter.addAttribute("table:structure-protected", "true" );
203 TQCString str = KCodecs::base64Encode( m_strPassword );
204 xmlWriter.addAttribute("table:protection-key", TQString( str.data() ) );/* FIXME !!!!*/
205 }
206
207 GenValidationStyles valStyle;
208
209 KTempFile bodyTmpFile;
210 //Check that creation of temp file was successful
211 if (bodyTmpFile.status() != 0)
212 {
213 tqWarning("Creation of temporary file to store document body failed.");
214 return false;
215 }
216
217 bodyTmpFile.setAutoDelete( true );
218 TQFile* tmpFile = bodyTmpFile.file();
219 KoXmlWriter bodyTmpWriter( tmpFile );
220
221
222 TQPtrListIterator<Sheet> it( m_lstSheets );
223 for( ; it.current(); ++it )
224 {
225 it.current()->saveOasis( bodyTmpWriter, mainStyles, valStyle, store, manifestWriter, _indexObj, _partIndexObj );
226 }
227
228 valStyle.writeStyle( xmlWriter );
229
230
231 tmpFile->close();
232 xmlWriter.addCompleteElement( tmpFile );
233 bodyTmpFile.close();
234
235 return true;
236}
237
238TQDomElement Map::save( TQDomDocument& doc )
239{
240 TQDomElement mymap = doc.createElement( "map" );
241 // Save visual info for the first view, such as active sheet and active cell
242 // It looks like a hack, but reopening a document creates only one view anyway (David)
243 View * view = static_cast<View*>(m_doc->views().getFirst());
244 if ( view ) // no view if embedded document
245 {
246 Canvas * canvas = view->canvasWidget();
247 mymap.setAttribute( "activeTable", canvas->activeSheet()->sheetName() );
248 mymap.setAttribute( "markerColumn", canvas->markerColumn() );
249 mymap.setAttribute( "markerRow", canvas->markerRow() );
250 mymap.setAttribute( "xOffset", canvas->xOffset() );
251 mymap.setAttribute( "yOffset", canvas->yOffset() );
252 }
253
254 if ( !m_strPassword.isNull() )
255 {
256 if ( m_strPassword.size() > 0 )
257 {
258 TQCString str = KCodecs::base64Encode( m_strPassword );
259 mymap.setAttribute( "protected", TQString( str.data() ) );
260 }
261 else
262 mymap.setAttribute( "protected", "" );
263 }
264
265 TQPtrListIterator<Sheet> it( m_lstSheets );
266 for( ; it.current(); ++it )
267 {
268 TQDomElement e = it.current()->saveXML( doc );
269 if ( e.isNull() )
270 return e;
271 mymap.appendChild( e );
272 }
273 return mymap;
274}
275
276bool Map::loadOasis( const TQDomElement& body, KoOasisLoadingContext& oasisContext )
277{
278 if ( body.hasAttributeNS( KoXmlNS::table, "structure-protected" ) )
279 {
280 TQCString passwd( "" );
281 if ( body.hasAttributeNS( KoXmlNS::table, "protection-key" ) )
282 {
283 TQString p = body.attributeNS( KoXmlNS::table, "protection-key", TQString() );
284 TQCString str( p.latin1() );
285 passwd = KCodecs::base64Decode( str );
286 }
287 m_strPassword = passwd;
288 }
289 TQDomNode sheetNode = KoDom::namedItemNS( body, KoXmlNS::table, "table" );
290
291 // sanity check
292 if ( sheetNode.isNull() ) return false;
293
294 while ( !sheetNode.isNull() )
295 {
296 TQDomElement sheetElement = sheetNode.toElement();
297 if( !sheetElement.isNull() )
298 {
299 //kdDebug()<<" Map::loadOasis tableElement is not null \n";
300 //kdDebug()<<"tableElement.nodeName() :"<<sheetElement.nodeName()<<endl;
301 if( sheetElement.nodeName() == "table:table" )
302 {
303 if( !sheetElement.attributeNS( KoXmlNS::table, "name", TQString() ).isEmpty() )
304 {
305 Sheet* sheet = addNewSheet();
306 sheet->setSheetName( sheetElement.attributeNS( KoXmlNS::table, "name", TQString() ), true, false );
307 }
308 }
309 }
310 sheetNode = sheetNode.nextSibling();
311 }
312
313 //pre-load auto styles
314 TQDict<Style> autoStyles = doc()->styleManager()->loadOasisAutoStyles( oasisContext.oasisStyles() );
315
316 // load the sheet
317 sheetNode = body.firstChild();
318 while ( !sheetNode.isNull() )
319 {
320 TQDomElement sheetElement = sheetNode.toElement();
321 if( !sheetElement.isNull() )
322 {
323 //kdDebug()<<"tableElement.nodeName() bis :"<<sheetElement.nodeName()<<endl;
324 if( sheetElement.nodeName() == "table:table" )
325 {
326 if( !sheetElement.attributeNS( KoXmlNS::table, "name", TQString() ).isEmpty() )
327 {
328 TQString name = sheetElement.attributeNS( KoXmlNS::table, "name", TQString() );
329 Sheet* sheet = findSheet( name );
330 if( sheet )
331 sheet->loadOasis( sheetElement, oasisContext, autoStyles );
332 }
333 }
334 }
335 sheetNode = sheetNode.nextSibling();
336 }
337
338 //delete any styles which were not used
339 doc()->styleManager()->releaseUnusedAutoStyles( autoStyles );
340
341 return true;
342}
343
344
345bool Map::loadXML( const TQDomElement& mymap )
346{
347 TQString activeSheet = mymap.attribute( "activeTable" );
348 m_initialMarkerColumn = mymap.attribute( "markerColumn" ).toInt();
349 m_initialMarkerRow = mymap.attribute( "markerRow" ).toInt();
350 m_initialXOffset = mymap.attribute( "xOffset" ).toDouble();
351 m_initialYOffset = mymap.attribute( "yOffset" ).toDouble();
352
353 TQDomNode n = mymap.firstChild();
354 if ( n.isNull() )
355 {
356 // We need at least one sheet !
357 doc()->setErrorMessage( i18n("This document has no sheets (tables).") );
358 return false;
359 }
360 while( !n.isNull() )
361 {
362 TQDomElement e = n.toElement();
363 if ( !e.isNull() && e.tagName() == "table" )
364 {
365 Sheet *t = addNewSheet();
366 if ( !t->loadXML( e ) )
367 return false;
368 }
369 n = n.nextSibling();
370 }
371
372 if ( mymap.hasAttribute( "protected" ) )
373 {
374 TQString passwd = mymap.attribute( "protected" );
375
376 if ( passwd.length() > 0 )
377 {
378 TQCString str( passwd.latin1() );
379 m_strPassword = KCodecs::base64Decode( str );
380 }
381 else
382 m_strPassword = TQCString( "" );
383 }
384
385 if (!activeSheet.isEmpty())
386 {
387 // Used by View's constructor
388 m_initialActiveSheet = findSheet( activeSheet );
389 }
390
391 return true;
392}
393
394void Map::update()
395{
396 TQPtrListIterator<Sheet> it( m_lstSheets );
397 for( ; it.current(); ++it )
398 it.current()->recalc();
399}
400
401Sheet* Map::findSheet( const TQString & _name )
402{
403 Sheet * t;
404
405 for ( t = m_lstSheets.first(); t != 0L; t = m_lstSheets.next() )
406 {
407 if ( _name.lower() == t->sheetName().lower() )
408 return t;
409 }
410
411 return 0L;
412}
413
414Sheet * Map::nextSheet( Sheet * currentSheet )
415{
416 Sheet * t;
417
418 if( currentSheet == m_lstSheets.last())
419 return currentSheet;
420
421 for ( t = m_lstSheets.first(); t != 0L; t = m_lstSheets.next() )
422 {
423 if ( t == currentSheet )
424 return m_lstSheets.next();
425 }
426
427 return 0L;
428}
429
430Sheet * Map::previousSheet( Sheet * currentSheet )
431{
432 Sheet * t;
433
434 if( currentSheet == m_lstSheets.first())
435 return currentSheet;
436
437 for ( t = m_lstSheets.first(); t != 0L; t = m_lstSheets.next() )
438 {
439 if ( t == currentSheet )
440 return m_lstSheets.prev();
441 }
442
443 return 0L;
444}
445
446bool Map::saveChildren( KoStore * _store )
447{
448 TQPtrListIterator<Sheet> it( m_lstSheets );
449 for( ; it.current(); ++it )
450 {
451 // set the child document's url to an internal url (ex: "tar:/0/1")
452 if ( !it.current()->saveChildren( _store, it.current()->sheetName() ) )
453 return false;
454 }
455 return true;
456}
457
458bool Map::loadChildren( KoStore * _store )
459{
460 TQPtrListIterator<Sheet> it( m_lstSheets );
461 for( ; it.current(); ++it )
462 if ( !it.current()->loadChildren( _store ) )
463 return false;
464
465 return true;
466}
467
468DCOPObject * Map::dcopObject()
469{
470 if ( !m_dcop )
471 m_dcop = new MapIface( this );
472
473 return m_dcop;
474}
475
476void Map::takeSheet( Sheet * sheet )
477{
478 int pos = m_lstSheets.findRef( sheet );
479 m_lstSheets.take( pos );
480 m_lstDeletedSheets.append( sheet );
481}
482
483void Map::insertSheet( Sheet * sheet )
484{
485 int pos = m_lstDeletedSheets.findRef( sheet );
486 if ( pos != -1 )
487 m_lstDeletedSheets.take( pos );
488 m_lstSheets.append(sheet);
489}
490
491// FIXME cache this for faster operation
492TQStringList Map::visibleSheets() const
493{
494 TQStringList result;
495
496 TQPtrListIterator<Sheet> it( m_lstSheets );
497 for( ; it; ++it )
498 {
499 Sheet* sheet = it.current();
500 if( !sheet->isHidden() )
501 result.append( sheet->sheetName() );
502 }
503
504 return result;
505}
506
507// FIXME cache this for faster operation
508TQStringList Map::hiddenSheets() const
509{
510 TQStringList result;
511
512 TQPtrListIterator<Sheet> it( m_lstSheets );
513 for( ; it; ++it )
514 {
515 Sheet* sheet = it.current();
516 if( sheet->isHidden() )
517 result.append( sheet->sheetName() );
518 }
519
520 return result;
521}
522
523#include "kspread_map.moc"
524
int markerRow() const
convenience function.
int markerColumn() const
convenience function.
double yOffset() const
Sheet * activeSheet() const
double xOffset() const
This class holds the data that makes up a spreadsheet.
Definition kspread_doc.h:98
Sheet * findSheet(const TQString &_name)
searches for a sheet named _name
virtual DCOPObject * dcopObject()
Needed for the printing Extension KOffice::Print.
Sheet * addNewSheet()
add a new sheet to the map, returning a pointer to it
void addSheet(Sheet *_sheet)
add sheet to the map, making it active
void sig_addSheet(Sheet *_table)
Emitted if a new table is added to the document.
virtual ~Map()
This deletes all sheets contained in this map.
Map(Doc *doc, const char *name=0)
Created an empty map.
Sheet * createSheet()
void moveSheet(const TQString &_from, const TQString &_to, bool _before=true)
The sheet named _from is being moved to the sheet _to.
Sheet * nextSheet()
Call firstSheet first.
KoPoint offsetFromSheet(Sheet *sheet) const
Canvas * canvasWidget() const
Returns the canvas of the view.
TQPoint markerFromSheet(Sheet *sheet) const
void saveCurrentSheetSelection()
Save current sheet selection.
The KSpread namespace.
Definition commands.h:39