71
73
#include <KoXmlWriter.h>
72
74
#include <KoZoomHandler.h>
73
75
#include <KoShapeSavingContext.h>
76
#include <KoUpdater.h>
77
#include <KoProgressUpdater.h>
75
79
#include "BindingManager.h"
76
80
#include "CalculationSettings.h"
77
81
#include "Canvas.h"
82
#include "CanvasItem.h"
78
83
#include "DependencyManager.h"
79
84
#include "Factory.h"
80
85
#include "Formula.h"
81
87
#include "FunctionModuleRegistry.h"
82
#include "Functions.h"
88
#include "HeaderFooter.h"
83
89
#include "LoadingInfo.h"
84
90
#include "Localization.h"
86
92
#include "NamedAreaManager.h"
93
#include "PrintSettings.h"
87
94
#include "RecalcManager.h"
88
#include "Selection.h"
90
96
#include "SheetPrint.h"
91
#include "SheetView.h"
92
97
#include "StyleManager.h"
95
100
#include "SheetAccessModel.h"
101
#include "BindingModel.h"
104
#include "interfaces/MapAdaptor.h"
105
#include "interfaces/SheetAdaptor.h"
106
#include <QtDBus/QtDBus>
98
#include "kchart/shape/ChartShape.h"
109
#include "plugins/chartshape/ChartShape.h"
99
110
#include "chart/ChartDialog.h"
113
#include "ui/Selection.h"
114
#include "ui/SheetView.h"
101
116
using namespace std;
102
117
using namespace KSpread;
104
static const int CURRENT_SYNTAX_VERSION = 1;
105
// Make sure an appropriate DTD is available in www/koffice/DTD if changing this value
106
static const char * CURRENT_DTD_VERSION = "1.2";
108
typedef QMap<QString, QDomElement> SavedDocParts;
110
119
class Doc::Private
138
144
int Doc::Private::s_docId = 0;
140
146
Doc::Doc(QWidget *parentWidget, QObject* parent, bool singleViewMode)
141
: KoDocument(parentWidget, parent, singleViewMode)
147
: DocBase(parentWidget, parent, singleViewMode)
144
d->map = new Map(this, "Map");
146
d->configLoadFromFile = false;
148
documents().append(this);
150
setComponentData(Factory::global(), false);
151
setTemplateType("kspread_template");
153
d->isLoading = false;
155
// default document properties
156
d->syntaxVersion = CURRENT_SYNTAX_VERSION;
158
d->sheetAccessModel = new SheetAccessModel(d->map);
150
connect(d->map, SIGNAL(sheetAdded(Sheet*)), this, SLOT(sheetAdded(Sheet*)));
151
new MapAdaptor(d->map);
152
QDBusConnection::sessionBus().registerObject('/' + objectName() + '/' + d->map->objectName(), d->map);
160
155
// Init chart shape factory with KSpread's specific configuration panels.
161
156
KoShapeFactoryBase *chartShape = KoShapeRegistry::instance()->value(ChartShapeId);
162
157
if (chartShape) {
163
QList<KoShapeConfigFactoryBase*> panels = ChartDialog::panels(this);
158
QList<KoShapeConfigFactoryBase*> panels = ChartDialog::panels(d->map);
164
159
chartShape->setOptionPanels(panels);
162
connect(d->map, SIGNAL(commandAdded(QUndoCommand *)),
163
this, SLOT(addCommand(QUndoCommand *)));
165
setComponentData(Factory::global(), false);
166
setTemplateType("kspread_template");
167
168
// Load the function modules.
168
FunctionModuleRegistry::instance();
169
FunctionModuleRegistry::instance()->loadFunctionModules();
330
300
return map()->loadChildren(_store);
333
bool Doc::saveOdf(SavingContext &documentContext)
335
ElapsedTime et("OpenDocument Saving", ElapsedTime::PrintOnlyTime);
336
return saveOdfHelper(documentContext, SaveAll);
339
bool Doc::saveOdfHelper(SavingContext & documentContext, SaveFlag saveFlag,
340
QString* /*plainText*/)
343
KoStore * store = documentContext.odfStore.store();
344
KoXmlWriter * manifestWriter = documentContext.odfStore.manifestWriter();
346
/* don't pull focus away from the editor if this is just a background
348
if (!isAutosaving()) {
349
foreach(KoView* view, views())
350
static_cast<View *>(view)->selection()->emitCloseEditor(true);
353
KoStoreDevice dev(store);
354
KoGenStyles mainStyles;//for compile
356
KoXmlWriter* contentWriter = documentContext.odfStore.contentWriter();
358
KoXmlWriter* bodyWriter = documentContext.odfStore.bodyWriter();
359
KoShapeSavingContext savingContext(*bodyWriter, mainStyles, documentContext.embeddedSaver);
361
//todo fixme just add a element for testing saving content.xml
362
bodyWriter->startElement("office:body");
363
bodyWriter->startElement("office:spreadsheet");
366
map()->saveOdf(*contentWriter, savingContext);
368
bodyWriter->endElement(); ////office:spreadsheet
369
bodyWriter->endElement(); ////office:body
371
// Done with writing out the contents to the tempfile, we can now write out the automatic styles
372
mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter);
374
documentContext.odfStore.closeContentWriter();
376
//add manifest line for content.xml
377
manifestWriter->addManifestEntry("content.xml", "text/xml");
379
mainStyles.saveOdfStylesDotXml(store, manifestWriter);
381
if (!store->open("settings.xml"))
384
KoXmlWriter* settingsWriter = KoOdfWriteStore::createOasisXmlWriter(&dev, "office:document-settings");
385
settingsWriter->startElement("office:settings");
386
settingsWriter->startElement("config:config-item-set");
387
settingsWriter->addAttribute("config:name", "view-settings");
389
saveUnitOdf(settingsWriter);
391
saveOdfSettings(*settingsWriter);
393
settingsWriter->endElement(); // config:config-item-set
395
settingsWriter->startElement("config:config-item-set");
396
settingsWriter->addAttribute("config:name", "configuration-settings");
397
settingsWriter->addConfigItem("SpellCheckerIgnoreList", d->spellListIgnoreAll.join(","));
398
settingsWriter->endElement(); // config:config-item-set
399
settingsWriter->endElement(); // office:settings
400
settingsWriter->endElement(); // Root:element
401
settingsWriter->endDocument();
402
delete settingsWriter;
407
if (!savingContext.saveDataCenter(store, manifestWriter)) {
411
manifestWriter->addManifestEntry("settings.xml", "text/xml");
418
void Doc::loadOdfSettings(const KoXmlDocument&settingsDoc)
420
KoOasisSettings settings(settingsDoc);
421
KoOasisSettings::Items viewSettings = settings.itemSet("view-settings");
422
if (!viewSettings.isNull()) {
423
setUnit(KoUnit::unit(viewSettings.parseConfigItemString("unit")));
425
map()->loadOdfSettings(settings);
426
loadOdfIgnoreList(settings);
429
void Doc::saveOdfSettings(KoXmlWriter &settingsWriter)
431
settingsWriter.startElement("config:config-item-map-indexed");
432
settingsWriter.addAttribute("config:name", "Views");
433
settingsWriter.startElement("config:config-item-map-entry");
434
map()->saveOdfSettings(settingsWriter);
435
settingsWriter.endElement();
436
settingsWriter.endElement();
440
void Doc::loadOdfIgnoreList(const KoOasisSettings& settings)
442
KoOasisSettings::Items configurationSettings = settings.itemSet("configuration-settings");
443
if (!configurationSettings.isNull()) {
444
const QString ignorelist = configurationSettings.parseConfigItemString("SpellCheckerIgnoreList");
445
//kDebug()<<" ignorelist :"<<ignorelist;
446
d->spellListIgnoreAll = ignorelist.split(',', QString::SkipEmptyParts);
451
bool Doc::loadOdf(KoOdfReadStore & odfStore)
458
d->spellListIgnoreAll.clear();
460
KoXmlElement content = odfStore.contentDoc().documentElement();
461
KoXmlElement realBody(KoXml::namedItemNS(content, KoXmlNS::office, "body"));
462
if (realBody.isNull()) {
463
setErrorMessage(i18n("Invalid OASIS OpenDocument file. No office:body tag found."));
464
map()->deleteLoadingInfo();
467
KoXmlElement body = KoXml::namedItemNS(realBody, KoXmlNS::office, "spreadsheet");
470
kError(32001) << "No office:spreadsheet found!" << endl;
471
KoXmlElement childElem;
473
forEachElement(childElem, realBody) {
474
localName = childElem.localName();
476
if (localName.isEmpty())
477
setErrorMessage(i18n("Invalid OASIS OpenDocument file. No tag found inside office:body."));
479
setErrorMessage(i18n("This document is not a spreadsheet, but %1. Please try opening it with the appropriate application." , KoDocument::tagNameToDocumentType(localName)));
480
map()->deleteLoadingInfo();
484
KoOdfLoadingContext context(odfStore.styles(), odfStore.store());
486
// TODO check versions and mimetypes etc.
488
// all <sheet:sheet> goes to workbook
489
if (!map()->loadOdf(body, context)) {
490
d->isLoading = false;
491
map()->deleteLoadingInfo();
495
if (!odfStore.settingsDoc().isNull()) {
496
loadOdfSettings(odfStore.settingsDoc());
499
emit sigProgress(-1);
501
//display loading time
502
kDebug(36001) << "Loading took" << (float)(dt.elapsed()) / 1000.0 << " seconds";
507
304
bool Doc::loadXML(const KoXmlDocument& doc, KoStore*)
306
QPointer<KoUpdater> updater;
307
if (progressUpdater()) {
308
updater = progressUpdater()->startSubtask(1, "KSpread::Doc::loadXML");
309
updater->setProgress(0);
514
312
d->spellListIgnoreAll.clear();
516
314
KoXmlElement spread = doc.documentElement();
518
316
if (spread.attribute("mime") != "application/x-kspread" && spread.attribute("mime") != "application/vnd.kde.kspread") {
519
d->isLoading = false;
520
317
setErrorMessage(i18n("Invalid document. Expected mimetype application/x-kspread or application/vnd.kde.kspread, got %1" , spread.attribute("mime")));
525
322
int version = spread.attribute("syntaxVersion").toInt(&ok);
526
d->syntaxVersion = ok ? version : 0;
527
if (d->syntaxVersion > CURRENT_SYNTAX_VERSION) {
323
map()->setSyntaxVersion(ok ? version : 0);
324
if (map()->syntaxVersion() > CURRENT_SYNTAX_VERSION) {
528
325
int ret = KMessageBox::warningContinueCancel(
529
326
0, i18n("This document was created with a newer version of KSpread (syntax version: %1)\n"
530
"When you open it with this version of KSpread, some information may be lost.", d->syntaxVersion),
327
"When you open it with this version of KSpread, some information may be lost.", map()->syntaxVersion()),
531
328
i18n("File Format Mismatch"), KStandardGuiItem::cont());
532
329
if (ret == KMessageBox::Cancel) {
533
330
setErrorMessage("USER_CANCELED");
736
521
return d->spellListIgnoreAll;
739
void Doc::newZoomAndResolution(bool updateViews, bool /*forPrint*/)
744
emit sig_refreshView();
748
524
void Doc::paintContent(QPainter& painter, const QRect& rect)
750
paintContent(painter, rect, map()->sheet(0), false);
753
void Doc::paintContent(QPainter& painter, const QRect& rect, Sheet* sheet, bool drawCursor)
755
Q_UNUSED(drawCursor);
757
QPixmap thumbnail = sheet->generateThumbnail(rect.size());
758
painter.drawPixmap(rect, thumbnail);
761
void Doc::paintUpdates()
763
foreach(KoView* view, views()) {
764
static_cast<View *>(view)->paintUpdates();
768
void Doc::paintRegion(QPainter &painter, const QRectF &viewRegion,
769
View* view, const QRect &cellRegion, const Sheet* sheet)
771
// cellRegion has cell coordinates (col,row) while viewRegion has
772
// world coordinates. cellRegion is the cells to update and
773
// viewRegion is the area actually onscreen.
775
if (cellRegion.left() <= 0 || cellRegion.top() <= 0)
526
paintContent(painter, rect, 0);
529
void Doc::paintContent(QPainter& painter, const QRect& rect, Sheet* _sheet)
531
if (rect.isEmpty()) {
778
const QRectF viewRegionF(viewRegion.left(), viewRegion.right(), viewRegion.width(), viewRegion.height());
780
// Get the world coordinates of the upper left corner of the
781
// cellRegion The view is 0, when cellRegion is called from
782
// paintContent, which itself is only called, when we should paint
783
// the output for INACTIVE embedded view. If inactive embedded,
784
// then there is no view and we alwas start at top/left, so the
788
if (view == 0) //Most propably we are embedded and inactive, so no offset
789
topLeft = QPointF(sheet->columnPosition(cellRegion.left()),
790
sheet->rowPosition(cellRegion.top()));
792
topLeft = QPointF(sheet->columnPosition(cellRegion.left()) - view->canvasWidget()->xOffset(),
793
sheet->rowPosition(cellRegion.top()) - view->canvasWidget()->yOffset());
795
SheetView sheetView(sheet); // FIXME Stefan: make member, otherwise cache lost
797
sheetView.setPaintDevice(view->canvasWidget());
798
sheetView.setViewConverter(view->zoomHandler());
800
sheetView.setPaintCellRange(cellRegion);
801
sheetView.paintCells(view ? view->canvasWidget() : 0, painter, viewRegionF, topLeft);
804
void Doc::addStringCompletion(const QString &stringCompletion)
806
if (d->listCompletion.items().contains(stringCompletion) == 0)
807
d->listCompletion.addItem(stringCompletion);
810
void Doc::refreshInterface()
812
emit sig_refreshView();
534
Sheet *const sheet = _sheet ? _sheet : d->map->sheet(0);
536
const KoPageLayout pageLayout = sheet->printSettings()->pageLayout();
537
QPixmap thumbnail(pageLayout.width, pageLayout.height);
538
thumbnail.fill(Qt::white);
540
SheetView sheetView(sheet);
542
const qreal zoom = sheet->printSettings()->zoom();
543
KoZoomHandler zoomHandler;
544
zoomHandler.setZoom(zoom);
545
sheetView.setViewConverter(&zoomHandler);
547
sheetView.setPaintCellRange(sheet->print()->cellRange(1)); // first page
549
QPainter pixmapPainter(&thumbnail);
550
pixmapPainter.setClipRect(QRect(QPoint(0, 0), thumbnail.size()));
551
sheetView.paintCells(pixmapPainter, QRect(0, 0, pageLayout.width, pageLayout.height), QPointF(0,0));
553
// The pixmap gets scaled to fit the rectangle.
554
painter.drawPixmap(rect & QRect(0, 0, 100, 100), thumbnail);
557
void Doc::updateAllViews()
815
562
void Doc::updateBorderButton()
846
593
return d->configLoadFromFile;
849
void Doc::repaint(const QRectF& rect)
852
foreach(KoView* koview, views()) {
853
const View* view = static_cast<View*>(koview);
854
Canvas* canvas = view->canvasWidget();
856
r = view->zoomHandler()->documentToView(rect);
857
r.translate(-canvas->xOffset() * view->zoomHandler()->zoomedResolutionX(),
858
-canvas->yOffset() * view->zoomHandler()->zoomedResolutionY());
859
canvas->update(r.toRect());
863
SheetAccessModel *Doc::sheetAccessModel() const
865
return d->sheetAccessModel;
868
#if 0 // UNDOREDOLIMIT
869
int Doc::undoRedoLimit() const
871
return d->commandHistory->undoLimit();
874
void Doc::setUndoRedoLimit(int val)
876
d->commandHistory->setUndoLimit(val);
877
d->commandHistory->setRedoLimit(val);
596
void Doc::sheetAdded(Sheet* sheet)
598
new SheetAdaptor(sheet);
599
QString dbusPath('/' + sheet->map()->objectName() + '/' + objectName());
600
if (sheet->parent()) {
601
dbusPath.prepend('/' + sheet->parent()->objectName());
603
QDBusConnection::sessionBus().registerObject(dbusPath, sheet);
607
void Doc::saveOdfViewSettings(KoXmlWriter& settingsWriter)
609
if (!views().isEmpty()) { // no view if embedded document
610
// Save visual info for the first view, such as active sheet and active cell
611
// It looks like a hack, but reopening a document creates only one view anyway (David)
612
View *const view = static_cast<View*>(views().first());
613
// save current sheet selection before to save marker, otherwise current pos is not saved
614
view->saveCurrentSheetSelection();
615
//<config:config-item config:name="ActiveTable" config:type="string">Feuille1</config:config-item>
616
if (Sheet *sheet = view->activeSheet()) {
617
settingsWriter.addConfigItem("ActiveTable", sheet->sheetName());
622
void Doc::saveOdfViewSheetSettings(Sheet *sheet, KoXmlWriter &settingsWriter)
624
if (!views().isEmpty()) {
625
View *const view = static_cast<View*>(views().first());
626
QPoint marker = view->markerFromSheet(sheet);
627
QPointF offset = view->offsetFromSheet(sheet);
628
settingsWriter.addConfigItem("CursorPositionX", marker.x() - 1);
629
settingsWriter.addConfigItem("CursorPositionY", marker.y() - 1);
630
settingsWriter.addConfigItem("xOffset", offset.x());
631
settingsWriter.addConfigItem("yOffset", offset.y());
635
bool Doc::saveOdfHelper(SavingContext &documentContext, SaveFlag saveFlag, QString *plainText)
637
/* don't pull focus away from the editor if this is just a background
639
if (!isAutosaving()) {
640
foreach(KoView* view, views())
641
static_cast<View *>(view)->selection()->emitCloseEditor(true);
644
return DocBase::saveOdfHelper(documentContext, saveFlag, plainText);
881
647
#include "Doc.moc"