Warning, file /office/calligra/gemini/MainWindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  * SPDX-FileCopyrightText: 2012 KO GmbH. Contact : Boudewijn Rempt <boud@kogmbh.com>
0004  * SPDX-FileCopyrightText: 2013 Dan Leinir Turthra Jensen <admin@leinir.dk>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #include "MainWindow.h"
0010 #include <QHBoxLayout>
0011 #include "desktopviewproxy.h"
0012 
0013 #include <QApplication>
0014 #include <QResizeEvent>
0015 #include <QQuickWidget>
0016 #include <QQmlContext>
0017 #include <QQmlEngine>
0018 #include <QGraphicsObject>
0019 #include <QDir>
0020 #include <QFile>
0021 #include <QMessageBox>
0022 #include <QToolButton>
0023 #include <QMenuBar>
0024 #include <QAction>
0025 #include <QDesktopWidget>
0026 #include <QFileInfo>
0027 #include <QUrl>
0028 #include <QStandardPaths>
0029 
0030 #include <kactioncollection.h>
0031 #include <ktoolbar.h>
0032 #include <kmessagebox.h>
0033 #include <KConfigGroup>
0034 #include <KSharedConfig>
0035 
0036 #include <gemini/ViewModeSwitchEvent.h>
0037 #include <KoCanvasBase.h>
0038 #include <KoToolManager.h>
0039 #include <KoMainWindow.h>
0040 #include <KoGlobal.h>
0041 #include <KoDocumentInfo.h>
0042 #include <KoView.h>
0043 #include <KoPart.h>
0044 #include <KoDocumentEntry.h>
0045 #include <KoFilterManager.h>
0046 #include <part/KWFactory.h>
0047 #include <stage/part/KPrDocument.h>
0048 #include <stage/part/KPrFactory.h>
0049 #include <stage/part/KPrViewModePresentation.h>
0050 #include <KoAbstractGradient.h>
0051 #include <KoZoomController.h>
0052 #include <KoFileDialog.h>
0053 #include <KoDialog.h>
0054 #include <KoIcon.h>
0055 
0056 #include "PropertyContainer.h"
0057 #include "RecentFileManager.h"
0058 #include "DocumentManager.h"
0059 #include "QmlGlobalEngine.h"
0060 #include "Settings.h"
0061 #include "Theme.h"
0062 #include "DocumentListModel.h"
0063 #include "Constants.h"
0064 #include "SimpleTouchArea.h"
0065 #include "ToolManager.h"
0066 #include "ParagraphStylesModel.h"
0067 #include "KeyboardModel.h"
0068 #include "ScribbleArea.h"
0069 #include "RecentImageImageProvider.h"
0070 #include "RecentFilesModel.h"
0071 #include "TemplatesModel.h"
0072 #include "CloudAccountsModel.h"
0073 
0074 #ifdef Q_OS_WIN
0075 // Slate mode/docked detection stuff
0076 #include <Shellapi.h>
0077 #define SM_CONVERTIBLESLATEMODE 0x2003
0078 #define SM_SYSTEMDOCKED         0x2004
0079 #endif
0080 
0081 class MainWindow::Private
0082 {
0083 public:
0084     Private(MainWindow* qq)
0085         : q(qq)
0086         , allowClose(true)
0087         , touchView(0)
0088         , desktopView(0)
0089         , currentView(0)
0090         , settings(0)
0091         , slateMode(false)
0092         , docked(false)
0093         , touchKoView(0)
0094         , touchEventReceiver(0)
0095         , desktopKoView(0)
0096         , desktopViewProxy(0)
0097         , forceDesktop(false)
0098         , forceTouch(false)
0099         , temporaryFile(false)
0100         , syncObject(0)
0101         , toDesktop(0)
0102         , toTouch(0)
0103         , switcher(0)
0104         , alternativeSaveAction(0)
0105     {
0106 #ifdef Q_OS_WIN
0107         // slateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
0108         // docked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0);
0109 #endif
0110         fullScreenThrottle = new QTimer(qq);
0111         fullScreenThrottle->setInterval(500);
0112         fullScreenThrottle->setSingleShot(true);
0113     }
0114     MainWindow* q;
0115     bool allowClose;
0116     QQuickWidget* touchView;
0117     QPointer<KoMainWindow> desktopView;
0118     QObject* currentView;
0119     Settings *settings;
0120 
0121     bool slateMode;
0122     bool docked;
0123     QString currentTouchPage;
0124     KoView* touchKoView;
0125     QObject* touchEventReceiver;
0126     KoView* desktopKoView;
0127     DesktopViewProxy* desktopViewProxy;
0128 
0129     bool forceDesktop;
0130     bool forceTouch;
0131     bool temporaryFile;
0132     ViewModeSynchronisationObject* syncObject;
0133 
0134     QAction* toDesktop;
0135     QAction* toTouch;
0136     QToolButton* switcher;
0137     QAction* alternativeSaveAction;
0138     QTimer* fullScreenThrottle;
0139 
0140     void shouldAcceptTouchEvents(QWidget* widget) {
0141         // See https://bugreports.qt.io/browse/QTBUG-66718
0142         static QVersionNumber qtVersion = QVersionNumber::fromString(qVersion());
0143         static bool shouldWidgetAcceptTouchEvents = qtVersion > QVersionNumber(5, 9, 3) && qtVersion.normalized() != QVersionNumber(5, 10);
0144         if(shouldWidgetAcceptTouchEvents)
0145         {
0146             widget->setAttribute(Qt::WA_AcceptTouchEvents, true);
0147         }
0148     }
0149 
0150     void initTouchView(QObject* parent)
0151     {
0152         touchView = new QQuickWidget();
0153         shouldAcceptTouchEvents(touchView);
0154         QmlGlobalEngine::instance()->setEngine(touchView->engine());
0155         touchView->engine()->rootContext()->setContextObject(new KLocalizedContext(touchView));
0156         touchView->engine()->addImageProvider(QLatin1String("recentimage"), new RecentImageImageProvider);
0157         touchView->engine()->rootContext()->setContextProperty("mainWindow", parent);
0158 
0159         settings = new Settings( q );
0160         DocumentManager::instance()->setSettingsManager( settings );
0161         touchView->engine()->rootContext()->setContextProperty("DocumentManager", DocumentManager::instance());
0162         touchView->engine()->rootContext()->setContextProperty("Settings", settings);
0163         touchView->engine()->rootContext()->setContextProperty("Constants", new Constants( q ));
0164         touchView->engine()->rootContext()->setContextProperty("RecentFileManager", DocumentManager::instance()->recentFileManager());
0165         touchView->engine()->rootContext()->setContextProperty("WORDS_MIME_TYPE", QString(WORDS_MIME_TYPE));
0166         touchView->engine()->rootContext()->setContextProperty("STAGE_MIME_TYPE", QString(STAGE_MIME_TYPE));
0167 
0168 #ifdef Q_OS_WIN
0169         QDir appdir(qApp->applicationDirPath());
0170 
0171         // Corrects for mismatched case errors in path (qtdeclarative fails to load)
0172         wchar_t buffer[1024];
0173         QString absolute = appdir.absolutePath();
0174         DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024);
0175         rv = ::GetLongPathName(buffer, buffer, 1024);
0176         QString correctedPath((QChar *)buffer);
0177         appdir.setPath(correctedPath);
0178 
0179         // for now, the app in bin/ and we still use the env.bat script
0180         appdir.cdUp();
0181 
0182         // QT5TODO: adapt to QML_IMPORT_PATH usage and install to ${QML_INSTALL_DIR}
0183         touchView->engine()->addImportPath(appdir.canonicalPath() + "/imports");
0184         touchView->engine()->addImportPath(appdir.canonicalPath() + "/lib/calligra/imports");
0185         touchView->engine()->addImportPath(appdir.canonicalPath() + "/lib64/calligra/imports");
0186         touchView->engine()->addImportPath(appdir.canonicalPath() + "/bin/data/calligragemini");
0187         QString mainqml = appdir.canonicalPath() + "/bin/data/calligragemini/calligragemini.qml";
0188 #else
0189         QString mainqml = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("calligragemini/calligragemini.qml"));
0190 #endif
0191 
0192         Q_ASSERT(QFile::exists(mainqml));
0193         if (!QFile::exists(mainqml)) {
0194             QMessageBox::warning(0, "No QML found", mainqml + " doesn't exist.");
0195         }
0196         QFileInfo fi(mainqml);
0197 
0198         touchView->setSource(QUrl::fromLocalFile(fi.canonicalFilePath()));
0199         touchView->setResizeMode( QQuickWidget::SizeRootObjectToView );
0200 
0201         toDesktop = new QAction(q);
0202         toDesktop->setEnabled(true);
0203         toDesktop->setText(tr("Switch to Desktop"));
0204         // useful for monkey-testing to crash...
0205         //toDesktop->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
0206         //q->addAction(toDesktop);
0207         //connect(toDesktop, SIGNAL(triggered(bool)), q, SLOT(switchDesktopForced()));
0208         connect(toDesktop, &QAction::triggered, q, &MainWindow::switchToDesktop);
0209         touchView->engine()->rootContext()->setContextProperty("switchToDesktopAction", toDesktop);
0210     }
0211 
0212     void initDesktopView()
0213     {
0214         if(settings->currentFile().isEmpty()) {
0215             return;
0216         }
0217         // Initialize all Calligra directories etc.
0218         KoGlobal::initialize();
0219 
0220         // The default theme is not what we want for Gemini
0221         KConfigGroup group(KSharedConfig::openConfig(), "theme");
0222         if(group.readEntry("Theme", "no-theme-is-set") == QLatin1String("no-theme-is-set")) {
0223             group.writeEntry("Theme", "Krita-dark");
0224         }
0225 
0226         if(settings->currentFileClass() == WORDS_MIME_TYPE) {
0227             qApp->setApplicationName("calligrawords");
0228             desktopView = new KoMainWindow(WORDS_MIME_TYPE, KWFactory::componentData());
0229         } else if(settings->currentFileClass() == STAGE_MIME_TYPE) {
0230             qApp->setApplicationName("calligrastage");
0231             desktopView = new KoMainWindow(STAGE_MIME_TYPE, KPrFactory::componentData());
0232         } else {
0233             desktopView = 0;
0234             qDebug() << "Big trouble, things gonna break. desktopView is not created." << settings->currentFileClass();
0235             return;
0236         }
0237 
0238         toTouch = new QAction(desktopView);
0239         toTouch->setEnabled(false);
0240         toTouch->setText(tr("Switch to Touch"));
0241         toTouch->setIcon(koIcon("system-reboot"));
0242         toTouch->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S);
0243         //connect(toTouch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchTouchForced()));
0244         connect(toTouch, &QAction::triggered, q, &MainWindow::switchToTouch);
0245         desktopView->actionCollection()->addAction("SwitchToTouchView", toTouch);
0246         switcher = new QToolButton();
0247         switcher->setEnabled(false);
0248         switcher->setText(tr("Switch to Touch"));
0249         switcher->setIcon(koIcon("system-reboot"));
0250         switcher->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0251         //connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchDesktopForced()));
0252         connect(switcher, &QAbstractButton::clicked, q, &MainWindow::switchToTouch);
0253         desktopView->menuBar()->setCornerWidget(switcher);
0254 
0255         // DesktopViewProxy connects itself up to everything appropriate on construction,
0256         // and destroys itself again when the view is removed
0257         desktopViewProxy = new DesktopViewProxy(q, desktopView);
0258         connect(desktopViewProxy, &DesktopViewProxy::documentSaved, q, &MainWindow::documentSaved);
0259         connect(desktopViewProxy, &DesktopViewProxy::documentSaved, q, &MainWindow::resetWindowTitle);
0260         connect(desktopViewProxy, &DesktopViewProxy::documentSaved, q, &MainWindow::enableAltSaveAction);
0261     }
0262 
0263     void notifySlateModeChange();
0264     void notifyDockingModeChange();
0265     bool queryClose();
0266     void altSaveQuery();
0267 };
0268 
0269 MainWindow::MainWindow(QStringList fileNames, QWidget* parent, Qt::WindowFlags flags )
0270     : QMainWindow( parent, flags ), d( new Private(this) )
0271 {
0272     qmlRegisterUncreatableType<PropertyContainer>("org.calligra", 1, 0, "PropertyContainer", "Contains properties and naively extends QML to support dynamic properties");
0273     qmlRegisterType<Theme>("org.calligra", 1, 0, "Theme");
0274     qmlRegisterType<DocumentListModel>("org.calligra", 1, 0, "DocumentListModel");
0275     qmlRegisterType<SimpleTouchArea>("org.calligra", 1, 0, "SimpleTouchArea");
0276     qmlRegisterType<ToolManager>("org.calligra", 1, 0, "ToolManager");
0277     qmlRegisterType<ParagraphStylesModel>("org.calligra", 1, 0, "ParagraphStylesModel");
0278     qmlRegisterType<KeyboardModel>("org.calligra", 1, 0, "KeyboardModel");
0279     qmlRegisterType<ScribbleArea>("org.calligra", 1, 0, "ScribbleArea");
0280     qmlRegisterType<RecentFilesModel>("org.calligra", 1, 0, "RecentFilesModel");
0281     qmlRegisterType<TemplatesModel>("org.calligra", 1, 0, "TemplatesModel");
0282     qmlRegisterType<CloudAccountsModel>("org.calligra", 1, 0, "CloudAccountsModel");
0283     qmlRegisterType<KPrViewModePresentation>();
0284     qRegisterMetaType<QAction*>();
0285 
0286     qApp->setActiveWindow( this );
0287     setWindowTitle(i18n("Calligra Gemini"));
0288     setWindowIcon(koIcon("calligragemini"));//gemini"));
0289     resize(QApplication::desktop()->availableGeometry().size() * 3/4);
0290     d->shouldAcceptTouchEvents(this);
0291 
0292     foreach(const QString &fileName, fileNames) {
0293         DocumentManager::instance()->recentFileManager()->addRecent( QDir::current().absoluteFilePath( fileName ) );
0294     }
0295 
0296     connect(DocumentManager::instance(), &DocumentManager::documentChanged, this, &MainWindow::documentChanged);
0297     connect(DocumentManager::instance(), &DocumentManager::documentChanged, this, &MainWindow::resetWindowTitle);
0298     connect(DocumentManager::instance(), &DocumentManager::documentSaved, this, &MainWindow::resetWindowTitle);
0299     connect(DocumentManager::instance(), &DocumentManager::documentSaved, this, &MainWindow::enableAltSaveAction);
0300     connect(DocumentManager::instance(), &DocumentManager::aboutToDeleteDocument, this, &MainWindow::closeWindow);
0301 
0302     d->initTouchView(this);
0303 
0304     // Set the initial view to touch... because reasons.
0305     // Really, this allows us to show the pleasant welcome screen from Touch
0306     switchToTouch();
0307 
0308     if(!fileNames.isEmpty()) {
0309         //It feels a little hacky, but call a QML function to open files.
0310         //This saves a lot of hassle required to change state for loading dialogs etc.
0311         QMetaObject::invokeMethod(d->touchView->rootObject(), "openFile", Q_ARG(QVariant, fileNames.at(0)));
0312     }
0313 }
0314 
0315 void MainWindow::resetWindowTitle()
0316 {
0317     KoDocument* document = DocumentManager::instance()->document();
0318     if (!document)
0319         return;
0320     QUrl url = document->url();
0321     QString fileName = url.fileName();
0322     if(url.scheme() == "temp" || url.isEmpty())
0323         fileName = i18n("Untitled");
0324 
0325     KoDialog::CaptionFlags flags = KoDialog::HIGCompliantCaption;
0326     if ( document->isModified() ) {
0327         flags |= KoDialog::ModifiedCaption;
0328     }
0329 
0330     setWindowTitle( KoDialog::makeStandardCaption(fileName, this, flags) );
0331 }
0332 
0333 void MainWindow::switchDesktopForced()
0334 {
0335     if (d->slateMode)
0336         d->forceDesktop = true;
0337     d->forceTouch = false;
0338 }
0339 
0340 void MainWindow::switchTouchForced()
0341 {
0342     if (!d->slateMode)
0343         d->forceTouch = true;
0344     d->forceDesktop = false;
0345 }
0346 
0347 void MainWindow::switchToTouch()
0348 {
0349     QElapsedTimer timer;
0350     timer.start();
0351     qDebug() << "Switching to touch";
0352 
0353     if (d->toTouch)
0354     {
0355         d->toTouch->setEnabled(false);
0356         d->switcher->setEnabled(false);
0357     }
0358 
0359     d->syncObject = new ViewModeSynchronisationObject;
0360 
0361     if (d->desktopView && centralWidget() == d->desktopView) {
0362         if (KoView* view = d->desktopView->rootView()) {
0363             //Notify the view we are switching away from that we are about to switch away from it
0364             //giving it the possibility to set up the synchronisation object.
0365             ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, view, d->touchView, d->syncObject);
0366             QApplication::sendEvent(view, &aboutToSwitchEvent);
0367         }
0368         d->desktopView->setParent(0);
0369     }
0370 
0371     setCentralWidget(d->touchView);
0372     qApp->processEvents();
0373     d->touchView->setVisible(true);
0374     resize(size());
0375     emit switchedToTouch();
0376 
0377     if (d->slateMode) {
0378         if (d->syncObject->initialized)
0379             QTimer::singleShot(50, this, &MainWindow::touchChange);
0380     }
0381     else
0382         QTimer::singleShot(50, this, &MainWindow::touchChange);
0383 
0384     //qDebug() << "milliseconds to switch to touch:" << timer.elapsed();
0385 }
0386 
0387 void MainWindow::touchChange()
0388 {
0389     if (centralWidget() != d->touchView || !d->syncObject)
0390         return;
0391 
0392     if (d->desktopView)
0393     {
0394         //if (/*!d->touchKoView ||*/ !d->touchView->canvasWidget())
0395         //{
0396         //    QTimer::singleShot(100, this, SLOT(touchChange()));
0397         //    return;
0398         //}
0399         qApp->processEvents();
0400         KoView* view = d->desktopView->rootView();
0401         //Notify the new view that we just switched to it, passing our synchronisation object
0402         //so it can use those values to sync with the old view.
0403         ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToTouchModeEvent, view, d->touchView, d->syncObject);
0404         QApplication::sendEvent(d->touchEventReceiver, &switchedEvent);
0405         d->syncObject = 0;
0406         qApp->processEvents();
0407     }
0408     if (d->toDesktop)
0409     {
0410         qApp->processEvents();
0411         d->toDesktop->setEnabled(true);
0412     }
0413 }
0414 
0415 void MainWindow::switchToDesktop()
0416 {
0417     QElapsedTimer timer;
0418     timer.start();
0419     qDebug() << "Switching to desktop";
0420 
0421     if (d->toDesktop)
0422         d->toDesktop->setEnabled(false);
0423 
0424     ViewModeSynchronisationObject* syncObject = new ViewModeSynchronisationObject;
0425 
0426     KoView* view = 0;
0427     if (d->desktopView) {
0428         view = d->desktopView->rootView();
0429     }
0430 
0431     if (!view) {
0432         return;
0433     }
0434 
0435     //Notify the view we are switching away from that we are about to switch away from it
0436     //giving it the possibility to set up the synchronisation object.
0437     ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, d->touchView, view, syncObject);
0438     QApplication::sendEvent(d->touchEventReceiver, &aboutToSwitchEvent);
0439     qApp->processEvents();
0440 
0441     if (d->currentTouchPage == "MainPage")
0442     {
0443         d->touchView->setParent(0);
0444         d->touchView->setVisible(false);
0445         setCentralWidget(d->desktopView);
0446     }
0447 
0448     //Notify the new view that we just switched to it, passing our synchronisation object
0449     //so it can use those values to sync with the old view.
0450     ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToDesktopModeEvent, d->touchView, view, syncObject);
0451     QApplication::sendEvent(view, &switchedEvent);
0452 
0453     qApp->processEvents();
0454     d->toTouch->setEnabled(true);
0455     d->switcher->setEnabled(true);
0456 
0457     //qDebug() << "milliseconds to switch to desktop:" << timer.elapsed();
0458 }
0459 
0460 void MainWindow::setDocAndPart(QObject* document, QObject* part)
0461 {
0462     if(DocumentManager::instance()->document()) {
0463         disconnect(DocumentManager::instance()->document(), &KoDocument::modified, this, &MainWindow::resetWindowTitle);
0464     }
0465     qDebug() << "Attempting to set doc and part to" << document << "and" << part;
0466     d->touchEventReceiver = d->touchView->rootObject()->findChild<QQuickItem*>("controllerItem");
0467     DocumentManager::instance()->setDocAndPart(qobject_cast<KoDocument*>(document), qobject_cast<KoPart*>(part));
0468     if(DocumentManager::instance()->document()) {
0469         connect(DocumentManager::instance()->document(), &KoDocument::modified, this, &MainWindow::resetWindowTitle);
0470     }
0471     if(document && part && !d->settings->currentFile().isEmpty()) {
0472         QAction* undo = qobject_cast<KoPart*>(part)->views().at(0)->action("edit_undo");
0473         d->touchView->rootContext()->setContextProperty("undoaction", undo);
0474         QAction* redo = qobject_cast<KoPart*>(part)->views().at(0)->action("edit_redo");
0475         d->touchView->rootContext()->setContextProperty("redoaction", redo);
0476     }
0477     resetWindowTitle();
0478 }
0479 
0480 void MainWindow::documentChanged()
0481 {
0482     if (d->desktopView) {
0483         d->desktopView->deleteLater();
0484         d->desktopView = 0;
0485         qApp->processEvents();
0486     }
0487     d->initDesktopView();
0488     if(d->desktopView) {
0489         d->desktopView->setRootDocument(DocumentManager::instance()->document(), DocumentManager::instance()->part(), false);
0490         qApp->processEvents();
0491         d->desktopKoView = d->desktopView->rootView();
0492         emit desktopKoViewChanged();
0493     //    d->desktopKoView->setQtMainWindow(d->desktopView);
0494     //    connect(d->desktopKoView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start()));
0495     //    connect(d->desktopKoView, SIGNAL(sigSavingFinished()), this, SLOT(resetWindowTitle()));
0496     //    KWView* wordsview = qobject_cast<KWView*>(d->desktopView->rootView());
0497     //    if(wordsview) {
0498     //        connect(wordsview->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)),
0499     //                this, SLOT(resourceChanged(int, const QVariant&)));
0500     //    }
0501         if (!d->forceTouch && !d->slateMode)
0502             switchToDesktop();
0503     }
0504 }
0505 
0506 bool MainWindow::allowClose() const
0507 {
0508     return d->allowClose;
0509 }
0510 
0511 void MainWindow::setAllowClose(bool allow)
0512 {
0513     d->allowClose = allow;
0514 }
0515 
0516 bool MainWindow::slateMode() const
0517 {
0518     return d->slateMode;
0519 }
0520 
0521 QString MainWindow::currentTouchPage() const
0522 {
0523     return d->currentTouchPage;
0524 }
0525 
0526 void MainWindow::setCurrentTouchPage(QString newPage)
0527 {
0528     d->currentTouchPage = newPage;
0529     emit currentTouchPageChanged();
0530 
0531     if (newPage == "MainPage")
0532     {
0533         if (!d->forceTouch && !d->slateMode)
0534         {
0535             // Just loaded to desktop, do nothing
0536         }
0537         else
0538         {
0539             //QTimer::singleShot(3000, this, SLOT(adjustZoomOnDocumentChangedAndStuff()));
0540         }
0541     }
0542 }
0543 
0544 void MainWindow::setAlternativeSaveAction(QAction* altAction)
0545 {
0546     // if mainwindow exists, and alt action exists, remove alt action from current mainwindow
0547     if(d->desktopView && d->alternativeSaveAction) {
0548         d->desktopView->actionCollection()->removeAction(d->alternativeSaveAction);
0549         d->desktopView->actionCollection()->action("file_save")->disconnect(d->alternativeSaveAction);
0550     }
0551     d->alternativeSaveAction = altAction;
0552     // if mainwindow exists, set alt action into current mainwindow
0553     if(d->desktopView && d->alternativeSaveAction) {
0554         QAction* cloudSave = d->desktopView->actionCollection()->addAction("cloud_save", d->alternativeSaveAction);
0555         KToolBar* tb = d->desktopView->toolBar("mainToolBar");
0556         if(tb) {
0557             tb->removeAction(cloudSave);
0558             // find the action /after/ the save action (because we want the alt save there, not before it)
0559             QAction* saveAction = d->desktopView->actionCollection()->action("file_save");
0560             QAction* afterSave = 0;
0561             bool useNext = false;
0562             Q_FOREACH(QAction* action, tb->actions()) {
0563                 if(useNext) {
0564                     afterSave = action;
0565                     break;
0566                 }
0567                 if(action == saveAction) {
0568                     useNext = true;
0569                 }
0570             }
0571             if(afterSave) {
0572                 tb->insertAction(afterSave, cloudSave);
0573             } else {
0574                 tb->addAction(cloudSave);
0575             }
0576         }
0577     }
0578     if(d->alternativeSaveAction) {
0579         // disabled for a start - this is called on load completion, so let's just assume we're not ready to reupload yet
0580         d->alternativeSaveAction->setEnabled(false);
0581     }
0582 }
0583 
0584 void MainWindow::enableAltSaveAction()
0585 {
0586     if(d->alternativeSaveAction) {
0587         d->alternativeSaveAction->setEnabled(true);
0588     }
0589 }
0590 
0591 void MainWindow::openFile()
0592 {
0593     QStringList mimeFilter;
0594 
0595     KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(WORDS_MIME_TYPE);
0596     if (!entry.isEmpty()) {
0597         QJsonObject json = entry.metaData();
0598         QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList();
0599 
0600         mimeFilter << KoFilterManager::mimeFilter(WORDS_MIME_TYPE,
0601                                                   KoFilterManager::Import,
0602                                                   mimeTypes);
0603     }
0604     entry = KoDocumentEntry::queryByMimeType(STAGE_MIME_TYPE);
0605     if (!entry.isEmpty()) {
0606         QJsonObject json = entry.metaData();
0607         QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList();
0608         mimeFilter << KoFilterManager::mimeFilter(STAGE_MIME_TYPE,
0609                                                   KoFilterManager::Import,
0610                                                   mimeTypes);
0611     }
0612 
0613     KoFileDialog dialog(d->desktopView, KoFileDialog::OpenFile, "OpenDocument");
0614     dialog.setCaption(i18n("Open Document"));
0615     dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
0616     dialog.setMimeTypeFilters(mimeFilter);
0617     QString filename = dialog.filename();
0618     if(!filename.isEmpty()) {
0619         QMetaObject::invokeMethod(d->touchView->rootObject(), "openFile", Q_ARG(QVariant, filename), Q_ARG(QVariant, 0));
0620     }
0621 }
0622 
0623 bool MainWindow::temporaryFile() const
0624 {
0625     return d->temporaryFile;
0626 }
0627 
0628 void MainWindow::setTemporaryFile(bool newValue)
0629 {
0630     d->temporaryFile = newValue;
0631     emit temporaryFileChanged();
0632 }
0633 
0634 bool MainWindow::fullScreen() const
0635 {
0636     return Qt::WindowFullScreen == (windowState() & Qt::WindowFullScreen);
0637 }
0638 
0639 void MainWindow::setFullScreen(bool newValue)
0640 {
0641     if(newValue) {
0642         if(d->fullScreenThrottle->isActive()) {
0643             // not a good thing... you need to avoid this happening. This exists to avoid a death-loop,
0644             // such as what might happen if readermode is enabled when the window is not maximised
0645             // as this causes a resize loop which makes readermode switch between enabled and disabled,
0646             // which in turn makes fullScreen be set and reset all the time... very bad, so let's try
0647             // and avoid that.
0648         }
0649         else {
0650             setWindowState(windowState() | Qt::WindowFullScreen);
0651         }
0652     }
0653     else {
0654         // this is really unpleasant... however, fullscreen is very twitchy, and exiting it as below
0655         // will cause an inconsistent state, so we simply assume exiting fullscreen leaves you maximised.
0656         // It isn't optimal, but it is the best state for now, this has taken too long to work out.
0657         // setWindowState(windowState() & ~Qt::WindowFullScreen);
0658         // should really do it, but... it doesn't. So, we end up with what we have next:
0659         showMaximized();
0660     }
0661     d->fullScreenThrottle->start();
0662     emit fullScreenChanged();
0663 }
0664 
0665 QObject* MainWindow::desktopKoView() const
0666 {
0667     return d->desktopKoView;
0668 }
0669 
0670 int MainWindow::lastScreen() const
0671 {
0672     QDesktopWidget desktop;
0673     return desktop.screenCount() - 1;
0674 }
0675 
0676 void MainWindow::resourceChanged(int key, const QVariant& v)
0677 {
0678     Q_UNUSED(key)
0679     Q_UNUSED(v)
0680     if(centralWidget() == d->touchView)
0681         return;
0682 }
0683 
0684 void MainWindow::resourceChangedTouch(int key, const QVariant& v)
0685 {
0686     Q_UNUSED(key)
0687     Q_UNUSED(v)
0688     if(centralWidget() == d->desktopView)
0689         return;
0690 }
0691 
0692 void MainWindow::minimize()
0693 {
0694     setWindowState(windowState() ^ Qt::WindowMinimized);
0695 }
0696 
0697 void MainWindow::closeWindow()
0698 {
0699     if (d->desktopView) {
0700         d->desktopView->setNoCleanup(true);
0701         if (centralWidget() == d->desktopView)
0702             d->allowClose = d->queryClose();
0703     }
0704 
0705     if (d->allowClose)
0706     {
0707         d->altSaveQuery();
0708         d->settings->setCurrentFile("");
0709     }
0710     qApp->processEvents();
0711     qApp->quit();
0712 }
0713 
0714 bool MainWindow::Private::queryClose()
0715 {
0716     desktopView->setNoCleanup(true);
0717     if (DocumentManager::instance()->document() == 0)
0718         return true;
0719 
0720     // main doc + internally stored child documents
0721     if (DocumentManager::instance()->document()->isModified()) {
0722         auto url = DocumentManager::instance()->document()->defaultUrl();
0723 
0724         int res = KMessageBox::warningYesNoCancel(q,
0725                   i18n("<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>", url.fileName()),
0726                   QString(),
0727                   KStandardGuiItem::save(),
0728                   KStandardGuiItem::discard());
0729 
0730         switch (res) {
0731         case KMessageBox::Yes : {
0732             if (DocumentManager::instance()->isTemporaryFile() && !desktopViewProxy->fileSaveAs())
0733                 return false;
0734             if (!DocumentManager::instance()->save())
0735                 return false;
0736             break;
0737         }
0738         case KMessageBox::No :
0739             DocumentManager::instance()->document()->removeAutoSaveFiles();
0740             DocumentManager::instance()->document()->setModified(false);   // Now when queryClose() is called by closeEvent it won't do anything.
0741             break;
0742         default : // case KMessageBox::Cancel :
0743             return false;
0744         }
0745     }
0746     return true;
0747 }
0748 
0749 void MainWindow::Private::altSaveQuery()
0750 {
0751     if(alternativeSaveAction && alternativeSaveAction->isEnabled())
0752     {
0753         int res = KMessageBox::warningYesNo(q, i18n("<p>The cloud copy of the document is out of date. Do you want to upload a new copy?</p>"));
0754         switch (res) {
0755         case KMessageBox::Yes : {
0756             alternativeSaveAction->trigger();
0757             while(alternativeSaveAction->isEnabled()) {
0758                 qApp->processEvents();
0759             }
0760             break;
0761         }
0762         case KMessageBox::No :
0763         default:
0764             break;
0765         }
0766     }
0767 }
0768 
0769 void MainWindow::closeEvent(QCloseEvent* event)
0770 {
0771     if (centralWidget() == d->desktopView)
0772     {
0773         KoDocument* document = DocumentManager::instance()->document();
0774         if (document && document->isLoading()) {
0775             event->ignore();
0776             return;
0777         }
0778     }
0779 
0780     event->accept();
0781     closeWindow();
0782 }
0783 
0784 MainWindow::~MainWindow()
0785 {
0786     delete d;
0787 }
0788 
0789 #ifdef Q_OS_WIN
0790 bool MainWindow::winEvent( MSG * message, long * result )
0791 {
0792     if (message && message->message == WM_SETTINGCHANGE && message->lParam)
0793     {
0794         if (wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *) message->lParam) == 0)
0795             d->notifySlateModeChange();
0796         else if (wcscmp(TEXT("SystemDockMode"), (TCHAR *) message->lParam) == 0)
0797             d->notifyDockingModeChange();
0798         *result = 0;
0799         return true;
0800     }
0801     return false;
0802 }
0803 #endif
0804 
0805 void MainWindow::Private::notifySlateModeChange()
0806 {
0807 #ifdef Q_OS_WIN
0808     bool bSlateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
0809 
0810     if (slateMode != bSlateMode)
0811     {
0812         slateMode = bSlateMode;
0813         emit q->slateModeChanged();
0814         if (forceTouch || (slateMode && !forceDesktop))
0815         {
0816             if (!toTouch || (toTouch && toTouch->isEnabled()))
0817                 q->switchToTouch();
0818         }
0819         else
0820         {
0821                 q->switchToDesktop();
0822         }
0823         //qDebug() << "Slate mode is now" << slateMode;
0824     } 
0825 #endif
0826 }
0827 
0828 void MainWindow::Private::notifyDockingModeChange()
0829 {
0830 #ifdef Q_OS_WIN
0831     bool bDocked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0);
0832 
0833     if (docked != bDocked)
0834     {
0835         docked = bDocked;
0836         //qDebug() << "Docking mode is now" << docked;
0837     }
0838 #endif
0839 }