diff --git a/gemini/CMakeLists.txt b/gemini/CMakeLists.txt index c8713d67713..b286768fe60 100644 --- a/gemini/CMakeLists.txt +++ b/gemini/CMakeLists.txt @@ -1,101 +1,103 @@ project(calligragemini) # TEMPORARY: for Qt5/KF5 build porting phase deprecation warnings are only annoying noise if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) add_definitions(-Wno-deprecated -Wno-deprecated-declarations) endif () +add_subdirectory(lib) if (WIN32) option(ENABLE_GEMINI_STARTUP_MAGIC "Enable Gemini's windows startup magic" ON) if (ENABLE_GEMINI_STARTUP_MAGIC) add_definitions(-DGEMINI_ENABLE_STARTUP_MAGIC) endif () endif () include_directories(${KOMAIN_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/libs/pageapp ${CMAKE_SOURCE_DIR}/stage ${CMAKE_SOURCE_DIR}/stage/part ${CMAKE_SOURCE_DIR}/words ${CMAKE_SOURCE_DIR}/words/part) set(calligragemini_SRCS main.cpp MainWindow.cpp desktopviewproxy.cpp Constants.cpp DocumentListModel.cpp DocumentManager.cpp ProgressProxy.cpp QmlGlobalEngine.cpp RecentFileManager.cpp Settings.cpp SimpleTouchArea.cpp # TouchDeclarativeView.cpp PropertyContainer.cpp Theme.cpp ToolManager.cpp ParagraphStylesModel.cpp KeyboardModel.cpp ScribbleArea.cpp RecentImageImageProvider.cpp RecentFilesModel.cpp TemplatesModel.cpp TemplateVariantsModel.cpp CloudAccountsModel.cpp ) set(thumbnailhelper_SRCS thumbnailhelper.cpp ThumbnailHelperImpl.cpp ) if (WIN32) set(calligragemini_SRCS ${calligragemini_SRCS} calligrageminiwin.rc) else () file(GLOB ICONS_SRCS "pics/*-apps-calligragemini.png") ecm_add_app_icon(calligragemini_SRCS ICONS ${ICONS_SRCS}) endif () # Thumbnail helper application # ============================================================================= add_executable(calligrageminithumbnailhelper ${thumbnailhelper_SRCS}) target_link_libraries(calligrageminithumbnailhelper komain KF5::Crash # needed to explicitely disable drkonqi if any plugin links to kdelibs4support ) # Calligra Gemini application # ============================================================================= add_executable(calligragemini ${calligragemini_SRCS}) target_link_libraries(calligragemini Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Core KF5::IconThemes komain wordsprivate calligrastageprivate + gemini ) install(TARGETS calligragemini calligrageminithumbnailhelper ${INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS calligragemini.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) #install(FILES calligrageminirc calligrageminipanelsrc DESTINATION ${CONFIG_INSTALL_DIR} ) install(FILES calligragemini.qml DESTINATION ${DATA_INSTALL_DIR}/calligragemini) #install(FILES calligragemini.rc DESTINATION ${DATA_INSTALL_DIR}/calligragemini) install(DIRECTORY qml DESTINATION ${DATA_INSTALL_DIR}/calligragemini) install(DIRECTORY themes DESTINATION ${DATA_INSTALL_DIR}/calligragemini) if(APPLE) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/calligragemini_SRCS.icns DESTINATION ${BUNDLE_INSTALL_DIR}/calligragemini.app/Contents/Resources) endif() add_subdirectory(pics) add_subdirectory(cloud) diff --git a/gemini/lib/CMakeLists.txt b/gemini/lib/CMakeLists.txt new file mode 100644 index 00000000000..ffbedb126eb --- /dev/null +++ b/gemini/lib/CMakeLists.txt @@ -0,0 +1,13 @@ +project(gemini) + +set(gemini_SRCS + GeminiMainWindow.cpp +) + +kde4_add_library(gemini SHARED ${gemini_SRCS}) +target_link_libraries(gemini + Qt5::Quick + Qt5::Gui + Qt5::Core + Qt5::Widgets +) diff --git a/gemini/lib/GeminiMainWindow.cpp b/gemini/lib/GeminiMainWindow.cpp new file mode 100644 index 00000000000..60ff490a6ba --- /dev/null +++ b/gemini/lib/GeminiMainWindow.cpp @@ -0,0 +1,223 @@ +/* This file is part of the KDE project + * Copyright (C) 2015 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 "GeminiMainWindow.h" +#include "GeminiModeSwitchEvent.h" + +#include +#include +#include +#include +#include + +struct View { + View() : quickView(0), widget(0) {} + QQuickView* quickView; + QWidget* widget; + QWidget* getWidget(QWidget* parent = 0) { + QWidget* view; + if(widget) { + view = widget; + } + else { + QWidget* container = QWidget::createWindowContainer(quickView); + view = new QWidget(parent); + view->setLayout(new QVBoxLayout()); + view->layout()->setContentsMargins(0,0,0,0); + view->layout()->setSpacing(0); + view->layout()->addWidget(container); + } + return view; + } +}; + +class GeminiMainWindow::Private { +public: + Private(GeminiMainWindow* qq) + : q(qq) + , fullScreenThrottle(new QTimer(qq)) + , currentState(UnknownState) + , stateLocked(false) + { + fullScreenThrottle->setInterval(500); + fullScreenThrottle->setSingleShot(true); + + // Initialise the event receiver map to empty + eventReceivers.insert(CreateState, 0); + eventReceivers.insert(EditState, 0); + eventReceivers.insert(ViewState, 0); + + // Initialise the view map to empty + views.insert(CreateState, 0); + views.insert(EditState, 0); + views.insert(ViewState, 0); + } + GeminiMainWindow* q; + QTimer* fullScreenThrottle; + + GeminiState previousState; + GeminiState currentState; + bool stateLocked; + + QMap views; + QMap eventReceivers; + + GeminiModeSynchronisationObject* syncObject; +}; + +GeminiMainWindow::GeminiMainWindow(QWidget* parent, Qt::WindowFlags flags) + : QMainWindow(parent, flags) + , d(new Private(this)) +{ +} + +GeminiMainWindow::~GeminiMainWindow() +{ + delete d; +} + +GeminiMainWindow::GeminiState GeminiMainWindow::currentState() const +{ + return d->currentState; +} + +bool GeminiMainWindow::fullScreen() const +{ + return (windowState() & Qt::WindowFullScreen) == Qt::WindowFullScreen; +} + +void GeminiMainWindow::setFullScreen(bool newState) +{ + if(newState) { + 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(); +} + + +void GeminiMainWindow::changeState(GeminiMainWindow::GeminiState newState, bool lockNewState) +{ + d->syncObject = new GeminiModeSynchronisationObject; + + if(centralWidget()) { + //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. + GeminiModeSwitchEvent aboutToSwitchEvent(GeminiModeSwitchEvent::AboutToSwitchViewModeEvent, d->eventReceivers[d->currentState], d->eventReceivers[newState], d->syncObject); + QApplication::sendEvent(d->eventReceivers[d->currentState], &aboutToSwitchEvent); + + centralWidget()->setParent(0); + } + + View* view = d->views[newState]; + setCentralWidget(view->getWidget()); + qApp->processEvents(); + if(view->quickView) + view->quickView->setVisible(true); + resize(size()); + + d->previousState = d->currentState; + d->currentState = newState; + emit currentStateChanged(); + + QTimer::singleShot(50, this, SLOT(stateChanging())); +} + +void GeminiMainWindow::stateChanging() +{ + qApp->processEvents(); + //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. + GeminiModeSwitchEvent switchedEvent(GeminiModeSwitchEvent::SwitchedToThisModeEvent, d->eventReceivers[d->previousState], d->eventReceivers[d->currentState], d->syncObject); + QApplication::sendEvent(d->eventReceivers[d->currentState], &switchedEvent); + d->syncObject = 0; + qApp->processEvents(); +} + +bool GeminiMainWindow::stateLocked() const +{ + return d->stateLocked; +} + +void GeminiMainWindow::setStateLocked(bool locked) +{ + d->stateLocked = locked; + emit stateLockedChanged(); +} + +void GeminiMainWindow::setViewForState(QWidget* widget, GeminiMainWindow::GeminiState state) +{ + View* view = d->views[state]; + view->quickView = 0; + view->widget = widget; +} + +void GeminiMainWindow::setViewForState(QQuickView* quickView, GeminiMainWindow::GeminiState state) +{ + View* view = d->views[state]; + view->quickView = quickView; + view->widget = 0; +} + +void GeminiMainWindow::setEventReceiverForState(QObject* receiver, GeminiMainWindow::GeminiState state) +{ + d->eventReceivers[state] = receiver; +} + +#ifdef Q_OS_WIN +bool GeminiMainWindow::winEvent( MSG * message, long * result ) +{ + if (message && message->message == WM_SETTINGCHANGE && message->lParam && !d->stateLocked) + { + if (wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *) message->lParam) == 0 || wcscmp(TEXT("SystemDockMode"), (TCHAR *) message->lParam) == 0) { + bool slateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); + bool undocked = !(GetSystemMetrics(SM_SYSTEMDOCKED) != 0); + if(slateMode || undocked) { + // find out if we are entirely without sensible input devices, or portrait + // if we are, change to ViewState. EditState will do for now as an autoswitch... + changeState(EditState); + } + else { + // If we are neither slate nor undocked, then we're in clamshell or we are docked, which is the same thing. + changeState(CreateState); + } + *result = 0; + return true; + } + } + return false; +} +#endif diff --git a/gemini/lib/GeminiMainWindow.h b/gemini/lib/GeminiMainWindow.h new file mode 100644 index 00000000000..15fc63a2201 --- /dev/null +++ b/gemini/lib/GeminiMainWindow.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project + * Copyright (C) 2015 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. + */ + +#ifndef GEMINIMAINWINDOW_H +#define GEMINIMAINWINDOW_H + +#include +class QQuickView; + +/** + * \short + * + */ +class GeminiMainWindow : public QMainWindow +{ + Q_OBJECT + Q_PROPERTY(GeminiState currentState READ currentState NOTIFY currentStateChanged) + Q_PROPERTY(bool fullScreen READ fullScreen WRITE setFullScreen NOTIFY fullScreenChanged) + Q_ENUMS(GeminiState) +public: + enum GeminiState { + AllStates = -3, + UnknownState = -2, + NoState = -1, + CreateState = 1, + EditState = 2, + ViewState = 3 + }; + + explicit GeminiMainWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0); + virtual ~GeminiMainWindow(); + + GeminiState currentState() const; + + bool fullScreen() const; + void setFullScreen(bool newState); + + bool stateLocked() const; + void setStateLocked(bool locked); + + void changeState(GeminiState newState, bool lockNewState = false); + + void setViewForState(QWidget* widget, GeminiState state); + void setViewForState(QQuickView* quickView, GeminiState state); + + void setEventReceiverForState(QObject* receiver, GeminiState state); + +Q_SIGNALS: + void stateLockedChanged(); + void currentStateChanged(); + void fullScreenChanged(); + +private Q_SLOTS: + void stateChanging(); + +private: + class Private; + Private* d; +#ifdef Q_OS_WIN + bool winEvent(MSG * message, long * result); +#endif +}; + +#endif // GEMINIMAINWINDOW_H diff --git a/gemini/lib/GeminiModeSwitchEvent.h b/gemini/lib/GeminiModeSwitchEvent.h new file mode 100644 index 00000000000..36decc0be7a --- /dev/null +++ b/gemini/lib/GeminiModeSwitchEvent.h @@ -0,0 +1,65 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2013 Arjen Hiemstra + * Copyright (C) 2015 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) 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. + */ + +#ifndef GEMINIMODESWITCHEVENT_H +#define GEMINIMODESWITCHEVENT_H + +#include + +struct GeminiModeSynchronisationObject { + GeminiModeSynchronisationObject() : initialized(false) { } + bool initialized; +}; + +class GeminiModeSwitchEvent : public QEvent +{ +public: + enum GeminiModeEventType { + AboutToSwitchViewModeEvent = QEvent::User + 1, + SwitchedToThisModeEvent ///< This is the case when + }; + + inline GeminiModeSwitchEvent(GeminiModeEventType type, QObject* fromView, QObject* toView, GeminiModeSynchronisationObject* syncObject) + : QEvent(static_cast(type)) + , m_fromView(fromView) + , m_toView(toView) + , m_syncObject(syncObject) { + + } + + inline QObject* fromView() const { + return m_fromView; + } + inline QObject* toView() const { + return m_toView; + } + + inline GeminiModeSynchronisationObject* synchronisationObject() const { + return m_syncObject; + } + +private: + QObject* m_fromView; + QObject* m_toView; + GeminiModeSynchronisationObject* m_syncObject; +}; + +#endif // GEMINIMODESWITCHEVENT_H +