File indexing completed on 2024-05-05 17:04:26

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