diff --git a/gemini/DocumentManager.cpp b/gemini/DocumentManager.cpp index 7ef77ec6461..4ebda57c2e6 100644 --- a/gemini/DocumentManager.cpp +++ b/gemini/DocumentManager.cpp @@ -1,299 +1,300 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "DocumentManager.h" #include "Settings.h" #include "RecentFileManager.h" #include "ProgressProxy.h" #include #include #include #include #include #include #include #include class DocumentManager::Private { public: Private() : proxy(0) , document(0) , part(0) , settingsManager(0) , recentFileManager(0) , importingDocument(false) , temporaryFile(false) { } ProgressProxy* proxy; QPointer document; QPointer part; Settings* settingsManager; RecentFileManager* recentFileManager; QString saveAsFilename; QString openDocumentFilename; bool importingDocument; QVariantMap newDocOptions; bool temporaryFile; }; DocumentManager *DocumentManager::sm_instance = 0; KoDocument* DocumentManager::document() const { return d->document; } QObject* DocumentManager::doc() const { return d->document; } KoPart* DocumentManager::part(const QString& type) { Q_UNUSED(type) if (!d->part) d->part = new KWPart(this); return d->part; } ProgressProxy* DocumentManager::progressProxy() const { return d->proxy; } Settings* DocumentManager::settingsManager() const { return d->settingsManager; } void DocumentManager::setSettingsManager(Settings* newManager) { d->settingsManager = newManager; } void DocumentManager::setDocAndPart(KoDocument* document, KoPart* part) { d->document = document; d->part = part; d->temporaryFile = false; emit documentChanged(); + connect(document, SIGNAL(destroyed()), SIGNAL(aboutToDeleteDocument())); } RecentFileManager* DocumentManager::recentFileManager() const { return d->recentFileManager; } bool DocumentManager::isTemporaryFile() const { if(d->document->url().isEmpty()) { return true; } return d->temporaryFile; } void DocumentManager::newDocument(int width, int height, float resolution) { Q_UNUSED(width) Q_UNUSED(height) Q_UNUSED(resolution) closeDocument(); QTimer::singleShot(300, this, SLOT(delayedNewDocument())); } void DocumentManager::newDocument(const QVariantMap& options) { closeDocument(); d->newDocOptions = options; QTimer::singleShot(300, this, SLOT(delayedNewDocument())); } void DocumentManager::delayedNewDocument() { QString filetype; if(d->newDocOptions.value("type", WORDS_MIME_TYPE).toString() == WORDS_MIME_TYPE) { filetype = "odt"; d->document = new KWDocument(part(WORDS_MIME_TYPE)); } else { filetype = "odp"; d->document = new KPrDocument(part(STAGE_MIME_TYPE)); } d->document->setProgressProxy(d->proxy); d->document->setSaveInBatchMode(true); part()->setDocument(d->document); + connect(d->document, SIGNAL(destroyed()), SIGNAL(aboutToDeleteDocument())); if(d->newDocOptions.isEmpty()) { //d->document->newImage("Untitled", d->newDocWidth, d->newDocHeight, KoColorSpaceRegistry::instance()->rgb8()); //d->document->image()->setResolution(d->newDocResolution, d->newDocResolution); //d->document->setUrl(QUrl("Untitled.kra")); } else { QString name = d->newDocOptions.value("name", "Untitled").toString(); //int width = d->newDocOptions.value("width").toInt(); //int height = d->newDocOptions.value("height").toInt(); // internal resolution is pixels per point, not ppi //float res = d->newDocOptions.value("resolution", 72.0f).toFloat() / 72.0f; QString colorModelId = d->newDocOptions.value("colorModelId").toString(); QString colorDepthId = d->newDocOptions.value("colorDepthId").toString(); QString colorProfileId = d->newDocOptions.value("colorProfileId").toString(); const KoColorSpace* profile; if(colorModelId.isEmpty() || colorDepthId.isEmpty() || colorProfileId.isEmpty()) { profile = KoColorSpaceRegistry::instance()->rgb8(); } else { profile = KoColorSpaceRegistry::instance()->colorSpace(colorModelId, colorDepthId, colorProfileId); } QColor background = d->newDocOptions.value("backgroundColor", QColor("white")).value(); background.setAlphaF(d->newDocOptions.value("backgroundOpacity", 1.0f).toFloat()); KoColor bg(background, profile); d->document->setUrl(QUrl(QString("Untitled.").append(filetype))); } d->temporaryFile = true; emit documentChanged(); } void DocumentManager::openDocument(const QString& document, bool import) { closeDocument(); d->openDocumentFilename = document; d->importingDocument = import; - QTimer::singleShot(300, this, SLOT(delayedOpenDocument())); + QTimer::singleShot(0, this, SLOT(delayedOpenDocument())); } void DocumentManager::delayedOpenDocument() { d->document = 0; QMimeDatabase db; QMimeType mimeType = db.mimeTypeForUrl(QUrl::fromLocalFile(d->openDocumentFilename)); KoDocumentEntry documentEntry = KoDocumentEntry::queryByMimeType(mimeType.name()); d->part = documentEntry.createKoPart(); if (d->part) { d->document = d->part->document(); d->document->setProgressProxy(d->proxy); d->document->setSaveInBatchMode(true); d->document->setModified(false); if (d->importingDocument) d->document->importDocument(QUrl::fromLocalFile(d->openDocumentFilename)); else d->document->openUrl(QUrl::fromLocalFile(d->openDocumentFilename)); d->recentFileManager->addRecent(d->openDocumentFilename); d->temporaryFile = false; } emit documentChanged(); } void DocumentManager::closeDocument() { if (d->document) { - emit aboutToDeleteDocument(); d->document->closeUrl(false); - //d->document->deleteLater(); + delete d->document; d->document = 0; } } bool DocumentManager::save() { if (d->document->save()) { d->recentFileManager->addRecent(d->document->url().toLocalFile()); d->settingsManager->setCurrentFile(d->document->url().toLocalFile()); emit documentSaved(); return true; } return false; } void DocumentManager::saveAs(const QString &filename, const QString &mimetype) { d->document->setOutputMimeType(mimetype.toAscii()); d->saveAsFilename = filename; // Yes. This is a massive hack. Basically, we need to wait a little while, to ensure // the save call happens late enough for a variety of UI things to happen first. // A second seems like a long time, but well, we do have file system interaction here, // so for now, we can get away with it. - QTimer::singleShot(300, this, SLOT(delayedSaveAs())); + QTimer::singleShot(0, this, SLOT(delayedSaveAs())); } void DocumentManager::delayedSaveAs() { d->document->saveAs(QUrl::fromLocalFile(d->saveAsFilename)); d->settingsManager->setCurrentFile(d->saveAsFilename); d->recentFileManager->addRecent(d->saveAsFilename); emit documentSaved(); } void DocumentManager::reload() { QUrl url = d->document->url(); closeDocument(); d->openDocumentFilename = url.toLocalFile(); QTimer::singleShot(0, this, SLOT(delayedOpenDocument())); } void DocumentManager::setTemporaryFile(bool temp) { d->temporaryFile = temp; emit documentSaved(); } DocumentManager* DocumentManager::instance() { if (!sm_instance) { sm_instance = new DocumentManager(QCoreApplication::instance()); } return sm_instance; } DocumentManager::DocumentManager(QObject* parent) : QObject(parent), d(new Private) { d->proxy = new ProgressProxy(this); d->recentFileManager = new RecentFileManager(this); } DocumentManager::~DocumentManager() { delete d; } diff --git a/gemini/MainWindow.cpp b/gemini/MainWindow.cpp index b984ac86900..d26cb2d2b67 100644 --- a/gemini/MainWindow.cpp +++ b/gemini/MainWindow.cpp @@ -1,874 +1,868 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt * Copyright (C) 2013 Dan Leinir Turthra Jensen * * 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 "MainWindow.h" #include #include "desktopviewproxy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // CALLIGRA_OLD_PLUGIN_METADATA #include "PropertyContainer.h" #include "RecentFileManager.h" #include "DocumentManager.h" #include "QmlGlobalEngine.h" #include "Settings.h" #include "Theme.h" #include "DocumentListModel.h" #include "Constants.h" #include "SimpleTouchArea.h" #include "ToolManager.h" #include "ParagraphStylesModel.h" #include "KeyboardModel.h" #include "ScribbleArea.h" #include "RecentImageImageProvider.h" #include "RecentFilesModel.h" #include "TemplatesModel.h" #include "CloudAccountsModel.h" #ifdef Q_OS_WIN // Slate mode/docked detection stuff #include #define SM_CONVERTIBLESLATEMODE 0x2003 #define SM_SYSTEMDOCKED 0x2004 #endif class MainWindow::Private { public: Private(MainWindow* qq) : q(qq) , allowClose(true) , touchView(0) , desktopView(0) , currentView(0) , settings(0) , slateMode(false) , docked(false) , touchKoView(0) , touchEventReceiver(0) , desktopKoView(0) , desktopViewProxy(0) , forceDesktop(false) , forceTouch(false) , temporaryFile(false) , syncObject(0) , toDesktop(0) , toTouch(0) , switcher(0) , alternativeSaveAction(0) { #ifdef Q_OS_WIN // slateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); // docked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0); #endif fullScreenThrottle = new QTimer(qq); fullScreenThrottle->setInterval(500); fullScreenThrottle->setSingleShot(true); } MainWindow* q; bool allowClose; QQuickWidget* touchView; QPointer desktopView; QObject* currentView; Settings *settings; bool slateMode; bool docked; QString currentTouchPage; KoView* touchKoView; QObject* touchEventReceiver; KoView* desktopKoView; DesktopViewProxy* desktopViewProxy; bool forceDesktop; bool forceTouch; bool temporaryFile; ViewModeSynchronisationObject* syncObject; QAction* toDesktop; QAction* toTouch; QToolButton* switcher; QAction* alternativeSaveAction; QTimer* fullScreenThrottle; void shouldAcceptTouchEvents(QWidget* widget) { // See https://bugreports.qt.io/browse/QTBUG-66718 static QVersionNumber qtVersion = QVersionNumber::fromString(qVersion()); static bool shouldWidgetAcceptTouchEvents = qtVersion > QVersionNumber(5, 9, 3) && qtVersion.normalized() != QVersionNumber(5, 10); if(shouldWidgetAcceptTouchEvents) { widget->setAttribute(Qt::WA_AcceptTouchEvents, true); } } void initTouchView(QObject* parent) { touchView = new QQuickWidget(); shouldAcceptTouchEvents(touchView); QmlGlobalEngine::instance()->setEngine(touchView->engine()); touchView->engine()->addImageProvider(QLatin1String("recentimage"), new RecentImageImageProvider); touchView->engine()->rootContext()->setContextProperty("mainWindow", parent); settings = new Settings( q ); DocumentManager::instance()->setSettingsManager( settings ); touchView->engine()->rootContext()->setContextProperty("DocumentManager", DocumentManager::instance()); touchView->engine()->rootContext()->setContextProperty("Settings", settings); touchView->engine()->rootContext()->setContextProperty("Constants", new Constants( q )); touchView->engine()->rootContext()->setContextProperty("RecentFileManager", DocumentManager::instance()->recentFileManager()); touchView->engine()->rootContext()->setContextProperty("WORDS_MIME_TYPE", QString(WORDS_MIME_TYPE)); touchView->engine()->rootContext()->setContextProperty("STAGE_MIME_TYPE", QString(STAGE_MIME_TYPE)); #ifdef Q_OS_WIN QDir appdir(qApp->applicationDirPath()); // Corrects for mismatched case errors in path (qtdeclarative fails to load) wchar_t buffer[1024]; QString absolute = appdir.absolutePath(); DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); rv = ::GetLongPathName(buffer, buffer, 1024); QString correctedPath((QChar *)buffer); appdir.setPath(correctedPath); // for now, the app in bin/ and we still use the env.bat script appdir.cdUp(); // QT5TODO: adapt to QML_IMPORT_PATH usage and install to ${QML_INSTALL_DIR} touchView->engine()->addImportPath(appdir.canonicalPath() + "/imports"); touchView->engine()->addImportPath(appdir.canonicalPath() + "/lib/calligra/imports"); touchView->engine()->addImportPath(appdir.canonicalPath() + "/lib64/calligra/imports"); touchView->engine()->addImportPath(appdir.canonicalPath() + "/bin/data/calligragemini"); QString mainqml = appdir.canonicalPath() + "/bin/data/calligragemini/calligragemini.qml"; #else QString mainqml = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("calligragemini/calligragemini.qml")); #endif Q_ASSERT(QFile::exists(mainqml)); if (!QFile::exists(mainqml)) { QMessageBox::warning(0, "No QML found", mainqml + " doesn't exist."); } QFileInfo fi(mainqml); touchView->setSource(QUrl::fromLocalFile(fi.canonicalFilePath())); touchView->setResizeMode( QQuickWidget::SizeRootObjectToView ); toDesktop = new QAction(q); toDesktop->setEnabled(true); toDesktop->setText(tr("Switch to Desktop")); // useful for monkey-testing to crash... //toDesktop->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D); //q->addAction(toDesktop); //connect(toDesktop, SIGNAL(triggered(bool)), q, SLOT(switchDesktopForced())); connect(toDesktop, SIGNAL(triggered(bool)), q, SLOT(switchToDesktop())); touchView->engine()->rootContext()->setContextProperty("switchToDesktopAction", toDesktop); } void initDesktopView() { if(settings->currentFile().isEmpty()) { return; } // Initialize all Calligra directories etc. KoGlobal::initialize(); // The default theme is not what we want for Gemini KConfigGroup group(KSharedConfig::openConfig(), "theme"); if(group.readEntry("Theme", "no-theme-is-set") == QLatin1String("no-theme-is-set")) { group.writeEntry("Theme", "Krita-dark"); } if(settings->currentFileClass() == WORDS_MIME_TYPE) { qApp->setApplicationName("calligrawords"); desktopView = new KoMainWindow(WORDS_MIME_TYPE, KWFactory::componentData()); } else if(settings->currentFileClass() == STAGE_MIME_TYPE) { qApp->setApplicationName("calligrastage"); desktopView = new KoMainWindow(STAGE_MIME_TYPE, KPrFactory::componentData()); } else { desktopView = 0; qDebug() << "Big trouble, things gonna break. desktopView is not created." << settings->currentFileClass(); return; } toTouch = new QAction(desktopView); toTouch->setEnabled(false); toTouch->setText(tr("Switch to Touch")); toTouch->setIcon(koIcon("system-reboot")); toTouch->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S); //connect(toTouch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchTouchForced())); connect(toTouch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchToTouch())); desktopView->actionCollection()->addAction("SwitchToTouchView", toTouch); switcher = new QToolButton(); switcher->setEnabled(false); switcher->setText(tr("Switch to Touch")); switcher->setIcon(koIcon("system-reboot")); switcher->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); //connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchDesktopForced())); connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchToTouch())); desktopView->menuBar()->setCornerWidget(switcher); // DesktopViewProxy connects itself up to everything appropriate on construction, // and destroys itself again when the view is removed desktopViewProxy = new DesktopViewProxy(q, desktopView); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SIGNAL(documentSaved())); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SLOT(resetWindowTitle())); connect(desktopViewProxy, SIGNAL(documentSaved()), q, SLOT(enableAltSaveAction())); } void notifySlateModeChange(); void notifyDockingModeChange(); bool queryClose(); void altSaveQuery(); }; MainWindow::MainWindow(QStringList fileNames, QWidget* parent, Qt::WindowFlags flags ) : QMainWindow( parent, flags ), d( new Private(this) ) { qmlRegisterUncreatableType("org.calligra", 1, 0, "PropertyContainer", "Contains properties and naively extends QML to support dynamic properties"); qmlRegisterType("org.calligra", 1, 0, "Theme"); qmlRegisterType("org.calligra", 1, 0, "DocumentListModel"); qmlRegisterType("org.calligra", 1, 0, "SimpleTouchArea"); qmlRegisterType("org.calligra", 1, 0, "ToolManager"); qmlRegisterType("org.calligra", 1, 0, "ParagraphStylesModel"); qmlRegisterType("org.calligra", 1, 0, "KeyboardModel"); qmlRegisterType("org.calligra", 1, 0, "ScribbleArea"); qmlRegisterType("org.calligra", 1, 0, "RecentFilesModel"); qmlRegisterType("org.calligra", 1, 0, "TemplatesModel"); qmlRegisterType("org.calligra", 1, 0, "CloudAccountsModel"); qmlRegisterType(); qRegisterMetaType(); qApp->setActiveWindow( this ); setWindowTitle(i18n("Calligra Gemini")); setWindowIcon(koIcon("calligragemini"));//gemini")); resize(QApplication::desktop()->availableGeometry().size() * 3/4); d->shouldAcceptTouchEvents(this); foreach(const QString &fileName, fileNames) { DocumentManager::instance()->recentFileManager()->addRecent( QDir::current().absoluteFilePath( fileName ) ); } connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(documentChanged())); connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(resetWindowTitle())); connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(resetWindowTitle())); connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(enableAltSaveAction())); + connect(DocumentManager::instance(), SIGNAL(aboutToDeleteDocument()), SLOT(closeWindow())); d->initTouchView(this); // Set the initial view to touch... because reasons. // Really, this allows us to show the pleasant welcome screen from Touch switchToTouch(); if(!fileNames.isEmpty()) { //It feels a little hacky, but call a QML function to open files. //This saves a lot of hassle required to change state for loading dialogs etc. QMetaObject::invokeMethod(d->touchView->rootObject(), "openFile", Q_ARG(QVariant, fileNames.at(0))); } } void MainWindow::resetWindowTitle() { - QUrl url = DocumentManager::instance()->document()->url(); + KoDocument* document = DocumentManager::instance()->document(); + if (!document) + return; + QUrl url = document->url(); QString fileName = url.fileName(); if(url.scheme() == "temp" || url.isEmpty()) fileName = i18n("Untitled"); KoDialog::CaptionFlags flags = KoDialog::HIGCompliantCaption; - KoDocument* document = DocumentManager::instance()->document(); - if (document && document->isModified() ) { + if ( document->isModified() ) { flags |= KoDialog::ModifiedCaption; } setWindowTitle( KoDialog::makeStandardCaption(fileName, this, flags) ); } void MainWindow::switchDesktopForced() { if (d->slateMode) d->forceDesktop = true; d->forceTouch = false; } void MainWindow::switchTouchForced() { if (!d->slateMode) d->forceTouch = true; d->forceDesktop = false; } void MainWindow::switchToTouch() { QTime timer; timer.start(); qDebug() << "Switching to touch"; if (d->toTouch) { d->toTouch->setEnabled(false); d->switcher->setEnabled(false); } d->syncObject = new ViewModeSynchronisationObject; - KoView* view = 0; if (d->desktopView && centralWidget() == d->desktopView) { - view = d->desktopView->rootView(); - - //Notify the view we are switching away from that we are about to switch away from it - //giving it the possibility to set up the synchronisation object. - ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, view, d->touchView, d->syncObject); - QApplication::sendEvent(view, &aboutToSwitchEvent); - + if (KoView* view = d->desktopView->rootView()) { + //Notify the view we are switching away from that we are about to switch away from it + //giving it the possibility to set up the synchronisation object. + ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, view, d->touchView, d->syncObject); + QApplication::sendEvent(view, &aboutToSwitchEvent); + } d->desktopView->setParent(0); } setCentralWidget(d->touchView); qApp->processEvents(); d->touchView->setVisible(true); resize(size()); emit switchedToTouch(); if (d->slateMode) { if (d->syncObject->initialized) QTimer::singleShot(50, this, SLOT(touchChange())); } else QTimer::singleShot(50, this, SLOT(touchChange())); //qDebug() << "milliseconds to switch to touch:" << timer.elapsed(); } void MainWindow::touchChange() { if (centralWidget() != d->touchView || !d->syncObject) return; if (d->desktopView) { //if (/*!d->touchKoView ||*/ !d->touchView->canvasWidget()) //{ // QTimer::singleShot(100, this, SLOT(touchChange())); // return; //} qApp->processEvents(); KoView* view = d->desktopView->rootView(); //Notify the new view that we just switched to it, passing our synchronisation object //so it can use those values to sync with the old view. ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToTouchModeEvent, view, d->touchView, d->syncObject); QApplication::sendEvent(d->touchEventReceiver, &switchedEvent); d->syncObject = 0; qApp->processEvents(); } if (d->toDesktop) { qApp->processEvents(); d->toDesktop->setEnabled(true); } } void MainWindow::switchToDesktop() { QTime timer; timer.start(); qDebug() << "Switching to desktop"; if (d->toDesktop) d->toDesktop->setEnabled(false); ViewModeSynchronisationObject* syncObject = new ViewModeSynchronisationObject; KoView* view = 0; if (d->desktopView) { view = d->desktopView->rootView(); } + if (!view) { + return; + } + //Notify the view we are switching away from that we are about to switch away from it //giving it the possibility to set up the synchronisation object. ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, d->touchView, view, syncObject); QApplication::sendEvent(d->touchEventReceiver, &aboutToSwitchEvent); qApp->processEvents(); if (d->currentTouchPage == "MainPage") { d->touchView->setParent(0); d->touchView->setVisible(false); setCentralWidget(d->desktopView); } - if (view) { - //Notify the new view that we just switched to it, passing our synchronisation object - //so it can use those values to sync with the old view. - ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToDesktopModeEvent, d->touchView, view, syncObject); - QApplication::sendEvent(view, &switchedEvent); - } + //Notify the new view that we just switched to it, passing our synchronisation object + //so it can use those values to sync with the old view. + ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToDesktopModeEvent, d->touchView, view, syncObject); + QApplication::sendEvent(view, &switchedEvent); qApp->processEvents(); d->toTouch->setEnabled(true); d->switcher->setEnabled(true); //qDebug() << "milliseconds to switch to desktop:" << timer.elapsed(); } void MainWindow::setDocAndPart(QObject* document, QObject* part) { if(DocumentManager::instance()->document()) { disconnect(DocumentManager::instance()->document(), SIGNAL(modified(bool)), this, SLOT(resetWindowTitle())); } qDebug() << "Attempting to set doc and part to" << document << "and" << part; d->touchEventReceiver = d->touchView->rootObject()->findChild("controllerItem"); DocumentManager::instance()->setDocAndPart(qobject_cast(document), qobject_cast(part)); if(DocumentManager::instance()->document()) { connect(DocumentManager::instance()->document(), SIGNAL(modified(bool)), this, SLOT(resetWindowTitle())); } if(document && part && !d->settings->currentFile().isEmpty()) { QAction* undo = qobject_cast(part)->views().at(0)->action("edit_undo"); d->touchView->rootContext()->setContextProperty("undoaction", undo); QAction* redo = qobject_cast(part)->views().at(0)->action("edit_redo"); d->touchView->rootContext()->setContextProperty("redoaction", redo); } resetWindowTitle(); } void MainWindow::documentChanged() { if (d->desktopView) { d->desktopView->deleteLater(); d->desktopView = 0; qApp->processEvents(); } d->initDesktopView(); if(d->desktopView) { d->desktopView->setRootDocument(DocumentManager::instance()->document(), DocumentManager::instance()->part(), false); qApp->processEvents(); d->desktopKoView = d->desktopView->rootView(); emit desktopKoViewChanged(); // d->desktopKoView->setQtMainWindow(d->desktopView); // connect(d->desktopKoView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start())); // connect(d->desktopKoView, SIGNAL(sigSavingFinished()), this, SLOT(resetWindowTitle())); // KWView* wordsview = qobject_cast(d->desktopView->rootView()); // if(wordsview) { // connect(wordsview->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), // this, SLOT(resourceChanged(int, const QVariant&))); // } if (!d->forceTouch && !d->slateMode) switchToDesktop(); } } bool MainWindow::allowClose() const { return d->allowClose; } void MainWindow::setAllowClose(bool allow) { d->allowClose = allow; } bool MainWindow::slateMode() const { return d->slateMode; } QString MainWindow::currentTouchPage() const { return d->currentTouchPage; } void MainWindow::setCurrentTouchPage(QString newPage) { d->currentTouchPage = newPage; emit currentTouchPageChanged(); if (newPage == "MainPage") { if (!d->forceTouch && !d->slateMode) { // Just loaded to desktop, do nothing } else { //QTimer::singleShot(3000, this, SLOT(adjustZoomOnDocumentChangedAndStuff())); } } } void MainWindow::setAlternativeSaveAction(QAction* altAction) { // if mainwindow exists, and alt action exists, remove alt action from current mainwindow if(d->desktopView && d->alternativeSaveAction) { d->desktopView->actionCollection()->removeAction(d->alternativeSaveAction); d->desktopView->actionCollection()->action("file_save")->disconnect(d->alternativeSaveAction); } d->alternativeSaveAction = altAction; // if mainwindow exists, set alt action into current mainwindow if(d->desktopView && d->alternativeSaveAction) { QAction* cloudSave = d->desktopView->actionCollection()->addAction("cloud_save", d->alternativeSaveAction); KToolBar* tb = d->desktopView->toolBar("mainToolBar"); if(tb) { tb->removeAction(cloudSave); // find the action /after/ the save action (because we want the alt save there, not before it) QAction* saveAction = d->desktopView->actionCollection()->action("file_save"); QAction* afterSave = 0; bool useNext = false; Q_FOREACH(QAction* action, tb->actions()) { if(useNext) { afterSave = action; break; } if(action == saveAction) { useNext = true; } } if(afterSave) { tb->insertAction(afterSave, cloudSave); } else { tb->addAction(cloudSave); } } } if(d->alternativeSaveAction) { // disabled for a start - this is called on load completion, so let's just assume we're not ready to reupload yet d->alternativeSaveAction->setEnabled(false); } } void MainWindow::enableAltSaveAction() { if(d->alternativeSaveAction) { d->alternativeSaveAction->setEnabled(true); } } void MainWindow::openFile() { QStringList mimeFilter; KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(WORDS_MIME_TYPE); if (!entry.isEmpty()) { QJsonObject json = entry.metaData(); #ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); #else QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); #endif mimeFilter << KoFilterManager::mimeFilter(WORDS_MIME_TYPE, KoFilterManager::Import, mimeTypes); } entry = KoDocumentEntry::queryByMimeType(STAGE_MIME_TYPE); if (!entry.isEmpty()) { QJsonObject json = entry.metaData(); #ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); #else QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); #endif mimeFilter << KoFilterManager::mimeFilter(STAGE_MIME_TYPE, KoFilterManager::Import, mimeTypes); } KoFileDialog dialog(d->desktopView, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(mimeFilter); QString filename = dialog.filename(); if(!filename.isEmpty()) { QMetaObject::invokeMethod(d->touchView->rootObject(), "openFile", Q_ARG(QVariant, filename), Q_ARG(QVariant, 0)); } } bool MainWindow::temporaryFile() const { return d->temporaryFile; } void MainWindow::setTemporaryFile(bool newValue) { d->temporaryFile = newValue; emit temporaryFileChanged(); } bool MainWindow::fullScreen() const { return Qt::WindowFullScreen == (windowState() & Qt::WindowFullScreen); } void MainWindow::setFullScreen(bool newValue) { if(newValue) { if(d->fullScreenThrottle->isActive()) { // not a good thing... you need to avoid this happening. This exists to avoid a death-loop, // such as what might happen if readermode is enabled when the window is not maximised // as this causes a resize loop which makes readermode switch between enabled and disabled, // which in turn makes fullScreen be set and reset all the time... very bad, so let's try // and avoid that. } else { setWindowState(windowState() | Qt::WindowFullScreen); } } else { // this is really unpleasant... however, fullscreen is very twitchy, and exiting it as below // will cause an inconsistent state, so we simply assume exiting fullscreen leaves you maximised. // It isn't optimal, but it is the best state for now, this has taken too long to work out. // setWindowState(windowState() & ~Qt::WindowFullScreen); // should really do it, but... it doesn't. So, we end up with what we have next: showMaximized(); } d->fullScreenThrottle->start(); emit fullScreenChanged(); } QObject* MainWindow::desktopKoView() const { return d->desktopKoView; } int MainWindow::lastScreen() const { QDesktopWidget desktop; return desktop.screenCount() - 1; } void MainWindow::resourceChanged(int key, const QVariant& v) { Q_UNUSED(key) Q_UNUSED(v) if(centralWidget() == d->touchView) return; } void MainWindow::resourceChangedTouch(int key, const QVariant& v) { Q_UNUSED(key) Q_UNUSED(v) if(centralWidget() == d->desktopView) return; } void MainWindow::minimize() { setWindowState(windowState() ^ Qt::WindowMinimized); } void MainWindow::closeWindow() { - d->desktopView->setNoCleanup(true); - //For some reason, close() does not work even if setAllowClose(true) was called just before this method. - //So instead just completely quit the application, since we are using a single window anyway. - DocumentManager::instance()->closeDocument(); - DocumentManager::instance()->part()->deleteLater(); + if (d->desktopView) { + d->desktopView->setNoCleanup(true); + if (centralWidget() == d->desktopView) + d->allowClose = d->queryClose(); + } + + if (d->allowClose) + { + d->altSaveQuery(); + d->settings->setCurrentFile(""); + } qApp->processEvents(); - QApplication::instance()->quit(); + qApp->quit(); } bool MainWindow::Private::queryClose() { desktopView->setNoCleanup(true); if (DocumentManager::instance()->document() == 0) return true; // main doc + internally stored child documents if (DocumentManager::instance()->document()->isModified()) { QString name; if (DocumentManager::instance()->document()->documentInfo()) { name = DocumentManager::instance()->document()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = DocumentManager::instance()->document()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = KMessageBox::warningYesNoCancel(q, i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes : { if (DocumentManager::instance()->isTemporaryFile() && !desktopViewProxy->fileSaveAs()) return false; if (!DocumentManager::instance()->save()) return false; break; } case KMessageBox::No : DocumentManager::instance()->document()->removeAutoSaveFiles(); DocumentManager::instance()->document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case KMessageBox::Cancel : return false; } } return true; } void MainWindow::Private::altSaveQuery() { - qApp->processEvents(); if(alternativeSaveAction && alternativeSaveAction->isEnabled()) { int res = KMessageBox::warningYesNo(q, i18n("

The cloud copy of the document is out of date. Do you want to upload a new copy?

")); switch (res) { case KMessageBox::Yes : { alternativeSaveAction->trigger(); while(alternativeSaveAction->isEnabled()) { qApp->processEvents(); } break; } case KMessageBox::No : default: break; } } } void MainWindow::closeEvent(QCloseEvent* event) { if (centralWidget() == d->desktopView) { - if (DocumentManager::instance()->document()->isLoading()) { + KoDocument* document = DocumentManager::instance()->document(); + if (document && document->isLoading()) { event->ignore(); return; } - d->allowClose = d->queryClose(); } - if (d->allowClose) - { - d->altSaveQuery(); - d->settings->setCurrentFile(""); - qApp->processEvents(); - if (d->desktopView) - { - d->desktopView->setNoCleanup(true); - } - event->accept(); - } - else - { - event->ignore(); - emit closeRequested(); - } + event->accept(); + closeWindow(); } MainWindow::~MainWindow() { delete d; } #ifdef Q_OS_WIN bool MainWindow::winEvent( MSG * message, long * result ) { if (message && message->message == WM_SETTINGCHANGE && message->lParam) { if (wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *) message->lParam) == 0) d->notifySlateModeChange(); else if (wcscmp(TEXT("SystemDockMode"), (TCHAR *) message->lParam) == 0) d->notifyDockingModeChange(); *result = 0; return true; } return false; } #endif void MainWindow::Private::notifySlateModeChange() { #ifdef Q_OS_WIN bool bSlateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); if (slateMode != bSlateMode) { slateMode = bSlateMode; emit q->slateModeChanged(); if (forceTouch || (slateMode && !forceDesktop)) { if (!toTouch || (toTouch && toTouch->isEnabled())) q->switchToTouch(); } else { q->switchToDesktop(); } //qDebug() << "Slate mode is now" << slateMode; } #endif } void MainWindow::Private::notifyDockingModeChange() { #ifdef Q_OS_WIN bool bDocked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0); if (docked != bDocked) { docked = bDocked; //qDebug() << "Docking mode is now" << docked; } #endif } diff --git a/gemini/desktopviewproxy.cpp b/gemini/desktopviewproxy.cpp index 4a78440e439..c90443a7270 100644 --- a/gemini/desktopviewproxy.cpp +++ b/gemini/desktopviewproxy.cpp @@ -1,185 +1,191 @@ /* * * Copyright 2013 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "desktopviewproxy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // CALLIGRA_OLD_PLUGIN_METADATA #include "MainWindow.h" #include #include #include #include #include class DesktopViewProxy::Private { public: Private(MainWindow* mainWindow, KoMainWindow* desktopView) : mainWindow(mainWindow) , desktopView(desktopView) , isImporting(false) {} MainWindow* mainWindow; KoMainWindow* desktopView; bool isImporting; }; DesktopViewProxy::DesktopViewProxy(MainWindow* mainWindow, KoMainWindow* parent) : QObject(parent) , d(new Private(mainWindow, parent)) { Q_ASSERT(parent); // "There MUST be a KoMainWindow assigned, otherwise everything will blow up"); // Hide this one... as it doesn't work at all well and release happens :P QAction* closeAction = d->desktopView->actionCollection()->action("file_close"); closeAction->setVisible(false); // Concept is simple - simply steal all the actions we require to work differently, and reconnect them to local functions QAction* newAction = d->desktopView->actionCollection()->action("file_new"); newAction->disconnect(d->desktopView); connect(newAction, SIGNAL(triggered(bool)), this, SLOT(fileNew())); QAction* openAction = d->desktopView->actionCollection()->action("file_open"); openAction->disconnect(d->desktopView); connect(openAction, SIGNAL(triggered(bool)), this, SLOT(fileOpen())); QAction* saveAction = d->desktopView->actionCollection()->action("file_save"); saveAction->disconnect(d->desktopView); connect(saveAction, SIGNAL(triggered(bool)), this, SLOT(fileSave())); QAction* saveasAction = d->desktopView->actionCollection()->action("file_save_as"); saveasAction->disconnect(d->desktopView); connect(saveasAction, SIGNAL(triggered(bool)), this, SLOT(fileSaveAs())); QAction* reloadAction = d->desktopView->actionCollection()->action("file_reload_file"); reloadAction->disconnect(d->desktopView); connect(reloadAction, SIGNAL(triggered(bool)), this, SLOT(reload())); QAction* loadExistingAsNewAction = d->desktopView->actionCollection()->action("file_import_file"); loadExistingAsNewAction->disconnect(d->desktopView); connect(loadExistingAsNewAction, SIGNAL(triggered(bool)), this, SLOT(loadExistingAsNew())); // Recent files need a touch more work, as they aren't simply an action. KRecentFilesAction* recent = qobject_cast(d->desktopView->actionCollection()->action("file_open_recent")); recent->disconnect(d->desktopView); connect(recent, SIGNAL(urlSelected(QUrl)), this, SLOT(slotFileOpenRecent(QUrl))); recent->clear(); recent->loadEntries(KSharedConfig::openConfig()->group("RecentFiles")); connect(d->desktopView, SIGNAL(documentSaved()), this, SIGNAL(documentSaved())); } DesktopViewProxy::~DesktopViewProxy() { delete d; } void DesktopViewProxy::fileNew() { QProcess::startDetached(qApp->applicationFilePath(), QStringList(), QDir::currentPath()); } void DesktopViewProxy::fileOpen() { QStringList mimeFilter; KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(DocumentManager::instance()->settingsManager()->currentFileClass().toLatin1()); if (!entry.isEmpty()) { QJsonObject json = entry.metaData(); #ifdef CALLIGRA_OLD_PLUGIN_METADATA QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); #else QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); #endif mimeFilter << KoFilterManager::mimeFilter(DocumentManager::instance()->settingsManager()->currentFileClass().toLatin1(), KoFilterManager::Import, mimeTypes); } KoFileDialog dialog(d->desktopView, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(mimeFilter); QString filename = dialog.filename(); if (filename.isEmpty()) return; DocumentManager::instance()->recentFileManager()->addRecent(filename); QProcess::startDetached(qApp->applicationFilePath(), QStringList() << filename, QDir::currentPath()); } void DesktopViewProxy::fileSave() { if(DocumentManager::instance()->isTemporaryFile()) { if(d->desktopView->saveDocument(true)) { - DocumentManager::instance()->recentFileManager()->addRecent(DocumentManager::instance()->document()->url().toLocalFile()); - DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); - DocumentManager::instance()->setTemporaryFile(false); - emit documentSaved(); + if (KoDocument* document = DocumentManager::instance()->document()) { + DocumentManager::instance()->recentFileManager()->addRecent(document->url().toLocalFile()); + DocumentManager::instance()->settingsManager()->setCurrentFile(document->url().toLocalFile()); + DocumentManager::instance()->setTemporaryFile(false); + emit documentSaved(); + } } } else { DocumentManager::instance()->save(); emit documentSaved(); } } bool DesktopViewProxy::fileSaveAs() { + KoDocument* document = DocumentManager::instance()->document(); + if (!document) + return false; + if(d->desktopView->saveDocument(true)) { - DocumentManager::instance()->recentFileManager()->addRecent(DocumentManager::instance()->document()->url().toLocalFile()); - DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); + DocumentManager::instance()->recentFileManager()->addRecent(document->url().toLocalFile()); + DocumentManager::instance()->settingsManager()->setCurrentFile(document->url().toLocalFile()); DocumentManager::instance()->setTemporaryFile(false); emit documentSaved(); return true; } - DocumentManager::instance()->settingsManager()->setCurrentFile(DocumentManager::instance()->document()->url().toLocalFile()); + DocumentManager::instance()->settingsManager()->setCurrentFile(document->url().toLocalFile()); return false; } void DesktopViewProxy::reload() { DocumentManager::instance()->reload(); } void DesktopViewProxy::loadExistingAsNew() { d->isImporting = true; fileOpen(); d->isImporting = false; } void DesktopViewProxy::slotFileOpenRecent(const QUrl& url) { QProcess::startDetached(qApp->applicationFilePath(), QStringList() << url.toLocalFile(), QDir::currentPath()); } diff --git a/libs/main/KoMainWindow.cpp b/libs/main/KoMainWindow.cpp index 8f28680435c..73720ada303 100644 --- a/libs/main/KoMainWindow.cpp +++ b/libs/main/KoMainWindow.cpp @@ -1,2174 +1,2174 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port 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 "KoMainWindow.h" #include "KoView.h" #include "KoDocument.h" #include "KoFilterManager.h" #include "KoDocumentInfo.h" #include "KoDocumentInfoDlg.h" #include "KoFileDialog.h" #include "KoVersionDialog.h" #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoPrintJob.h" #include "KoDocumentEntry.h" #include "KoDockerManager.h" #include "KoPart.h" #include #include #include "KoApplication.h" #include #include "KoResourcePaths.h" #include "KoComponentData.h" #include #include #include "calligraversion.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KACTIVITIES #include #endif // // qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MainDebug.h" class KoMainWindowPrivate { public: KoMainWindowPrivate(const QByteArray &_nativeMimeType, const KoComponentData &componentData_, KoMainWindow *w) : componentData(componentData_) { nativeMimeType = _nativeMimeType; parent = w; rootDocument = 0; rootPart = 0; partToOpen = 0; mainWindowGuiIsBuilt = false; forQuit = false; activePart = 0; activeView = 0; firstTime = true; progress = 0; showDocumentInfo = 0; saveAction = 0; saveActionAs = 0; printAction = 0; printActionPreview = 0; sendFileAction = 0; exportPdf = 0; closeFile = 0; reloadFile = 0; showFileVersions = 0; importFile = 0; exportFile = 0; encryptDocument = 0; #ifndef NDEBUG uncompressToDir = 0; #endif isImporting = false; isExporting = false; windowSizeDirty = false; lastExportSpecialOutputFlag = 0; readOnly = false; dockWidgetMenu = 0; dockerManager = 0; deferredClosingEvent = 0; #ifdef HAVE_KACTIVITIES activityResource = 0; #endif m_helpMenu = 0; // PartManger m_activeWidget = 0; m_activePart = 0; noCleanup = false; openingDocument = false; } ~KoMainWindowPrivate() { qDeleteAll(toolbarList); } void applyDefaultSettings(QPrinter &printer) { QString title = rootDocument->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { title = rootDocument->url().fileName(); // strip off the native extension (I don't want foobar.kwd.ps when printing into a file) QMimeType mime = QMimeDatabase().mimeTypeForName(rootDocument->outputMimeType()); if (mime.isValid()) { const QString extension = mime.preferredSuffix(); if (title.endsWith(extension)) title.chop(extension.length()); } } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", parent->componentData().componentDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } QByteArray nativeMimeType; KoMainWindow *parent; - KoDocument *rootDocument; + QPointer rootDocument; QList rootViews; // PartManager QPointer rootPart; QPointer partToOpen; QPointer activePart; QPointer m_activePart; QPointer m_registeredPart; KoView *activeView; QWidget *m_activeWidget; QPointer progress; QMutex progressMutex; QList toolbarList; bool mainWindowGuiIsBuilt; bool forQuit; bool firstTime; bool windowSizeDirty; bool readOnly; QAction *showDocumentInfo; QAction *saveAction; QAction *saveActionAs; QAction *printAction; QAction *printActionPreview; QAction *sendFileAction; QAction *exportPdf; QAction *closeFile; QAction *reloadFile; QAction *showFileVersions; QAction *importFile; QAction *exportFile; QAction *encryptDocument; #ifndef NDEBUG QAction *uncompressToDir; #endif KToggleAction *toggleDockers; KToggleAction *toggleDockerTitleBars; KRecentFilesAction *recent; bool isImporting; bool isExporting; QUrl lastExportUrl; QByteArray lastExportedFormat; int lastExportSpecialOutputFlag; QMap dockWidgetsMap; KActionMenu *dockWidgetMenu; QMap dockWidgetVisibilityMap; KoDockerManager *dockerManager; QList dockWidgets; QByteArray m_dockerStateBeforeHiding; QCloseEvent *deferredClosingEvent; #ifdef HAVE_KACTIVITIES KActivities::ResourceInstance *activityResource; #endif KoComponentData componentData; KHelpMenu *m_helpMenu; bool noCleanup; bool openingDocument; }; KoMainWindow::KoMainWindow(const QByteArray &nativeMimeType, const KoComponentData &componentData) : KXmlGuiWindow() , d(new KoMainWindowPrivate(nativeMimeType, componentData, this)) { setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); // PartManager // End QString doc; const QStringList allFiles = KoResourcePaths::findAllResources("data", "calligra/calligra_shell.rc"); setXMLFile(findMostRecentXMLFile(allFiles, doc)); setLocalXMLFile(KoResourcePaths::locateLocal("data", "calligra/calligra_shell.rc")); actionCollection()->addAction(KStandardAction::New, "file_new", this, SLOT(slotFileNew())); actionCollection()->addAction(KStandardAction::Open, "file_open", this, SLOT(slotFileOpen())); d->recent = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recent, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); d->saveAction = actionCollection()->addAction(KStandardAction::Save, "file_save", this, SLOT(slotFileSave())); d->saveActionAs = actionCollection()->addAction(KStandardAction::SaveAs, "file_save_as", this, SLOT(slotFileSaveAs())); d->printAction = actionCollection()->addAction(KStandardAction::Print, "file_print", this, SLOT(slotFilePrint())); d->printActionPreview = actionCollection()->addAction(KStandardAction::PrintPreview, "file_print_preview", this, SLOT(slotFilePrintPreview())); d->exportPdf = new QAction(i18n("Export as PDF..."), this); d->exportPdf->setIcon(koIcon("application-pdf")); actionCollection()->addAction("file_export_pdf", d->exportPdf); connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->sendFileAction = actionCollection()->addAction(KStandardAction::Mail, "file_send_file", this, SLOT(slotEmailFile())); d->closeFile = actionCollection()->addAction(KStandardAction::Close, "file_close", this, SLOT(slotFileClose())); actionCollection()->addAction(KStandardAction::Quit, "file_quit", this, SLOT(slotFileQuit())); d->reloadFile = new QAction(i18n("Reload"), this); actionCollection()->addAction("file_reload_file", d->reloadFile); connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->showFileVersions = new QAction(i18n("Versions..."), this); actionCollection()->addAction("file_versions_file", d->showFileVersions); connect(d->showFileVersions, SIGNAL(triggered(bool)), this, SLOT(slotVersionsFile())); d->importFile = new QAction(koIcon("document-import"), i18n("Open ex&isting Document as Untitled Document..."), this); actionCollection()->addAction("file_import_file", d->importFile); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = new QAction(koIcon("document-export"), i18n("E&xport..."), this); actionCollection()->addAction("file_export_file", d->exportFile); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); d->encryptDocument = new QAction(i18n("En&crypt Document"), this); actionCollection()->addAction("file_encrypt_doc", d->encryptDocument); connect(d->encryptDocument, SIGNAL(triggered(bool)), this, SLOT(slotEncryptDocument())); #ifndef NDEBUG d->uncompressToDir = new QAction(i18n("&Uncompress to Directory"), this); actionCollection()->addAction("file_uncompress_doc", d->uncompressToDir); connect(d->uncompressToDir, SIGNAL(triggered(bool)), this, SLOT(slotUncompressToDir())); #endif QAction *actionNewView = new QAction(koIcon("window-new"), i18n("&New View"), this); actionCollection()->addAction("view_newview", actionNewView); connect(actionNewView, SIGNAL(triggered(bool)), this, SLOT(newView())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = new QAction(koIcon("document-properties"), i18n("Document Information"), this); actionCollection()->addAction("file_documentinfo", d->showDocumentInfo); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(slotConfigureToolbars()), actionCollection()); d->showDocumentInfo->setEnabled(false); d->saveActionAs->setEnabled(false); d->reloadFile->setEnabled(false); d->showFileVersions->setEnabled(false); d->importFile->setEnabled(true); // always enabled like File --> Open d->exportFile->setEnabled(false); d->saveAction->setEnabled(false); d->printAction->setEnabled(false); d->printActionPreview->setEnabled(false); d->sendFileAction->setEnabled(false); d->exportPdf->setEnabled(false); d->closeFile->setEnabled(false); d->encryptDocument->setEnabled(false); #ifndef NDEBUG d->uncompressToDir->setEnabled(false); #endif KToggleAction *fullscreenAction = new KToggleAction(koIcon("view-fullscreen"), i18n("Full Screen Mode"), this); actionCollection()->addAction("view_fullscreen", fullscreenAction); actionCollection()->setDefaultShortcut(fullscreenAction, QKeySequence::FullScreen); connect(fullscreenAction, SIGNAL(toggled(bool)), this, SLOT(viewFullscreen(bool))); d->toggleDockers = new KToggleAction(i18n("Show Dockers"), this); d->toggleDockers->setChecked(true); actionCollection()->addAction("view_toggledockers", d->toggleDockers); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = new KToggleAction(i18nc("@action:inmenu", "Show Docker Titlebars"), this); KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); d->toggleDockerTitleBars->setChecked(configGroupInterface.readEntry("ShowDockerTitleBars", true)); d->toggleDockerTitleBars->setVisible(false); actionCollection()->addAction("view_toggledockertitlebars", d->toggleDockerTitleBars); connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); d->dockWidgetMenu = new KActionMenu(i18n("Dockers"), this); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); d->dockWidgetMenu->setVisible(false); d->dockWidgetMenu->setDelayed(false); // Load list of recent files KSharedConfigPtr configPtr = componentData.config(); d->recent->loadEntries(configPtr->group("RecentFiles")); createMainwindowGUI(); d->mainWindowGuiIsBuilt = true; // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen()); desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreState(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); d->dockerManager = new KoDockerManager(this); } void KoMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KoMainWindow::~KoMainWindow() { KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); // Explicitly delete the docker manager to ensure that it is deleted before the dockers delete d->dockerManager; d->dockerManager = 0; // The doc and view might still exist (this is the case when closing the window) if (d->rootPart) d->rootPart->removeMainWindow(this); if (d->partToOpen) { d->partToOpen->removeMainWindow(this); delete d->partToOpen; } // safety first ;) setActivePart(0, 0); if (d->rootViews.indexOf(d->activeView) == -1) { delete d->activeView; d->activeView = 0; } while (!d->rootViews.isEmpty()) { delete d->rootViews.takeFirst(); } if(d->noCleanup) return; // We have to check if this was a root document. // This has to be checked from queryClose, too :) if (d->rootPart && d->rootPart->viewCount() == 0) { //debugMain <<"Destructor. No more views, deleting old doc" << d->rootDoc; delete d->rootDocument; } delete d; } void KoMainWindow::setRootDocument(KoDocument *doc, KoPart *part, bool deletePrevious) { if (d->rootDocument == doc) return; if (d->partToOpen && d->partToOpen->document() != doc) { d->partToOpen->removeMainWindow(this); if (deletePrevious) delete d->partToOpen; } d->partToOpen = 0; //debugMain <<"KoMainWindow::setRootDocument this =" << this <<" doc =" << doc; QList oldRootViews = d->rootViews; d->rootViews.clear(); KoDocument *oldRootDoc = d->rootDocument; KoPart *oldRootPart = d->rootPart; if (oldRootDoc) { oldRootDoc->disconnect(this); oldRootPart->removeMainWindow(this); if (dockerManager()) { dockerManager()->resetToolDockerWidgets(); } // Hide all dockwidgets and remember their old state d->dockWidgetVisibilityMap.clear(); foreach(QDockWidget* dockWidget, d->dockWidgetsMap) { d->dockWidgetVisibilityMap.insert(dockWidget, dockWidget->isVisible()); dockWidget->setVisible(false); } d->toggleDockerTitleBars->setVisible(false); d->dockWidgetMenu->setVisible(false); } d->rootDocument = doc; // XXX remove this after the splitting if (!part && doc) { d->rootPart = doc->documentPart(); } else { d->rootPart = part; } if (doc) { d->toggleDockerTitleBars->setVisible(true); d->dockWidgetMenu->setVisible(true); d->m_registeredPart = d->rootPart.data(); KoView *view = d->rootPart->createView(doc, this); setCentralWidget(view); d->rootViews.append(view); view->show(); view->setFocus(); // The addMainWindow has been done already if using openUrl if (!d->rootPart->mainWindows().contains(this)) { d->rootPart->addMainWindow(this); } } bool enable = d->rootDocument != 0 ? true : false; d->showDocumentInfo->setEnabled(enable); d->saveAction->setEnabled(enable); d->saveActionAs->setEnabled(enable); d->importFile->setEnabled(enable); d->exportFile->setEnabled(enable); d->encryptDocument->setEnabled(enable); #ifndef NDEBUG d->uncompressToDir->setEnabled(enable); #endif d->printAction->setEnabled(enable); d->printActionPreview->setEnabled(enable); d->sendFileAction->setEnabled(enable); d->exportPdf->setEnabled(enable); d->closeFile->setEnabled(enable); updateCaption(); setActivePart(d->rootPart, doc ? d->rootViews.first() : 0); emit restoringDone(); while(!oldRootViews.isEmpty()) { delete oldRootViews.takeFirst(); } if (oldRootPart && oldRootPart->viewCount() == 0) { //debugMain <<"No more views, deleting old doc" << oldRootDoc; oldRootDoc->clearUndoHistory(); if(deletePrevious) delete oldRootDoc; } if (doc && !d->dockWidgetVisibilityMap.isEmpty()) { foreach(QDockWidget* dockWidget, d->dockWidgetsMap) { dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } if (!d->rootDocument) { statusBar()->setVisible(false); } else { #ifdef Q_OS_MAC statusBar()->setMaximumHeight(28); #endif connect(d->rootDocument, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified(QString,bool))); } } void KoMainWindow::updateReloadFileAction(KoDocument *doc) { d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KoMainWindow::updateVersionsFileAction(KoDocument *doc) { //TODO activate it just when we save it in oasis file format d->showFileVersions->setEnabled(doc && !doc->url().isEmpty() && (doc->outputMimeType() == doc->nativeOasisMimeType() || doc->outputMimeType() == doc->nativeOasisMimeType() + "-template")); } void KoMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KoMainWindow::addRecentURL(const QUrl &url) { debugMain << "KoMainWindow::addRecentURL url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KoDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = QStandardPaths::standardLocations(QStandardPaths::TempLocation); foreach (const QString &tmpDir, tmpDirs) { if (path.startsWith(tmpDir)) { ok = false; // it's in the tmp resource break; } } if (ok) { KRecentDocument::add(QUrl::fromLocalFile(path)); KRecentDirs::add(":OpenDialog", QFileInfo(path).dir().canonicalPath()); } } else { KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash)); } if (ok) { d->recent->addUrl(url); } saveRecentFiles(); #ifdef HAVE_KACTIVITIES if (!d->activityResource) { d->activityResource = new KActivities::ResourceInstance(winId(), this); } d->activityResource->setUri(url); #endif } } void KoMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = componentData().config(); debugMain << this << " Saving recent files list into config. componentData()=" << componentData().componentName(); d->recent->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start foreach(KMainWindow* window, KMainWindow::memberList()) static_cast(window)->reloadRecentFileList(); } void KoMainWindow::reloadRecentFileList() { KSharedConfigPtr config = componentData().config(); d->recent->loadEntries(config->group("RecentFiles")); } KoPart* KoMainWindow::createPart() const { KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(d->nativeMimeType); QString errorMsg; KoPart *part = entry.createKoPart(&errorMsg); if (!part || !errorMsg.isEmpty()) { return 0; } return part; } void KoMainWindow::updateCaption() { debugMain << "KoMainWindow::updateCaption()"; if (!d->rootDocument) { updateCaption(QString(), false); } else { QString caption( d->rootDocument->caption() ); if (d->readOnly) { caption += ' ' + i18n("(write protected)"); } updateCaption(caption, d->rootDocument->isModified()); if (!rootDocument()->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", d->rootDocument->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KoMainWindow::updateCaption(const QString & caption, bool mod) { debugMain << "KoMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef CALLIGRA_ALPHA setCaption(QString("ALPHA %1: %2").arg(CALLIGRA_ALPHA).arg(caption), mod); return; #endif #ifdef CALLIGRA_BETA setCaption(QString("BETA %1: %2").arg(CALLIGRA_BETA).arg(caption), mod); return; #endif #ifdef CALLIGRA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(CALLIGRA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KoDocument *KoMainWindow::rootDocument() const { return d->rootDocument; } KoView *KoMainWindow::rootView() const { if (d->rootViews.indexOf(d->activeView) != -1) return d->activeView; - return d->rootViews.first(); + return d->rootViews.empty() ? nullptr : d->rootViews.first(); } bool KoMainWindow::openDocument(const QUrl &url) { if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) { KMessageBox::error(0, i18n("The file %1 does not exist.", url.url())); d->recent->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url); } bool KoMainWindow::openDocument(KoPart *newPart, const QUrl &url) { // the part always has a document; the document doesn't know about the part. KoDocument *newdoc = newPart->document(); if (!KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0)) { newdoc->initEmpty(); //create an empty document setRootDocument(newdoc, newPart); newdoc->setUrl(url); QMimeType mime = QMimeDatabase().mimeTypeForUrl(url); QString mimetype = (!mime.isValid() || mime.isDefault()) ? newdoc->nativeFormatMimeType() : mime.name(); newdoc->setMimeTypeAfterLoading(mimetype); updateCaption(); return true; } return openDocumentInternal(url, newPart, newdoc); } bool KoMainWindow::openDocumentInternal(const QUrl &url, KoPart *newpart, KoDocument *newdoc) { debugMain <<"KoMainWindow::openDocument" << url.url(); if (!newpart) newpart = createPart(); if (!newpart) return false; if (!newdoc) newdoc = newpart->document(); d->firstTime = true; connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString))); d->openingDocument = true; newpart->addMainWindow(this); // used by openUrl bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url); if (!openRet) { newpart->removeMainWindow(this); delete newdoc; delete newpart; d->openingDocument = false; return false; } updateReloadFileAction(newdoc); updateVersionsFileAction(newdoc); KFileItem file(url, newdoc->mimeType(), KFileItem::Unknown); if (!file.isWritable()) { setReadWrite(false); } return true; } // Separate from openDocument to handle async loading (remote URLs) void KoMainWindow::slotLoadCompleted() { debugMain << "KoMainWindow::slotLoadCompleted"; KoDocument *newdoc = qobject_cast(sender()); KoPart *newpart = newdoc->documentPart(); if (d->rootDocument && d->rootDocument->isEmpty()) { // Replace current empty document setRootDocument(newdoc); emit loadCompleted(this); } else if (d->rootDocument && !d->rootDocument->isEmpty()) { // Open in a new main window // (Note : could create the main window first and the doc next for this // particular case, that would give a better user feedback...) KoMainWindow *s = newpart->createMainWindow(); s->show(); newpart->removeMainWindow(this); s->setRootDocument(newdoc, newpart); emit loadCompleted(s); } else { // We had no document, set the new one setRootDocument(newdoc); emit loadCompleted(this); } slotProgress(-1); disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString))); d->openingDocument = false; } void KoMainWindow::slotLoadCanceled(const QString & errMsg) { debugMain << "KoMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user KMessageBox::error(this, errMsg); // ... can't delete the document, it's the one who emitted the signal... KoDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString))); d->openingDocument = false; emit loadCanceled(); } void KoMainWindow::slotSaveCanceled(const QString &errMsg) { debugMain << "KoMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user KMessageBox::error(this, errMsg); slotSaveCompleted(); } void KoMainWindow::slotSaveCompleted() { debugMain << "KoMainWindow::slotSaveCompleted"; KoDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } // returns true if we should save, false otherwise. bool KoMainWindow::exportConfirmation(const QByteArray &outputFormat) { KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); if (!group.readEntry("WantExportConfirmation", true)) { return true; } QMimeType mime = QMimeDatabase().mimeTypeForName(outputFormat); QString comment = mime.isValid() ? mime.comment() : i18n("%1 (unknown file type)", QString::fromLatin1(outputFormat)); // Warn the user int ret; if (!isExporting()) { // File --> Save ret = KMessageBox::warningContinueCancel ( this, i18n("Saving as a %1 may result in some loss of formatting." "

Do you still want to save in this format?", QString("%1").arg(comment)), // in case we want to remove the bold later i18n("Confirm Save"), KStandardGuiItem::save(), KStandardGuiItem::cancel(), "NonNativeSaveConfirmation" ); } else { // File --> Export ret = KMessageBox::warningContinueCancel ( this, i18n("Exporting as a %1 may result in some loss of formatting." "

Do you still want to export to this format?", QString("%1").arg(comment)), // in case we want to remove the bold later i18n("Confirm Export"), KGuiItem(i18n("Export")), KStandardGuiItem::cancel(), "NonNativeExportConfirmation" // different to the one used for Save (above) ); } return (ret == KMessageBox::Continue); } bool KoMainWindow::saveDocument(bool saveas, bool silent, int specialOutputFlag) { if (!d->rootDocument || !d->rootPart) { return true; } bool reset_url; if (d->rootDocument->url().isEmpty()) { emit saveDialogShown(); reset_url = true; saveas = true; } else { reset_url = false; } connect(d->rootDocument, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(d->rootDocument, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(d->rootDocument, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString))); QUrl oldURL = d->rootDocument->url(); QString oldFile = d->rootDocument->localFilePath(); QByteArray _native_format = d->rootDocument->nativeFormatMimeType(); QByteArray oldOutputFormat = d->rootDocument->outputMimeType(); int oldSpecialOutputFlag = d->rootDocument->specialOutputFlag(); QUrl suggestedURL = d->rootDocument->url(); QStringList mimeFilter; QMimeType mime = QMimeDatabase().mimeTypeForName(_native_format); if (!mime.isValid()) // QT5TODO: find if there is no better way to get an object for the default type mime = QMimeDatabase().mimeTypeForName(QStringLiteral("application/octet-stream")); if (specialOutputFlag) mimeFilter = mime.globPatterns(); else mimeFilter = KoFilterManager::mimeFilter(_native_format, KoFilterManager::Export, d->rootDocument->extraNativeMimeTypes()); if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) { debugMain << "KoMainWindow::saveDocument no export filter for" << oldOutputFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = suggestedURL.fileName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name int c = suggestedFilename.lastIndexOf('.'); const QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (c < 0) suggestedFilename += ext; else suggestedFilename = suggestedFilename.left(c) + ext; } else { // current filename extension wrong anyway if (c > 0) { // this assumes that a . signifies an extension, not just a . suggestedFilename = suggestedFilename.left(c); } } suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (d->rootDocument->url().isEmpty() || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("untitled")); dialog.setDefaultDir((isExporting() && !d->lastExportUrl.isEmpty()) ? d->lastExportUrl.toLocalFile() : suggestedURL.toLocalFile()); dialog.setMimeTypeFilters(mimeFilter); QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { QMimeType mime = QMimeDatabase().mimeTypeForName(_native_format); fn.append(mime.preferredSuffix()); newURL = QUrl::fromLocalFile(fn); } } QByteArray outputFormat = _native_format; if (!specialOutputFlag) { QMimeType mime = QMimeDatabase().mimeTypeForUrl(newURL); outputFormat = mime.name().toLatin1(); } if (!isExporting()) justChangingFilterOptions = (newURL == d->rootDocument->url()) && (outputFormat == d->rootDocument->mimeType()) && (specialOutputFlag == oldSpecialOutputFlag); else justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat) && (specialOutputFlag == d->lastExportSpecialOutputFlag); bool bOk = true; if (newURL.isEmpty()) { bOk = false; } // adjust URL before doing checks on whether the file exists. if (specialOutputFlag) { QString fileName = newURL.fileName(); if ( specialOutputFlag== KoDocument::SaveAsDirectoryStore) { // Do nothing } else if (specialOutputFlag == KoDocument::SaveEncrypted) { int dot = fileName.lastIndexOf('.'); QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (dot < 0) fileName += ext; else fileName = fileName.left(dot) + ext; } else { // current filename extension wrong anyway if (dot > 0) fileName = fileName.left(dot); } newURL = newURL.adjusted(QUrl::RemoveFilename); newURL.setPath(newURL.path() + fileName); } } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions || d->rootDocument->confirmNonNativeSave(isExporting())) { if (!d->rootDocument->isNativeFormat(outputFormat)) wantToSave = exportConfirmation(outputFormat); } if (wantToSave) { // // Note: // If the user is stupid enough to Export to the current URL, // we do _not_ change this operation into a Save As. Reasons // follow: // // 1. A check like "isExporting() && oldURL == newURL" // doesn't _always_ work on case-insensitive filesystems // and inconsistent behaviour is bad. // 2. It is probably not a good idea to change d->rootDocument->mimeType // and friends because the next time the user File/Save's, // (not Save As) they won't be expecting that they are // using their File/Export settings // // As a bad side-effect of this, the modified flag will not // be updated and it is possible that what is currently on // their screen is not what is stored on disk (through loss // of formatting). But if you are dumb enough to change // mimetype but not the filename, then arguably, _you_ are // the "bug" :) // // - Clarence // d->rootDocument->setOutputMimeType(outputFormat, specialOutputFlag); if (!isExporting()) { // Save As ret = d->rootDocument->saveAs(newURL); if (ret) { debugMain << "Successful Save As!"; addRecentURL(newURL); setReadWrite(true); } else { debugMain << "Failed Save As!"; d->rootDocument->setUrl(oldURL); d->rootDocument->setLocalFilePath(oldFile); d->rootDocument->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } } else { // Export ret = d->rootDocument->exportDocument(newURL); if (ret) { // a few file dialog convenience things d->lastExportUrl = newURL; d->lastExportedFormat = outputFormat; d->lastExportSpecialOutputFlag = specialOutputFlag; } // always restore output format d->rootDocument->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } if (silent) // don't let the document change the window caption d->rootDocument->setTitleModified(); } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving bool needConfirm = d->rootDocument->confirmNonNativeSave(false) && !d->rootDocument->isNativeFormat(oldOutputFormat); if (!needConfirm || (needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */)) ) { // be sure d->rootDocument has the correct outputMimeType! if (isExporting() || d->rootDocument->isModified() || d->rootDocument->alwaysAllowSaving()) { ret = d->rootDocument->save(); } if (!ret) { debugMain << "Failed Save!"; d->rootDocument->setUrl(oldURL); d->rootDocument->setLocalFilePath(oldFile); } } else ret = false; } if (!ret && reset_url) d->rootDocument->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(d->rootDocument); updateCaption(); return ret; } void KoMainWindow::closeEvent(QCloseEvent *e) { // If we are in the process of opening a new document, rootDocument() may not have been set yet, // so we must prevent closing to avoid crash. if(d->openingDocument || (rootDocument() && rootDocument()->isLoading())) { e->setAccepted(false); return; } if (queryClose()) { d->deferredClosingEvent = e; if (d->partToOpen) { // The open pane is visible d->partToOpen->deleteOpenPane(true); } if (!d->m_dockerStateBeforeHiding.isEmpty()) { restoreState(d->m_dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if(d->noCleanup) return; setRootDocument(0); if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency foreach(QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KoMainWindow::saveWindowSettings() { KSharedConfigPtr config = componentData().config(); if (d->windowSizeDirty ) { // Save window size into the config file of our componentData // TODO: check if this is ever read again, seems lost over the years debugMain << "KoMainWindow::saveWindowSettings"; KConfigGroup mainWindowConfigGroup = config->group("MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), mainWindowConfigGroup); config->sync(); d->windowSizeDirty = false; } if ( rootDocument() && d->rootPart) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); //debugMain <<"KoMainWindow::closeEvent -> saveMainWindowSettings rootdoc's componentData=" << d->rootPart->componentData().componentName(); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KoMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } bool KoMainWindow::queryClose() { if (rootDocument() == 0) return true; //debugMain <<"KoMainWindow::queryClose() viewcount=" << rootDocument()->viewCount() // << " mainWindowCount=" << rootDocument()->mainWindowCount() << endl; if (!d->forQuit && d->rootPart && d->rootPart->mainwindowCount() > 1) // there are more open, and we are closing just one, so no problem for closing return true; // main doc + internally stored child documents if (d->rootDocument->isModified()) { QString name; if (rootDocument()->documentInfo()) { name = rootDocument()->documentInfo()->aboutInfo("title"); } if (name.isEmpty()) name = rootDocument()->url().fileName(); if (name.isEmpty()) name = i18n("Untitled"); int res = KMessageBox::warningYesNoCancel(this, i18n("

The document '%1' has been modified.

Do you want to save it?

", name), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard()); switch (res) { case KMessageBox::Yes : { bool isNative = (d->rootDocument->outputMimeType() == d->rootDocument->nativeFormatMimeType()); if (!saveDocument(!isNative)) return false; break; } case KMessageBox::No : rootDocument()->removeAutoSaveFiles(); rootDocument()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything. break; default : // case KMessageBox::Cancel : return false; } } return true; } // Helper method for slotFileNew and slotFileClose void KoMainWindow::chooseNewDocument(InitDocFlags initDocFlags) { KoDocument* doc = rootDocument(); KoPart *newpart = createPart(); KoDocument *newdoc = newpart->document(); if (!newdoc) return; disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); if ((!doc && initDocFlags == InitDocFileNew) || (doc && !doc->isEmpty())) { KoMainWindow *s = newpart->createMainWindow(); s->show(); newpart->addMainWindow(s); newpart->showStartUpWidget(s, true /*Always show widget*/); return; } if (doc) { setRootDocument(0); if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; } newpart->addMainWindow(this); newpart->showStartUpWidget(this, true /*Always show widget*/); } void KoMainWindow::slotFileNew() { chooseNewDocument(InitDocFileNew); } void KoMainWindow::slotFileOpen() { QUrl url; if (!isImporting()) { KoFileDialog dialog(this, KoFileDialog::OpenFile, "OpenDocument"); dialog.setCaption(i18n("Open Document")); dialog.setDefaultDir(qApp->applicationName().contains("karbon") ? QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) : QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(koApp->mimeFilter(KoFilterManager::Import)); dialog.setHideNameFilterDetailsOption(); url = QUrl::fromUserInput(dialog.filename()); } else { KoFileDialog dialog(this, KoFileDialog::ImportFile, "OpenDocument"); dialog.setCaption(i18n("Import Document")); dialog.setDefaultDir(qApp->applicationName().contains("karbon") ? QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) : QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); dialog.setMimeTypeFilters(koApp->mimeFilter(KoFilterManager::Import)); dialog.setHideNameFilterDetailsOption(); url = QUrl::fromUserInput(dialog.filename()); } if (url.isEmpty()) return; (void) openDocument(url); } void KoMainWindow::slotFileOpenRecent(const QUrl & url) { // Create a copy, because the original QUrl in the map of recent files in // KRecentFilesAction may get deleted. (void) openDocument(QUrl(url)); } void KoMainWindow::slotFileSave() { if (saveDocument()) emit documentSaved(); } void KoMainWindow::slotFileSaveAs() { if (saveDocument(true)) emit documentSaved(); } void KoMainWindow::slotEncryptDocument() { if (saveDocument(false, false, KoDocument::SaveEncrypted)) emit documentSaved(); } void KoMainWindow::slotUncompressToDir() { if (saveDocument(true, false, KoDocument::SaveAsDirectoryStore)) emit documentSaved(); } void KoMainWindow::slotDocumentInfo() { if (!rootDocument()) return; KoDocumentInfo *docInfo = rootDocument()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->rootDocument->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { rootDocument()->setModified(false); } else { rootDocument()->setModified(true); } rootDocument()->setTitleModified(); } delete dlg; } void KoMainWindow::slotFileClose() { if (queryClose()) { saveWindowSettings(); setRootDocument(0); // don't delete this main window when deleting the document if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; chooseNewDocument(InitDocFileClose); } } void KoMainWindow::slotFileQuit() { close(); } void KoMainWindow::slotFilePrint() { if (!rootView()) return; KoPrintJob *printJob = rootView()->createPrintJob(); if (printJob == 0) return; d->applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = rootView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) printJob->startPrinting(KoPrintJob::DeleteWhenDone); else delete printJob; delete printDialog; } void KoMainWindow::slotFilePrintPreview() { if (!rootView()) return; KoPrintJob *printJob = rootView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KoPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KoPrintJob* KoMainWindow::exportToPdf(const QString &pdfFileName) { if (!rootView()) return 0; KoPageLayout pageLayout; pageLayout = rootView()->pageLayout(); return exportToPdf(pageLayout, pdfFileName); } KoPrintJob* KoMainWindow::exportToPdf(const KoPageLayout &_pageLayout, const QString &_pdfFileName) { if (!rootView()) return 0; KoPageLayout pageLayout = _pageLayout; QString pdfFileName = _pdfFileName; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KoDocument* pDoc = rootDocument(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.fileName(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KoPrintJob *printJob = rootView()->createPdfPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } d->applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); //before printing check if the printer can handle printing if (!printJob->canPrint()) { KMessageBox::error(this, i18n("Cannot export to the specified file")); } printJob->startPrinting(KoPrintJob::DeleteWhenDone); return printJob; } void KoMainWindow::slotConfigureKeys() { QAction* undoAction=0; QAction* redoAction=0; QString oldUndoText; QString oldRedoText; if(currentView()) { //The undo/redo action text is "undo" + command, replace by simple text while inside editor undoAction = currentView()->actionCollection()->action("edit_undo"); redoAction = currentView()->actionCollection()->action("edit_redo"); oldUndoText = undoAction->text(); oldRedoText = redoAction->text(); undoAction->setText(i18n("Undo")); redoAction->setText(i18n("Redo")); } guiFactory()->configureShortcuts(); if(currentView()) { undoAction->setText(oldUndoText); redoAction->setText(oldRedoText); } emit keyBindingsChanged(); } void KoMainWindow::slotConfigureToolbars() { if (rootDocument()) { KConfigGroup componentConfigGroup = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); saveMainWindowSettings(componentConfigGroup); } KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); } void KoMainWindow::slotNewToolbarConfig() { if (rootDocument()) { KConfigGroup componentConfigGroup = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); applyMainWindowSettings(componentConfigGroup); } KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); } void KoMainWindow::slotToolbarToggled(bool toggle) { //debugMain <<"KoMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) bar->show(); else bar->hide(); if (rootDocument()) { KConfigGroup componentConfigGroup = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()); saveMainWindowSettings(componentConfigGroup); } } else warnMain << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } bool KoMainWindow::toolbarIsVisible(const char *tbName) { QWidget *tb = toolBar(tbName); return !tb->isHidden(); } void KoMainWindow::showToolbar(const char * tbName, bool shown) { QWidget * tb = toolBar(tbName); if (!tb) { warnMain << "KoMainWindow: toolbar " << tbName << " not found."; return; } if (shown) tb->show(); else tb->hide(); // Update the action appropriately foreach(QAction* action, d->toolbarList) { if (action->objectName() != tbName) { //debugMain <<"KoMainWindow::showToolbar setChecked" << shown; static_cast(action)->setChecked(shown); break; } } } void KoMainWindow::viewFullscreen(bool fullScreen) { if (fullScreen) { window()->setWindowState(window()->windowState() | Qt::WindowFullScreen); // set } else { window()->setWindowState(window()->windowState() & ~Qt::WindowFullScreen); // reset } } void KoMainWindow::slotProgress(int value) { QMutexLocker locker(&d->progressMutex); debugMain << "KoMainWindow::slotProgress" << value; if (value <= -1 || value >= 100) { if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->firstTime = true; return; } if (d->firstTime || !d->progress) { // The statusbar might not even be created yet. // So check for that first, and create it if necessary QStatusBar *bar = findChild(); if (!bar) { statusBar()->show(); QApplication::sendPostedEvents(this, QEvent::ChildAdded); } if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->progress = new QProgressBar(statusBar()); d->progress->setMaximumHeight(statusBar()->fontMetrics().height()); d->progress->setRange(0, 100); statusBar()->addPermanentWidget(d->progress); d->progress->show(); d->firstTime = false; } if (!d->progress.isNull()) { d->progress->setValue(value); } locker.unlock(); qApp->processEvents(); } void KoMainWindow::setMaxRecentItems(uint _number) { d->recent->setMaxItems(_number); } void KoMainWindow::slotEmailFile() { if (!rootDocument()) return; // Subject = Document file name // Attachment = The current file // Message Body = The current document in HTML export? <-- This may be an option. QString theSubject; QStringList urls; QString fileURL; if (rootDocument()->url().isEmpty() || rootDocument()->isModified()) { //Save the file as a temporary file bool const tmp_modified = rootDocument()->isModified(); QUrl const tmp_url = rootDocument()->url(); QByteArray const tmp_mimetype = rootDocument()->outputMimeType(); // a little open, close, delete dance to make sure we have a nice filename // to use, but won't block windows from creating a new file with this name. QTemporaryFile *tmpfile = new QTemporaryFile(); tmpfile->open(); QString fileName = tmpfile->fileName(); tmpfile->close(); delete tmpfile; QUrl u = QUrl::fromLocalFile(fileName); rootDocument()->setUrl(u); rootDocument()->setModified(true); rootDocument()->setOutputMimeType(rootDocument()->nativeFormatMimeType()); saveDocument(false, true); fileURL = fileName; theSubject = i18n("Document"); urls.append(fileURL); rootDocument()->setUrl(tmp_url); rootDocument()->setModified(tmp_modified); rootDocument()->setOutputMimeType(tmp_mimetype); } else { fileURL = rootDocument()->url().url(); theSubject = i18n("Document - %1", rootDocument()->url().fileName()); urls.append(fileURL); } debugMain << "(" << fileURL << ")"; if (!fileURL.isEmpty()) { KToolInvocation::invokeMailer(QString(), QString(), QString(), theSubject, QString(), //body QString(), urls); // attachments } } void KoMainWindow::slotVersionsFile() { if (!rootDocument()) return; KoVersionDialog *dlg = new KoVersionDialog(this, rootDocument()); dlg->exec(); delete dlg; } void KoMainWindow::slotReloadFile() { KoDocument* pDoc = rootDocument(); if (!pDoc || pDoc->url().isEmpty() || !pDoc->isModified()) return; bool bOk = KMessageBox::questionYesNo(this, i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), i18n("Warning")) == KMessageBox::Yes; if (!bOk) return; QUrl url = pDoc->url(); if (!pDoc->isEmpty()) { saveWindowSettings(); setRootDocument(0); // don't delete this main window when deleting the document if(d->rootDocument) d->rootDocument->clearUndoHistory(); delete d->rootDocument; d->rootDocument = 0; } openDocument(url); return; } void KoMainWindow::slotImportFile() { debugMain << "slotImportFile()"; d->isImporting = true; slotFileOpen(); d->isImporting = false; } void KoMainWindow::slotExportFile() { debugMain << "slotExportFile()"; d->isExporting = true; slotFileSaveAs(); d->isExporting = false; } bool KoMainWindow::isImporting() const { return d->isImporting; } bool KoMainWindow::isExporting() const { return d->isExporting; } void KoMainWindow::setPartToOpen(KoPart *part) { d->partToOpen = part; } KoComponentData KoMainWindow::componentData() const { return d->componentData; } QDockWidget* KoMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) return 0; d->dockWidgets.push_back(dockWidget); KoDockWidgetTitleBar *titleBar = 0; // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = factory->defaultVisible(); switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } if (rootDocument()) { KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()).group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; } addDockWidget(side, dockWidget); if (dockWidget->features() & QDockWidget::DockWidgetClosable) { d->dockWidgetMenu->addAction(dockWidget->toggleViewAction()); if (!visible) dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; if (rootDocument()) { KConfigGroup group = KSharedConfig::openConfig()->group(d->rootPart->componentData().componentName()).group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); } if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); if (titleBar) { KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); titleBar->setVisible(configGroupInterface.readEntry("ShowDockerTitleBars", true)); } d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[ factory->id()]; } #ifdef Q_OS_MAC dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KoMainWindow::forceDockTabFonts() { QObjectList chis = children(); for (int i = 0; i < chis.size(); ++i) { if (chis.at(i)->inherits("QTabBar")) { ((QTabBar *)chis.at(i))->setFont(KoDockRegistry::dockFont()); } } } QList KoMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QList KoMainWindow::canvasObservers() const { QList observers; foreach(QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } } return observers; } KoDockerManager * KoMainWindow::dockerManager() const { return d->dockerManager; } void KoMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->m_dockerStateBeforeHiding = saveState(); foreach(QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->m_dockerStateBeforeHiding); } } KRecentFilesAction *KoMainWindow::recentAction() const { return d->recent; } KoView* KoMainWindow::currentView() const { // XXX if (d->activeView) { return d->activeView; } else if (!d->rootViews.isEmpty()) { return d->rootViews.first(); } return 0; } void KoMainWindow::newView() { Q_ASSERT((d != 0 && d->activeView && d->activePart && d->activeView->koDocument())); KoMainWindow *mainWindow = d->activePart->createMainWindow(); mainWindow->setRootDocument(d->activeView->koDocument(), d->activePart); mainWindow->show(); } void KoMainWindow::createMainwindowGUI() { if ( isHelpMenuEnabled() && !d->m_helpMenu ) { d->m_helpMenu = new KHelpMenu( this, componentData().aboutData(), true ); KActionCollection *actions = actionCollection(); QAction *helpContentsAction = d->m_helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->m_helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->m_helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->m_helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->m_helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->m_helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } } QString f = xmlFile(); setXMLFile( QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("ui/ui_standards.rc")) ); if ( !f.isEmpty() ) setXMLFile( f, true ); else { QString auto_file( componentData().componentName() + "ui.rc" ); setXMLFile( auto_file, true ); } guiFactory()->addClient( this ); } // PartManager void KoMainWindow::removePart( KoPart *part ) { if (d->m_registeredPart.data() != part) { return; } d->m_registeredPart = 0; if ( part == d->m_activePart ) { setActivePart(0, 0); } } void KoMainWindow::setActivePart(KoPart *part, QWidget *widget ) { if (part && d->m_registeredPart.data() != part) { warnMain << "trying to activate a non-registered part!" << part->objectName(); return; // don't allow someone call setActivePart with a part we don't know about } // don't activate twice if ( d->m_activePart && part && d->m_activePart == part && (!widget || d->m_activeWidget == widget) ) return; KoPart *oldActivePart = d->m_activePart; QWidget *oldActiveWidget = d->m_activeWidget; d->m_activePart = part; d->m_activeWidget = widget; if (oldActivePart) { KoPart *savedActivePart = part; QWidget *savedActiveWidget = widget; if ( oldActiveWidget ) { disconnect( oldActiveWidget, SIGNAL(destroyed()), this, SLOT(slotWidgetDestroyed()) ); } d->m_activePart = savedActivePart; d->m_activeWidget = savedActiveWidget; } if (d->m_activePart && d->m_activeWidget ) { connect( d->m_activeWidget, SIGNAL(destroyed()), this, SLOT(slotWidgetDestroyed()) ); } // Set the new active instance in KGlobal // KGlobal::setActiveComponent(d->m_activePart ? d->m_activePart->componentData() : KGlobal::mainComponent()); // old slot called from part manager KoPart *newPart = static_cast(d->m_activePart.data()); if (d->activePart && d->activePart == newPart) { //debugMain <<"no need to change the GUI"; return; } KXMLGUIFactory *factory = guiFactory(); if (d->activeView) { factory->removeClient(d->activeView); unplugActionList("toolbarlist"); qDeleteAll(d->toolbarList); d->toolbarList.clear(); } if (!d->mainWindowGuiIsBuilt) { createMainwindowGUI(); } if (newPart && d->m_activeWidget && d->m_activeWidget->inherits("KoView")) { d->activeView = qobject_cast(d->m_activeWidget); d->activeView->actionCollection()->addAction("view_newview", actionCollection()->action("view_newview")); d->activePart = newPart; //debugMain <<"new active part is" << d->activePart; factory->addClient(d->activeView); // Position and show toolbars according to user's preference setAutoSaveSettings(newPart->componentData().componentName(), false); KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); const bool showDockerTitleBar = configGroupInterface.readEntry("ShowDockerTitleBars", true); foreach (QDockWidget *wdg, d->dockWidgets) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { if (wdg->titleBarWidget()) { wdg->titleBarWidget()->setVisible(showDockerTitleBar); } wdg->setVisible(true); } } // Create and plug toolbar list for Settings menu foreach(QWidget* it, factory->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { KToggleAction * act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); d->toolbarList.append(act); } else warnMain << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } plugActionList("toolbarlist", d->toolbarList); } else { d->activeView = 0; d->activePart = 0; } if (d->activeView) { d->activeView->guiActivateEvent(true); } } void KoMainWindow::slotWidgetDestroyed() { debugMain; if ( static_cast( sender() ) == d->m_activeWidget ) setActivePart(0, 0); //do not remove the part because if the part's widget dies, then the //part will delete itself anyway, invoking removePart() in its destructor } void KoMainWindow::slotDocumentTitleModified(const QString &caption, bool mod) { updateCaption(caption, mod); updateReloadFileAction(d->rootDocument); updateVersionsFileAction(d->rootDocument); } void KoMainWindow::showDockerTitleBars(bool show) { foreach (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(show); } } KConfigGroup configGroupInterface = KSharedConfig::openConfig()->group("Interface"); configGroupInterface.writeEntry("ShowDockerTitleBars", show); } diff --git a/libs/main/KoPart.cpp b/libs/main/KoPart.cpp index 27dc8e24aa3..b546a34f2fe 100644 --- a/libs/main/KoPart.cpp +++ b/libs/main/KoPart.cpp @@ -1,386 +1,382 @@ /* This file is part of the KDE project * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2000-2005 David Faure * Copyright (C) 2007-2008 Thorsten Zachmann * Copyright (C) 2010-2012 Boudewijn Rempt * Copyright (C) 2011 Inge Wallin * * 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 "KoPart.h" #include "KoApplication.h" #include "KoMainWindow.h" #include "KoDocument.h" #include "KoView.h" #include "KoOpenPane.h" #include "KoFilterManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef QT_NO_DBUS #include #include "KoPartAdaptor.h" #endif class Q_DECL_HIDDEN KoPart::Private { public: Private(const KoComponentData &componentData_, KoPart *_parent) : parent(_parent) , document(0) - , canvasItem(0) + , proxyWidget(0) , startUpWidget(0) , componentData(componentData_) { } ~Private() { - /// FIXME ok, so this is obviously bad to leave like this - // For now, this is undeleted, but only to avoid an odd double - // delete condition. Until that's discovered, we'll need this - // to avoid crashes in Gemini - //delete canvasItem; + delete proxyWidget; } KoPart *parent; QList views; QList mainWindows; KoDocument *document; QList documents; - QGraphicsItem *canvasItem; + QPointer proxyWidget; QPointer startUpWidget; QString templatesResourcePath; KoComponentData componentData; }; KoPart::KoPart(const KoComponentData &componentData, QObject *parent) : QObject(parent) , d(new Private(componentData, this)) { #ifndef QT_NO_DBUS new KoPartAdaptor(this); QDBusConnection::sessionBus().registerObject('/' + objectName(), this); #endif } KoPart::~KoPart() { // Tell our views that the document is already destroyed and // that they shouldn't try to access it. foreach(KoView *view, views()) { view->setDocumentDeleted(); } while (!d->mainWindows.isEmpty()) { delete d->mainWindows.takeFirst(); } delete d->startUpWidget; d->startUpWidget = 0; delete d; } KoComponentData KoPart::componentData() const { return d->componentData; } void KoPart::setDocument(KoDocument *document) { Q_ASSERT(document); d->document = document; } KoDocument *KoPart::document() const { return d->document; } KoView *KoPart::createView(KoDocument *document, QWidget *parent) { KoView *view = createViewInstance(document, parent); addView(view, document); if (!d->documents.contains(document)) { d->documents.append(document); } return view; } void KoPart::addView(KoView *view, KoDocument *document) { if (!view) return; if (!d->views.contains(view)) { d->views.append(view); } if (!d->documents.contains(document)) { d->documents.append(document); } view->updateReadWrite(document->isReadWrite()); if (d->views.size() == 1) { KoApplication *app = qobject_cast(qApp); if (0 != app) { emit app->documentOpened('/'+objectName()); } } } void KoPart::removeView(KoView *view) { d->views.removeAll(view); if (d->views.isEmpty()) { KoApplication *app = qobject_cast(qApp); if (0 != app) { emit app->documentClosed('/'+objectName()); } } } QList KoPart::views() const { return d->views; } int KoPart::viewCount() const { return d->views.count(); } QGraphicsItem *KoPart::canvasItem(KoDocument *document, bool create) { - if (create && !d->canvasItem) { - d->canvasItem = createCanvasItem(document); + if (create && !d->proxyWidget) { + return createCanvasItem(document); } - return d->canvasItem; + return d->proxyWidget; } QGraphicsItem *KoPart::createCanvasItem(KoDocument *document) { KoView *view = createView(document); - QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); + d->proxyWidget = new QGraphicsProxyWidget(); QWidget *canvasController = view->findChild(); - proxy->setWidget(canvasController); - return proxy; + d->proxyWidget->setWidget(canvasController); + return d->proxyWidget; } void KoPart::addMainWindow(KoMainWindow *mainWindow) { if (d->mainWindows.indexOf(mainWindow) == -1) { debugMain <<"mainWindow" << (void*)mainWindow <<"added to doc" << this; d->mainWindows.append(mainWindow); } } void KoPart::removeMainWindow(KoMainWindow *mainWindow) { debugMain <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this; if (mainWindow) { d->mainWindows.removeAll(mainWindow); } } const QList& KoPart::mainWindows() const { return d->mainWindows; } int KoPart::mainwindowCount() const { return d->mainWindows.count(); } KoMainWindow *KoPart::currentMainwindow() const { QWidget *widget = qApp->activeWindow(); KoMainWindow *mainWindow = qobject_cast(widget); while (!mainWindow && widget) { widget = widget->parentWidget(); mainWindow = qobject_cast(widget); } if (!mainWindow && mainWindows().size() > 0) { mainWindow = mainWindows().first(); } return mainWindow; } void KoPart::openExistingFile(const QUrl &url) { QApplication::setOverrideCursor(Qt::BusyCursor); d->document->openUrl(url); d->document->setModified(false); QApplication::restoreOverrideCursor(); } void KoPart::openTemplate(const QUrl &url) { QApplication::setOverrideCursor(Qt::BusyCursor); bool ok = d->document->loadNativeFormat(url.toLocalFile()); d->document->setModified(false); d->document->undoStack()->clear(); if (ok) { QString mimeType = QMimeDatabase().mimeTypeForUrl(url).name(); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); d->document->setMimeTypeAfterLoading(mimeType); deleteOpenPane(); d->document->resetURL(); d->document->setEmpty(); } else { d->document->showLoadingErrorDialog(); d->document->initEmpty(); } QApplication::restoreOverrideCursor(); } void KoPart::addRecentURLToAllMainWindows(const QUrl &url) { // Add to recent actions list in our mainWindows foreach(KoMainWindow *mainWindow, d->mainWindows) { mainWindow->addRecentURL(url); } } void KoPart::showStartUpWidget(KoMainWindow *mainWindow, bool alwaysShow) { #ifndef NDEBUG if (d->templatesResourcePath.isEmpty()) debugMain << "showStartUpWidget called, but setTemplatesResourcePath() never called. This will not show a lot"; #endif if (!alwaysShow) { KConfigGroup cfgGrp(componentData().config(), "TemplateChooserDialog"); QString fullTemplateName = cfgGrp.readPathEntry("AlwaysUseTemplate", QString()); if (!fullTemplateName.isEmpty()) { QFileInfo fi(fullTemplateName); if (!fi.exists()) { const QString templatesResourcePath = this->templatesResourcePath(); QString desktopfile = KoResourcePaths::findResource("data", templatesResourcePath + "*/" + fullTemplateName); if (desktopfile.isEmpty()) { desktopfile = KoResourcePaths::findResource("data", templatesResourcePath + fullTemplateName); } if (desktopfile.isEmpty()) { fullTemplateName.clear(); } else { QUrl templateURL; KDesktopFile f(desktopfile); templateURL.setPath(QFileInfo(desktopfile).absolutePath() + '/' + f.readUrl()); fullTemplateName = templateURL.toLocalFile(); } } if (!fullTemplateName.isEmpty()) { openTemplate(QUrl::fromUserInput(fullTemplateName)); mainWindows().first()->setRootDocument(d->document, this); return; } } } mainWindow->factory()->container("mainToolBar", mainWindow)->hide(); if (d->startUpWidget) { d->startUpWidget->show(); } else { d->startUpWidget = createOpenPane(mainWindow, d->templatesResourcePath); mainWindow->setCentralWidget(d->startUpWidget); } mainWindow->setPartToOpen(this); } void KoPart::deleteOpenPane(bool closing) { if (d->startUpWidget) { d->startUpWidget->hide(); d->startUpWidget->deleteLater(); if(!closing) { mainWindows().first()->setRootDocument(d->document, this); KoPart::mainWindows().first()->factory()->container("mainToolBar", mainWindows().first())->show(); } } } QList KoPart::createCustomDocumentWidgets(QWidget * /*parent*/) { return QList(); } void KoPart::setTemplatesResourcePath(const QString &templatesResourcePath) { Q_ASSERT(!templatesResourcePath.isEmpty()); Q_ASSERT(templatesResourcePath.endsWith(QLatin1Char('/'))); d->templatesResourcePath = templatesResourcePath; } QString KoPart::templatesResourcePath() const { return d->templatesResourcePath; } void KoPart::startCustomDocument() { deleteOpenPane(); } KoOpenPane *KoPart::createOpenPane(QWidget *parent, const QString& templatesResourcePath) { const QStringList mimeFilter = koApp->mimeFilter(KoFilterManager::Import); KoOpenPane *openPane = new KoOpenPane(parent, mimeFilter, templatesResourcePath); QList widgetList = createCustomDocumentWidgets(openPane); foreach(const CustomDocumentWidgetItem & item, widgetList) { openPane->addCustomDocumentWidget(item.widget, item.title, item.icon); connect(item.widget, SIGNAL(documentSelected()), this, SLOT(startCustomDocument())); } openPane->show(); connect(openPane, SIGNAL(openExistingFile(QUrl)), this, SLOT(openExistingFile(QUrl))); connect(openPane, SIGNAL(openTemplate(QUrl)), this, SLOT(openTemplate(QUrl))); return openPane; } diff --git a/words/part/KWPageManager.cpp b/words/part/KWPageManager.cpp index c59888e1a7e..2a04abe1735 100644 --- a/words/part/KWPageManager.cpp +++ b/words/part/KWPageManager.cpp @@ -1,456 +1,456 @@ /* This file is part of the Calligra project * Copyright (C) 2005-2010 Thomas Zander * Copyright (C) 2008 Pierre Ducroquet * Copyright (C) 2008,2011 Sebastian Sauer * * 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 "KWPageManager.h" #include "KWPageManager_p.h" #include "KWPageStyle_p.h" #include "KWPage.h" #include "KWDocument.h" #include #include #define DEBUG_PAGES KWPageManagerPrivate::KWPageManagerPrivate() : lastId(0), defaultPageStyle(QLatin1String("Standard")) // don't translate, used as identifier! { } qreal KWPageManagerPrivate::pageOffset(int pageNum/*, bool bottom*/) const { #if 0 Q_ASSERT(pageNum >= 0); qreal offset = 0.0; const qreal totalPadding = padding.top + padding.bottom; QMap::const_iterator iter = pageNumbers.constBegin(); for (;iter != pageNumbers.constEnd(); ++iter) { const KWPageManagerPrivate::Page &page = pages[iter.value()]; if (page.pageSide == KWPage::PageSpread && iter.key() % 2 == 1) continue; if (iter.key() == pageNum) { if (bottom) offset += page.style.pageLayout().height; break; } offset += page.style.priv()->pageLayout.height + totalPadding; } return offset; #else //Q_ASSERT(pageOffsets.contains(pageNum)); qreal offset = pageOffsets.value(pageNum); //debugWords << "pageNum=" << pageNum << "offset=" << offset; return offset; #endif } void KWPageManagerPrivate::setPageOffset(int pageNum, qreal offset) { pageOffsets[pageNum] = offset; } void KWPageManagerPrivate::setVisiblePageNumber(int pageId, int newPageNumber) { #if 0 if (pageNumbers.isEmpty() || ! pages.contains(pageId)) return; const int oldPageNumber = pages[pageId].pageNumber; int diff = newPageNumber - oldPageNumber; int from = oldPageNumber; int to = newPageNumber; if (from > to) qSwap(from, to); QMap oldPageNumbers = pageNumbers; // backup QHash oldPages = pages; // backup pageNumbers.clear(); pages.clear(); foreach (int id, oldPages.keys()) { Page page = oldPages[id]; if (diff < 0 && page.pageNumber >= from && page.pageNumber < to) { warnWords << "you requested to change the page number to a number that already exist, all will end soon"; return; } #ifdef DEBUG_PAGES const int oldPageNumber = page.pageNumber; // debug only #endif if (page.pageNumber >= from) page.pageNumber += diff; #ifdef DEBUG_PAGES debugWords << "adjusting page number from" << oldPageNumber << "to" << page.pageNumber << "side" << page.pageSide; #endif if (page.pageSide == KWPage::PageSpread) { if (page.pageNumber % 2 == 1) { // pagespreads can only be on even pageNumbers page.pageNumber++; diff++; } pageNumbers.insert(page.pageNumber + 1, id); } else { page.pageSide = page.pageNumber % 2 == 0 ? KWPage::Left : KWPage::Right; } pageNumbers.insert(page.pageNumber, id); pages.insert(id, page); } Q_ASSERT(pages.count() == oldPages.count()); // don't loose anything :) #else if (newPageNumber >= 0) visiblePageNumbers[pageId] = newPageNumber; else visiblePageNumbers.remove(pageId); #endif } void KWPageManagerPrivate::insertPage(const Page &newPage) { #ifdef DEBUG_PAGES debugWords << "pageNumber=" << newPage.pageNumber; #endif // increase the pagenumbers of pages following the pageNumber if (!pageNumbers.isEmpty()) { const QMap numbers = pageNumbers; QMap::ConstIterator iter = numbers.end(); do { --iter; if (iter.key() < newPage.pageNumber) break; KWPageManagerPrivate::Page page = pages[iter.value()]; pageNumbers.remove(iter.key()); page.pageNumber += 1; pages.insert(iter.value(), page); pageNumbers.insert(iter.key() + 1, iter.value()); } while (iter != numbers.begin()); } pages.insert(++lastId, newPage); Q_ASSERT(! pageNumbers.contains(newPage.pageNumber)); pageNumbers.insert(newPage.pageNumber, lastId); } /////////// KWPageManager::KWPageManager() : d (new KWPageManagerPrivate()) { addPageStyle(d->defaultPageStyle); } KWPageManager::~KWPageManager() { delete d; } int KWPageManager::pageNumber(const QPointF &point) const { qreal startOfpage = 0.0; int answer = -1; QMap::const_iterator iter = d->pageNumbers.constBegin(); for (;iter != d->pageNumbers.constEnd(); ++iter) { const KWPageManagerPrivate::Page page = d->pages[iter.value()]; startOfpage += page.style.pageLayout().height + d->padding.top + d->padding.bottom; answer = iter.key(); if (startOfpage >= point.y()) break; } return answer; } int KWPageManager::pageNumber(const KoShape *shape) const { return pageNumber(shape->absolutePosition()); } int KWPageManager::pageNumber(const qreal y) const { return pageNumber(QPointF(0, y)); } int KWPageManager::pageCount() const { int count = 0; QHash::const_iterator iter = d->pages.constBegin(); while (iter != d->pages.constEnd()) { ++count; ++iter; } return count; } KWPage KWPageManager::page(int pageNum) const { if (d->pageNumbers.contains(pageNum)) return KWPage(d, d->pageNumbers.value(pageNum)); #ifdef DEBUG_PAGES warnWords << "KWPageManager::page(" << pageNum << ") failed; Requested page does not exist"; #endif return KWPage(); } KWPage KWPageManager::page(const KoShape *shape) const { return page(pageNumber(shape)); } KWPage KWPageManager::page(const QPointF &point) const { return page(pageNumber(point)); } KWPage KWPageManager::page(qreal y) const { return page(pageNumber(y)); } KWPage KWPageManager::insertPage(int pageNumber, const KWPageStyle &pageStyle) { if (pageNumber <= 0 || d->pages.isEmpty() || pageNumber > last().pageNumber()) return appendPage(pageStyle); #ifdef DEBUG_PAGES debugWords << "pageNumber=" << pageNumber << "pageStyle=" << (pageStyle.isValid() ? pageStyle.name() : QString()); #endif KWPageManagerPrivate::Page newPage; newPage.style = pageStyle; KWPage prevPage = page(pageNumber - 1); if (prevPage.isValid()) { if (! newPage.style.isValid()) newPage.style = prevPage.pageStyle(); } if (! newPage.style.isValid()) newPage.style = defaultPageStyle(); newPage.pageNumber = pageNumber; if (newPage.pageNumber % 2 == 0) { newPage.pageSide = KWPage::Left; } else { newPage.pageSide = KWPage::Right; } d->insertPage(newPage); return KWPage(d, d->lastId); } KWPage KWPageManager::appendPage(const KWPageStyle &pageStyle) { KWPageManagerPrivate::Page page; if (! d->pages.isEmpty()) { QMap::ConstIterator end = d->pageNumbers.constEnd(); --end; // last one is one before the imaginary 'end' KWPageManagerPrivate::Page lastPage = d->pages[end.value()]; page = lastPage; ++page.pageNumber; } else { page.pageNumber = 1; } if (pageStyle.isValid()) { page.style = pageStyle; } else { if (page.style.isValid()) { QString nextPageMasterStyleName = page.style.nextStyleName(); KWPageStyle nextPageMasterStyle = this->pageStyle(nextPageMasterStyleName); if (nextPageMasterStyle.isValid()) { page.style = nextPageMasterStyle; } } } if (!page.style.isValid()) { page.style = defaultPageStyle(); } if (page.pageNumber % 2 == 0) { page.pageSide = KWPage::Left; } else { page.pageSide = KWPage::Right; } d->pages.insert(++d->lastId, page); d->pageNumbers.insert(page.pageNumber, d->lastId); #ifdef DEBUG_PAGES debugWords << "pageNumber=" << page.pageNumber << "pageCount=" << pageCount() << "pageStyle=" << (pageStyle.isValid() ? pageStyle.name() : QString()); #endif return KWPage(d, d->lastId); } qreal KWPageManager::topOfPage(int pageNum) const { return d->pageOffset(pageNum); } qreal KWPageManager::bottomOfPage(int pageNum) const { KWPage p = page(pageNum); Q_ASSERT(p.isValid()); return d->pageOffset(pageNum) + p.height(); } void KWPageManager::removePage(int pageNumber) { removePage(page(pageNumber)); } void KWPageManager::removePage(const KWPage &page) { Q_ASSERT(page.isValid()); debugWords << page.pageNumber(); const int removedPageNumber = page.pageNumber(); d->pages.remove(d->pageNumbers[removedPageNumber]); d->visiblePageNumbers.remove(removedPageNumber); // decrease the pagenumbers of pages following the pageNumber const QMap pageNumbers = d->pageNumbers; QMap::ConstIterator iter = pageNumbers.begin(); while (iter != pageNumbers.end()) { if (iter.key() < removedPageNumber) { // don't touch those } else if (iter.key() > removedPageNumber) { KWPageManagerPrivate::Page page = d->pages[iter.value()]; d->pageNumbers.remove(iter.key()); page.pageNumber--; d->pages.insert(iter.value(), page); d->pageNumbers.insert(page.pageNumber, iter.value()); } else { d->pageNumbers.remove(iter.key()); } ++iter; } #ifdef DEBUG_PAGES debugWords << "pageNumber=" << removedPageNumber << "pageCount=" << pageCount(); #endif } QVector KWPageManager::pages(const QString &pageStyle) const { QVector answer; const bool checkForStyle = !pageStyle.isEmpty(); QHash::ConstIterator it = d->pages.constBegin(); QHash::ConstIterator end = d->pages.constEnd(); for(; it != end; ++it) { if (checkForStyle && it.value().style.name() != pageStyle) continue; answer << KWPage(d, it.key()); } std::sort(answer.begin(), answer.end()); return answer; } QHash KWPageManager::pageStyles() const { return d->pageStyles; } KWPageStyle KWPageManager::pageStyle(const QString &name) const { if (d->pageStyles.contains(name)) return d->pageStyles[name]; if (d->pageStyleNames.contains(name)) return d->pageStyles[d->pageStyleNames[name]]; return KWPageStyle(); } void KWPageManager::addPageStyle(const KWPageStyle &pageStyle) { Q_ASSERT(!pageStyle.name().isEmpty()); Q_ASSERT(pageStyle.isValid()); d->pageStyles.insert(pageStyle.name(), pageStyle); if (!pageStyle.displayName().isEmpty()) d->pageStyleNames.insert(pageStyle.displayName(), pageStyle.name()); } void KWPageManager::removePageStyle(const KWPageStyle &pageStyle) { KWPageStyle style = d->pageStyles.value(pageStyle.name()); Q_ASSERT(style == pageStyle); d->pageStyles.remove(pageStyle.name()); Q_ASSERT(!d->pageStyleNames.contains(pageStyle.displayName()) || d->pageStyleNames[pageStyle.displayName()] == pageStyle.name()); d->pageStyleNames.remove(pageStyle.displayName()); } KWPageStyle KWPageManager::defaultPageStyle() const { return d->defaultPageStyle; } void KWPageManager::clearPageStyles() { d->pageStyles.clear(); d->pageStyleNames.clear(); d->defaultPageStyle = KWPageStyle(QLatin1String("Standard")); // don't translate, used as identifier! addPageStyle(d->defaultPageStyle); } const KWPage KWPageManager::begin() const { - if (d->pages.isEmpty()) + if (d->pages.isEmpty() || d->pageNumbers.empty()) return KWPage(); return KWPage(d, d->pageNumbers.constBegin().value()); } const KWPage KWPageManager::last() const { - if (d->pages.isEmpty()) + if (d->pages.isEmpty() || d->pageNumbers.empty()) return KWPage(); QMap::ConstIterator end = d->pageNumbers.constEnd(); --end; // last one is one before the imaginary 'end' return KWPage(d, end.value()); } KWPage KWPageManager::begin() { - if (d->pages.isEmpty()) + if (d->pages.isEmpty() || d->pageNumbers.empty()) return KWPage(); return KWPage(d, d->pageNumbers.begin().value()); } KWPage KWPageManager::last() { - if (d->pages.isEmpty()) + if (d->pages.isEmpty() || d->pageNumbers.empty()) return KWPage(); QMap::ConstIterator end = d->pageNumbers.constEnd(); --end; // last one is one before the imaginary 'end' return KWPage(d, end.value()); } KoInsets KWPageManager::padding() const { return d->padding; } void KWPageManager::setPadding(const KoInsets &padding) { d->padding = padding; }