Git commit 7d4fde1f5a039d1a700d6806a4c000046c845465 by Albert Astals Cid. Committed on 09/10/2017 at 11:25. Pushed by aacid into branch 'master'.
Add initial Android version Includes a few other tweaks for the desktop version too but very minor M +97 -68 CMakeLists.txt A +37 -0 android_data/AndroidManifest.xml A +- -- android_data/audio-volume-high.png A +13 -0 android_data/audio-volume-high.svg A +- -- android_data/audio-volume-muted.png A +21 -0 android_data/audio-volume-muted.svg A +- -- android_data/games-config-theme.png A +13 -0 android_data/games-config-theme.svg A +- -- android_data/res/drawable/ktuberling.png A +7 -0 android_data/resources.qrc M +0 -6 doc/index.docbook A +42 -0 filefactory.cpp [License: GPL (v2+)] A +23 -0 filefactory.h [License: GPL (v2+)] A +137 -0 main_mobile.cpp [License: GPL (v2+)] M +- -- pics/robot_workshop.svgz M +40 -29 playground.cpp M +18 -6 playground.h M +16 -22 soundfactory.cpp M +11 -8 soundfactory.h M +5 -5 toplevel.cpp M +8 -6 toplevel.h https://commits.kde.org/ktuberling/7d4fde1f5a039d1a700d6806a4c000046c845465 diff --git a/CMakeLists.txt b/CMakeLists.txt index 42f415f..ca6240e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,26 +7,27 @@ set (KF5_MIN_VERSION "5.15.0") find_package(ECM 1.7.0 REQUIRED CONFIG) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) -find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS PrintSupport Svg Widgets Xml) -find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS - Completion - Config - ConfigWidgets - CoreAddons - Crash - DBusAddons - KIO - DocTools - I18n - KDELibs4Support #TODO eventually remove kdelibs4support - WidgetsAddons - XmlGui -) - -find_package(KF5KDEGames 4.9.0 REQUIRED) -find_package(Phonon4Qt5 CONFIG REQUIRED) +find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS PrintSupport Svg Widgets Xml Multimedia) +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config) + +if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") + find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS + Completion + ConfigWidgets + CoreAddons + Crash + DBusAddons + KIO + DocTools + I18n + KDELibs4Support #TODO eventually remove kdelibs4support + WidgetsAddons + XmlGui + ) + + find_package(KF5KDEGames 4.9.0 REQUIRED) +endif() -include_directories(BEFORE ${PHONON_INCLUDES}) include(FeatureSummary) include(ECMAddAppIcon) @@ -40,59 +41,87 @@ add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) add_subdirectory(sounds) add_subdirectory(pics) -add_subdirectory(doc) +if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") + add_subdirectory(doc) +endif() ########### next target ############### -set(ktuberling_SRCS - action.cpp - main.cpp - toplevel.cpp - playground.cpp - todraw.cpp - soundfactory.cpp - playgrounddelegate.cpp +set(ktuberling_common_SRCS + action.cpp + playground.cpp + todraw.cpp + soundfactory.cpp + filefactory.cpp ) -file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*-apps-ktuberling.png") -ecm_add_app_icon(ktuberling_SRCS ICONS ${ICONS_SRCS}) -add_executable(ktuberling ${ktuberling_SRCS}) - -target_link_libraries(ktuberling - Qt5::PrintSupport - Qt5::Svg - KF5::Completion - KF5::Crash - KF5::DBusAddons - KF5::KIOCore - KF5::KDELibs4Support - KF5::XmlGui - Phonon::phonon4qt5 - KF5KDEGames -) - -install(TARGETS ktuberling ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) - - -########### install files ############### - -install(PROGRAMS org.kde.ktuberling.desktop DESTINATION ${KDE_INSTALL_APPDIR}) -install(FILES ktuberlingui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/ktuberling) - -ecm_install_icons(ICONS - 128-apps-ktuberling.png - 16-apps-ktuberling.png - 22-apps-ktuberling.png - 32-apps-ktuberling.png - 48-apps-ktuberling.png - 64-apps-ktuberling.png - 128-mimetypes-application-x-tuberling.png - 16-mimetypes-application-x-tuberling.png - 22-mimetypes-application-x-tuberling.png - 32-mimetypes-application-x-tuberling.png - 48-mimetypes-application-x-tuberling.png - 64-mimetypes-application-x-tuberling.png - DESTINATION ${KDE_INSTALL_ICONDIR} THEME hicolor -) +if(${CMAKE_SYSTEM_NAME} MATCHES "Android") + set(ktuberling_mobile_SRCS + ${ktuberling_common_SRCS} + main_mobile.cpp + ) + + qt5_add_resources(ktuberling_mobile_SRCS android_data/resources.qrc) + + add_executable(ktuberling_mobile ${ktuberling_mobile_SRCS}) + + target_link_libraries(ktuberling_mobile + Qt5::Gui + Qt5::Svg + Qt5::Multimedia + Qt5::Xml + Qt5::Widgets + KF5::ConfigCore ) + + install(TARGETS ktuberling_mobile RUNTIME DESTINATION bin) + +else() + + set(ktuberling_SRCS + ${ktuberling_common_SRCS} + main.cpp + toplevel.cpp + playgrounddelegate.cpp + ) + + file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/*-apps-ktuberling.png") + ecm_add_app_icon(ktuberling_SRCS ICONS ${ICONS_SRCS}) + + add_executable(ktuberling ${ktuberling_SRCS}) + + target_link_libraries(ktuberling + Qt5::PrintSupport + Qt5::Svg + Qt5::Multimedia + KF5::Completion + KF5::Crash + KF5::DBusAddons + KF5::KIOCore + KF5::KDELibs4Support + KF5::XmlGui + KF5KDEGames + ) + + install(TARGETS ktuberling ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) + + install(PROGRAMS org.kde.ktuberling.desktop DESTINATION ${KDE_INSTALL_APPDIR}) + install(FILES ktuberlingui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/ktuberling) + + ecm_install_icons(ICONS + 128-apps-ktuberling.png + 16-apps-ktuberling.png + 22-apps-ktuberling.png + 32-apps-ktuberling.png + 48-apps-ktuberling.png + 64-apps-ktuberling.png + 128-mimetypes-application-x-tuberling.png + 16-mimetypes-application-x-tuberling.png + 22-mimetypes-application-x-tuberling.png + 32-mimetypes-application-x-tuberling.png + 48-mimetypes-application-x-tuberling.png + 64-mimetypes-application-x-tuberling.png + DESTINATION ${KDE_INSTALL_ICONDIR} THEME hicolor + ) +endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/android_data/AndroidManifest.xml b/android_data/AndroidManifest.xml new file mode 100644 index 0000000..f57c1f0 --- /dev/null +++ b/android_data/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.0.3" package="org.kde.ktuberling" android:installLocation="auto" android:versionCode="7"> + <application android:name="org.qtproject.qt5.android.bindings.QtApplication" + android:label="KTuberling" + android:icon="@drawable/ktuberling"> + <activity android:name="org.qtproject.qt5.android.bindings.QtActivity" + android:label="KTuberling" + android:screenOrientation="landscape" + android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> + <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> + <meta-data android:name="android.app.repository" android:value="default"/> + <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> + <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/> + <!-- Deploy Qt libs as part of package --> + <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> + <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> + <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> + <!-- Run with local libs --> + <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> + <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> + <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/> + <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> + <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> + <!-- Messages maps --> + <meta-data android:name="android.app.ministro_not_found_msg" android:value="@string/ministro_not_found_msg"/> + <meta-data android:name="android.app.ministro_needed_msg" android:value="@string/ministro_needed_msg"/> + <meta-data android:name="android.app.fatal_error_msg" android:value="@string/fatal_error_msg"/> + </activity> + </application> + <supports-screens android:anyDensity="true" android:normalScreens="true" android:smallScreens="true" android:largeScreens="true"/> + <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="19"/> +</manifest> diff --git a/android_data/audio-volume-high.png b/android_data/audio-volume-high.png new file mode 100644 index 0000000..1df64b9 Binary files /dev/null and b/android_data/audio-volume-high.png differ diff --git a/android_data/audio-volume-high.svg b/android_data/audio-volume-high.svg new file mode 100644 index 0000000..a5dca4c --- /dev/null +++ b/android_data/audio-volume-high.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#4d4d4d; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 10.988281 3 L 6 7.9902344 L 6 8 L 6 9 L 6 13 L 6 14 L 6 14.009766 L 10.988281 19 L 12 19 L 12 18.597656 L 12 3.4023438 L 12 3 L 10.988281 3 z M 13.865234 3.5371094 L 13.621094 4.5136719 A 7 7 0 0 1 18 11 A 7 7 0 0 1 13.619141 17.478516 L 13.863281 18.453125 A 8 8 0 0 0 19 11 A 8 8 0 0 0 13.865234 3.5371094 z M 14.324219 7.28125 L 13.785156 8.1425781 A 4 4 0 0 1 15 11 A 4 4 0 0 1 13.789062 13.861328 L 14.328125 14.724609 A 5 5 0 0 0 16 11 A 5 5 0 0 0 14.324219 7.28125 z M 3 8 L 3 9 L 3 13 L 3 14 L 5 14 L 5 13 L 5 9 L 5 8 L 3 8 z " + class="ColorScheme-Text"/> +</svg> diff --git a/android_data/audio-volume-muted.png b/android_data/audio-volume-muted.png new file mode 100644 index 0000000..29508f7 Binary files /dev/null and b/android_data/audio-volume-muted.png differ diff --git a/android_data/audio-volume-muted.svg b/android_data/audio-volume-muted.svg new file mode 100644 index 0000000..1a9f68b --- /dev/null +++ b/android_data/audio-volume-muted.svg @@ -0,0 +1,21 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#4d4d4d; + } + .ColorScheme-NegativeText { + color:#da4453; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 10.988281 3 L 6 7.9902344 L 6 8 L 6 9 L 6 13 L 6 14 L 6 14.009766 L 10.988281 19 L 12 19 L 12 18.597656 L 12 3.4023438 L 12 3 L 10.988281 3 z M 3 8 L 3 9 L 3 13 L 3 14 L 5 14 L 5 13 L 5 9 L 5 8 L 3 8 z " + class="ColorScheme-Text"/> + <path + style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 13 10 L 13 12 L 19 12 L 19 10 L 13 10 z " + class="ColorScheme-NegativeText" + /> +</svg> diff --git a/android_data/games-config-theme.png b/android_data/games-config-theme.png new file mode 100644 index 0000000..29a358a Binary files /dev/null and b/android_data/games-config-theme.png differ diff --git a/android_data/games-config-theme.svg b/android_data/games-config-theme.svg new file mode 100644 index 0000000..c74afd0 --- /dev/null +++ b/android_data/games-config-theme.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <defs id="defs3051"> + <style type="text/css" id="current-color-scheme"> + .ColorScheme-Text { + color:#4d4d4d; + } + </style> + </defs> + <path style="fill:currentColor;fill-opacity:1;stroke:none" + d="M 19.513672 4.0078125 A 2 9 45 0 0 12.068359 9.1054688 A 2 9 45 0 0 8.5253906 13.21875 C 9.6014506 13.56139 10.440563 14.400494 10.783203 15.476562 A 2 9 45 0 0 14.896484 11.933594 A 2 9 45 0 0 19.845703 4.15625 A 2 9 45 0 0 19.513672 4.0078125 z M 8 14.664062 C 3.99999 15.735864 7 18.26795 4 20 C 8.00339 20 10 17.99918 10 16.664062 C 10 15.999182 10.0676 14.774622 8 14.664062 z " + class="ColorScheme-Text" + /> +</svg> diff --git a/android_data/res/drawable/ktuberling.png b/android_data/res/drawable/ktuberling.png new file mode 100644 index 0000000..6d5bbbf Binary files /dev/null and b/android_data/res/drawable/ktuberling.png differ diff --git a/android_data/resources.qrc b/android_data/resources.qrc new file mode 100644 index 0000000..8a596b4 --- /dev/null +++ b/android_data/resources.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>games-config-theme.png</file> + <file>audio-volume-high.png</file> + <file>audio-volume-muted.png</file> + </qresource> +</RCC> diff --git a/doc/index.docbook b/doc/index.docbook index 91cbdef..83bcd64 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -508,12 +508,6 @@ It will contain the playgrounds installed in your system. <sect2> <title>The Speech Menu</title> -<!--FIXME -<para> -Please note that you need to have <command>&phonon;</command> installed -and properly configured to be able to hear sounds. -</para>--> - <variablelist> <varlistentry> diff --git a/filefactory.cpp b/filefactory.cpp new file mode 100644 index 0000000..ef3d39c --- /dev/null +++ b/filefactory.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2017 by Albert Astals Cid <[email protected]> * + * * + * 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. * + ***************************************************************************/ + +#include "filefactory.h" + +#include <QFileInfo> +#include <QStandardPaths> + +bool FileFactory::folderExists(const QString &relativePath) +{ +#if defined(Q_OS_ANDROID) + QFileInfo fi("/data/data/org.kde.ktuberling/qt-reserved-files/share/ktuberling/" + relativePath); + return fi.isDir(); +#else + return !(QStandardPaths::locate(QStandardPaths::AppDataLocation, relativePath, QStandardPaths::LocateDirectory).isEmpty()); +#endif +} + +QString FileFactory::locate(const QString &relativePath) +{ +#if defined(Q_OS_ANDROID) + return "/data/data/org.kde.ktuberling/qt-reserved-files/share/ktuberling/" + relativePath; +#else + return QStandardPaths::locate(QStandardPaths::AppDataLocation, relativePath); +#endif +} + +QStringList FileFactory::locateAll(const QString &relativePath) +{ +#if defined(Q_OS_ANDROID) + return { "/data/data/org.kde.ktuberling/qt-reserved-files/share/ktuberling/" + relativePath }; +#else + return QStandardPaths::locateAll(QStandardPaths::AppDataLocation, relativePath, QStandardPaths::LocateDirectory); +#endif +} + diff --git a/filefactory.h b/filefactory.h new file mode 100644 index 0000000..f7954b8 --- /dev/null +++ b/filefactory.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * Copyright (C) 2017 by Albert Astals Cid <[email protected]> * + * * + * 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. * + ***************************************************************************/ + +#ifndef FILEFACTORY_H +#define FILEFACTORY_H + +class QString; +class QStringList; + +namespace FileFactory +{ + bool folderExists(const QString &relativePath); + QString locate(const QString &relativePath); + QStringList locateAll(const QString &relativePath); +}; + +#endif diff --git a/main_mobile.cpp b/main_mobile.cpp new file mode 100644 index 0000000..429323f --- /dev/null +++ b/main_mobile.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 1999-2006 by Éric Bischoff <[email protected]> * + * Copyright (C) 2007 by Albert Astals Cid <[email protected]> * + * * + * 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. * + ***************************************************************************/ + +#include <QApplication> +#include <QDebug> +#include <QDesktopWidget> +#include <QHBoxLayout> +#include <QLabel> +#include <QPushButton> + +#include "filefactory.h" +#include "soundfactory.h" +#include "playground.h" + +static const char version[] = "1.0.0"; + +class KTuberlingMobile : public PlayGroundCallbacks, public SoundFactoryCallbacks +{ +public: + KTuberlingMobile() + : m_soundEnabled(true) + { + m_soundFactory = new SoundFactory(this); + m_soundFactory->registerLanguages(); + m_soundFactory->loadLanguage(FileFactory::locate("sounds/en.soundtheme")); + + QWidget *mainWidget = new QWidget(); + QHBoxLayout *lay = new QHBoxLayout(mainWidget); + + m_themesWidget = new QWidget(); + m_gameboardLayout = new QGridLayout(m_themesWidget); + + m_playground = new PlayGround(this, mainWidget); + m_playground->registerPlayGrounds(); + m_playground->lockAspectRatio(true); + m_playground->setAllowOnlyDrag(true); + m_playground->loadPlayGround(FileFactory::locate("pics/default_theme.theme")); + + QVBoxLayout *sideLayout = new QVBoxLayout(); + + // Not sure this is the best way but it works for now + const int screenWidth = QDesktopWidget().screenGeometry().width(); + const int iconWidth = screenWidth / 15; + + QPushButton *themesButton = new QPushButton(mainWidget); + themesButton->setIcon(QPixmap(":/games-config-theme.png")); + themesButton->setIconSize(QSize(iconWidth, iconWidth)); + themesButton->setFocusPolicy(Qt::NoFocus); + QObject::connect(themesButton, &QPushButton::clicked, [this, mainWidget] { + m_themesWidget->showFullScreen(); + }); + + QPushButton *soundsButton = new QPushButton(mainWidget); + soundsButton->setIcon(QPixmap(":/audio-volume-high.png")); + soundsButton->setIconSize(QSize(iconWidth, iconWidth)); + soundsButton->setFocusPolicy(Qt::NoFocus); + QObject::connect(soundsButton, &QPushButton::clicked, [this, soundsButton] { + m_soundEnabled = !m_soundEnabled; + soundsButton->setIcon(QPixmap(m_soundEnabled ? ":/audio-volume-high.png" : ":/audio-volume-muted.png")); + }); + + sideLayout->addWidget(themesButton); + sideLayout->addWidget(soundsButton); + sideLayout->addStretch(1); + + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + lay->addWidget(m_playground); + lay->addLayout(sideLayout); + + mainWidget->showFullScreen(); + } + + ~KTuberlingMobile() + { + delete m_soundFactory; + } + + void playSound(const QString &ref) override + { + m_soundFactory->playSound(ref); + } + + void changeGameboard(const QString &/*gameboard*/) override + { + // Only needed when loading a file so not needed for now + } + + void registerGameboard(const QString& menuText, const QString& boardFile, const QPixmap &/*pixmap*/) override + { + // TODO this should be scrollable + // TODO use the pixmap + QPushButton *pb = new QPushButton(menuText); + QObject::connect(pb, &QPushButton::clicked, [this, boardFile] { + m_playground->loadPlayGround(boardFile); + m_themesWidget->hide(); + }); + + m_gameboardLayout->addWidget(pb, m_gameboardLayout->count() / 2, m_gameboardLayout->count() % 2); + } + + bool isSoundEnabled() const override + { + return m_soundEnabled; + } + + void registerLanguage(const QString &/*code*/, const QString &/*soundFile*/, bool /*enabled*/) + { + // TODO + } + +private: + SoundFactory *m_soundFactory; + PlayGround *m_playground; + QWidget *m_themesWidget; + QGridLayout *m_gameboardLayout; + bool m_soundEnabled; +}; + +// Main function +Q_DECL_EXPORT int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QLocale::system().name(); // needed to workaround QTBUG-41385 + app.setApplicationName("ktuberling"); + + KTuberlingMobile tuberling; + + return app.exec(); +} diff --git a/pics/robot_workshop.svgz b/pics/robot_workshop.svgz index 57a61cf..2e0717f 100644 Binary files a/pics/robot_workshop.svgz and b/pics/robot_workshop.svgz differ diff --git a/playground.cpp b/playground.cpp index afe70c2..58d8d1f 100644 --- a/playground.cpp +++ b/playground.cpp @@ -12,12 +12,12 @@ #include "playground.h" -#include <KLocalizedString> #include <kconfig.h> #include <kconfiggroup.h> #include <qdebug.h> #include <QAction> +#include <QApplication> #include <QCursor> #include <QDataStream> #include <QDir> @@ -27,15 +27,10 @@ #include <QGraphicsSvgItem> #include <QMouseEvent> #include <QPainter> -#include <QPrinter> -#include <QStandardPaths> - -#include <kstandardaction.h> -#include <kactioncollection.h> -#include <kstandardshortcut.h> +#include <QPagedPaintDevice> #include "action.h" -#include "toplevel.h" +#include "filefactory.h" #include "todraw.h" static const char *saveGameTextScaleTextMode = "KTuberlingSaveGameV2"; @@ -43,10 +38,9 @@ static const char *saveGameTextTextMode = "KTuberlingSaveGameV3"; static const char *saveGameText = "KTuberlingSaveGameV4"; // Constructor -PlayGround::PlayGround(TopLevel *parent) - : QGraphicsView(parent), m_newItem(0), m_dragItem(0), m_nextZValue(1), m_lockAspect(false) +PlayGround::PlayGround(PlayGroundCallbacks *callbacks, QWidget *parent) + : QGraphicsView(parent), m_callbacks(callbacks), m_newItem(0), m_dragItem(0), m_nextZValue(1), m_lockAspect(false), m_allowOnlyDrag(false) { - m_topLevel = parent; setFrameStyle(QFrame::NoFrame); setOptimizationFlag(QGraphicsView::DontSavePainterState, true); // all items here save the painter state setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -99,7 +93,7 @@ bool PlayGround::saveAs(const QString & name) } // Print gameboard's picture -bool PlayGround::printPicture(QPrinter &printer) +bool PlayGround::printPicture(QPagedPaintDevice &printer) { QPainter artist; QPixmap picture(getPicture()); @@ -139,6 +133,8 @@ void PlayGround::mousePressEvent(QMouseEvent *event) if (event->button() != Qt::LeftButton) return; + m_mousePressPos = event->pos(); + if (m_dragItem) placeDraggedItem(event->pos()); else if (m_newItem) placeNewItem(event->pos()); else @@ -163,7 +159,7 @@ void PlayGround::mousePressEvent(QMouseEvent *event) QPointF itemPos = mapToScene(event->pos()); itemPos -= QPointF(elementSize.width()/2, elementSize.height()/2); - m_topLevel->playSound(m_objectsNameSound.value(foundElem)); + m_callbacks->playSound(m_objectsNameSound.value(foundElem)); m_newItem = new ToDraw; m_newItem->setBeingDragged(true); @@ -186,7 +182,7 @@ void PlayGround::mousePressEvent(QMouseEvent *event) { QString elem = m_dragItem->elementId(); - m_topLevel->playSound(m_objectsNameSound.value(elem)); + m_callbacks->playSound(m_objectsNameSound.value(elem)); setCursor(Qt::BlankCursor); m_dragItem->setBeingDragged(true); m_itemDraggedPos = m_dragItem->pos(); @@ -202,18 +198,22 @@ void PlayGround::mousePressEvent(QMouseEvent *event) void PlayGround::mouseMoveEvent(QMouseEvent *event) { - if (m_newItem) { + ToDraw *movingItem = m_newItem ? m_newItem : m_dragItem; + if (movingItem) { QPointF itemPos = mapToScene(event->pos()); - const QSizeF elementSize = m_newItem->transform().mapRect(m_newItem->unclippedRect()).size(); + const QSizeF elementSize = movingItem->transform().mapRect(movingItem->unclippedRect()).size(); itemPos -= QPointF(elementSize.width()/2, elementSize.height()/2); - m_newItem->setPos(clipPos(itemPos, m_newItem)); - } else if (m_dragItem) { - QPointF itemPos = mapToScene(event->pos()); - const QSizeF elementSize = m_dragItem->transform().mapRect(m_dragItem->unclippedRect()).size(); - itemPos -= QPointF(elementSize.width()/2, elementSize.height()/2); + movingItem->setPos(clipPos(itemPos, movingItem)); + } +} - m_dragItem->setPos(clipPos(itemPos, m_dragItem)); +void PlayGround::mouseReleaseEvent(QMouseEvent *event) +{ + QPoint point = event->pos() - m_mousePressPos; + if (m_allowOnlyDrag || point.manhattanLength() > qApp->startDragDistance()) { + if (m_dragItem) placeDraggedItem(event->pos()); + else if (m_newItem) placeNewItem(event->pos()); } } @@ -317,7 +317,7 @@ bool PlayGround::isAspectRatioLocked() const void PlayGround::registerPlayGrounds() { QSet<QString> list; - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("pics"), QStandardPaths::LocateDirectory); + const QStringList dirs = FileFactory::locateAll(QStringLiteral("pics")); Q_FOREACH (const QString &dir, dirs) { const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.theme")); @@ -327,6 +327,8 @@ void PlayGround::registerPlayGrounds() } } + QMap<QString, QPair<QString, QPixmap>> sortedByName; + foreach(const QString &theme, list) { QFile layoutFile(theme); @@ -336,21 +338,26 @@ void PlayGround::registerPlayGrounds() if (layoutDocument.setContent(&layoutFile)) { QString desktop = layoutDocument.documentElement().attribute(QStringLiteral( "desktop" )); - KConfig c( QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "pics/" ) + desktop ) ); + KConfig c( FileFactory::locate( QLatin1String( "pics/" ) + desktop ) ); KConfigGroup cg = c.group("KTuberlingTheme"); QString gameboard = layoutDocument.documentElement().attribute(QStringLiteral( "gameboard" )); QPixmap pixmap(200,100); pixmap.fill(Qt::transparent); playGroundPixmap(gameboard,pixmap); - m_topLevel->registerGameboard(cg.readEntry("Name"), theme, pixmap); + sortedByName.insertMulti(cg.readEntry("Name"), QPair<QString, QPixmap>(theme, pixmap)); } } } + + for(auto it = sortedByName.begin(); it != sortedByName.end(); ++it) { + m_callbacks->registerGameboard(it.key(), it.value().first, it.value().second); + } + } void PlayGround::playGroundPixmap(const QString &playgroundName, QPixmap &pixmap) { - m_SvgRenderer.load(QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "pics/" ) + playgroundName )); + m_SvgRenderer.load(FileFactory::locate(QLatin1String( "pics/" ) + playgroundName )); QPainter painter(&pixmap); m_SvgRenderer.render(&painter,QStringLiteral( "background" )); } @@ -370,7 +377,6 @@ bool PlayGround::loadPlayGround(const QString &gameboardFile) QFile layoutFile(gameboardFile); if (!layoutFile.open(QIODevice::ReadOnly)) return false; - QDomDocument layoutDocument; if (!layoutDocument.setContent(&layoutFile)) return false; @@ -382,7 +388,7 @@ bool PlayGround::loadPlayGround(const QString &gameboardFile) if (!bgColor.isValid()) bgColor = Qt::white; - if (!m_SvgRenderer.load(QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "pics/" ) + gameboardName ))) + if (!m_SvgRenderer.load(FileFactory::locate( QLatin1String( "pics/" ) + gameboardName ))) return false; objectsList = playGroundElement.elementsByTagName(QStringLiteral( "object" )); @@ -434,6 +440,11 @@ bool PlayGround::loadPlayGround(const QString &gameboardFile) return true; } +void PlayGround::setAllowOnlyDrag(bool allowOnlyDrag) +{ + m_allowOnlyDrag = allowOnlyDrag; +} + QString PlayGround::currentGameboard() const { return m_gameboardFile; @@ -481,7 +492,7 @@ PlayGround::LoadError PlayGround::loadFrom(const QString &name) qreal xFactor = 1.0; qreal yFactor = 1.0; - m_topLevel->changeGameboard(board); + m_callbacks->changeGameboard(board); reset(); diff --git a/playground.h b/playground.h index 39072d3..a671e8b 100644 --- a/playground.h +++ b/playground.h @@ -23,16 +23,24 @@ class KActionCollection; class Action; class ToDraw; -class TopLevel; -class QPrinter; +class QPagedPaintDevice; class QGraphicsSvgItem; +class PlayGroundCallbacks +{ +public: + virtual ~PlayGroundCallbacks() {} + virtual void playSound(const QString &ref) = 0; + virtual void changeGameboard(const QString &gameboard) = 0; + virtual void registerGameboard(const QString& menuText, const QString& boardFile, const QPixmap& pixmap) = 0; +}; + class PlayGround : public QGraphicsView { Q_OBJECT public: - explicit PlayGround(TopLevel *parent); + explicit PlayGround(PlayGroundCallbacks *callbacks, QWidget *parent = nullptr); ~PlayGround(); enum LoadError { NoError, OldFileVersionError, OtherError }; @@ -40,7 +48,7 @@ public: void reset(); LoadError loadFrom(const QString &name); bool saveAs(const QString &name); - bool printPicture(QPrinter &printer); + bool printPicture(QPagedPaintDevice &printer); QPixmap getPicture(); void connectRedoAction(QAction *action); @@ -49,6 +57,8 @@ public: void registerPlayGrounds(); bool loadPlayGround(const QString &gameboardFile); + void setAllowOnlyDrag(bool allowOnlyDrag); + QString currentGameboard() const; bool isAspectRatioLocked() const; @@ -60,6 +70,7 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: @@ -75,12 +86,12 @@ private: QGraphicsScene *scene() const; QUndoStack *undoStack() const; + PlayGroundCallbacks *m_callbacks; QString m_gameboardFile; // the file the board QMap<QString, QString> m_objectsNameSound; // map between element name and sound QMap<QString, double> m_objectsNameRatio; // map between element name and scaling ratio - TopLevel *m_topLevel; // Top-level window - + QPoint m_mousePressPos; QPointF m_itemDraggedPos; ToDraw *m_newItem; // the new item we are moving ToDraw *m_dragItem; // the existing item we are dragging @@ -88,6 +99,7 @@ private: int m_nextZValue; // the next Z value to use bool m_lockAspect; // whether we are locking aspect ratio + bool m_allowOnlyDrag; QUndoGroup m_undoGroup; class SceneData diff --git a/soundfactory.cpp b/soundfactory.cpp index db207df..1a5e197 100644 --- a/soundfactory.cpp +++ b/soundfactory.cpp @@ -14,48 +14,42 @@ #include <stdlib.h> -#include <kmessagebox.h> -#include <KLocalizedString> - -#include <phonon/MediaObject> - #include <QDir> #include <QDomDocument> #include <QFile> -#include <QStandardPaths> +#include <QMediaPlayer> +#include <QSet> +#include <QUrl> -#include "toplevel.h" +#include "filefactory.h" // Constructor -SoundFactory::SoundFactory(TopLevel *parent) +SoundFactory::SoundFactory(SoundFactoryCallbacks *callbacks) + : m_callbacks(callbacks) { - topLevel = parent; - player = Phonon::createPlayer(Phonon::GameCategory); - player->setParent(parent); + player = new QMediaPlayer(); } // Destructor SoundFactory::~SoundFactory() { + delete player; } // Play some sound void SoundFactory::playSound(const QString &soundRef) const { - int sound; - QString soundFile; - - if (!topLevel->isSoundEnabled()) return; + if (!m_callbacks->isSoundEnabled()) return; + int sound; for (sound = 0; sound < sounds; sound++) if (!namesList[sound].compare(soundRef)) break; if (sound == sounds) return; - soundFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "sounds/" ) + filesList[sound]); + const QString soundFile = FileFactory::locate(QLatin1String( "sounds/" ) + filesList[sound]); if (soundFile.isEmpty()) return; -//printf("%s\n", (const char *) soundFile); - player->setCurrentSource(QUrl::fromLocalFile(soundFile)); + player->setMedia(QUrl::fromLocalFile(soundFile)); player->play(); } @@ -63,7 +57,7 @@ void SoundFactory::playSound(const QString &soundRef) const void SoundFactory::registerLanguages() { QSet<QString> list; - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("sounds"), QStandardPaths::LocateDirectory); + const QStringList dirs = FileFactory::locateAll(QStringLiteral("sounds")); Q_FOREACH (const QString &dir, dirs) { const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.soundtheme")); @@ -81,9 +75,9 @@ void SoundFactory::registerLanguages() QDomDocument document; if (document.setContent(&file)) { - QString code = document.documentElement().attribute(QStringLiteral( "code" )); - bool enabled = !(QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "sounds/" ) + code + QLatin1Char( '/' ), QStandardPaths::LocateDirectory).isEmpty()); - topLevel->registerLanguage(code, soundTheme, enabled); + const QString code = document.documentElement().attribute(QStringLiteral( "code" )); + const bool enabled = FileFactory::folderExists(QLatin1String( "sounds/" ) + code + QLatin1Char( '/' )); + m_callbacks->registerLanguage(code, soundTheme, enabled); } } } diff --git a/soundfactory.h b/soundfactory.h index a8d81e1..34b38b6 100644 --- a/soundfactory.h +++ b/soundfactory.h @@ -15,18 +15,20 @@ #include <QStringList> -class TopLevel; +class QMediaPlayer; -namespace Phonon +class SoundFactoryCallbacks { - class MediaObject; -} +public: + virtual ~SoundFactoryCallbacks() {}; + virtual bool isSoundEnabled() const = 0; + virtual void registerLanguage(const QString &code, const QString &soundFile, bool enabled) = 0; +}; class SoundFactory { public: - - explicit SoundFactory(TopLevel *parent); + explicit SoundFactory(SoundFactoryCallbacks *callbacks); ~SoundFactory(); bool loadLanguage(const QString &selectedLanguageFile); @@ -37,14 +39,15 @@ public: void registerLanguages(); private: + SoundFactoryCallbacks *m_callbacks; + QString currentSndFile; // The current language int sounds; // Number of sounds QStringList namesList, // List of sound names filesList; // List of sound files associated with each sound name - TopLevel *topLevel; // Top-level window - Phonon::MediaObject *player; // Sound player + QMediaPlayer *player; }; #endif diff --git a/toplevel.cpp b/toplevel.cpp index 414a78a..6df33b9 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -33,10 +33,10 @@ #include <QMimeDatabase> #include <QPrintDialog> #include <QPrinter> -#include <QStandardPaths> #include <QTemporaryFile> #include <QWidgetAction> +#include "filefactory.h" #include "playground.h" #include "soundfactory.h" #include "playgrounddelegate.h" @@ -52,7 +52,7 @@ TopLevel::TopLevel() { QString board, language; - playGround = new PlayGround(this); + playGround = new PlayGround(this, this); playGround->setObjectName( QStringLiteral( "playGround" ) ); soundFactory = new SoundFactory(this); @@ -146,7 +146,7 @@ void TopLevel::changeGameboard(const QString &newGameBoard) QFileInfo fi(newGameBoard); if (fi.isRelative()) { - fileToLoad = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "pics/" ) + newGameBoard); + fileToLoad = FileFactory::locate(QLatin1String( "pics/" ) + newGameBoard); } else { @@ -193,7 +193,7 @@ void TopLevel::changeLanguage(const QString &soundFile) QFileInfo fi(soundFile); if (fi.isRelative()) { - fileToLoad = QStandardPaths::locate(QStandardPaths::AppDataLocation, QLatin1String( "sounds/" ) + soundFile); + fileToLoad = FileFactory::locate(QLatin1String( "sounds/" ) + soundFile); } else { @@ -217,7 +217,7 @@ void TopLevel::changeLanguage(const QString &soundFile) } // Play a sound -void TopLevel::playSound(const QString &ref) const +void TopLevel::playSound(const QString &ref) { soundFactory->playSound(ref); } diff --git a/toplevel.h b/toplevel.h index d8b3d96..662baea 100644 --- a/toplevel.h +++ b/toplevel.h @@ -14,11 +14,13 @@ #include <kxmlguiwindow.h> #include <kcombobox.h> +#include "soundfactory.h" +#include "playground.h" + class QActionGroup; class PlayGround; -class SoundFactory; -class TopLevel : public KXmlGuiWindow +class TopLevel : public KXmlGuiWindow, public SoundFactoryCallbacks, public PlayGroundCallbacks { Q_OBJECT @@ -28,12 +30,12 @@ public: ~TopLevel(); void open(const QUrl &url); - void registerGameboard(const QString& menuText, const QString& boardFile, const QPixmap& pixmap); - void registerLanguage(const QString &code, const QString &soundFile, bool enabled); + void registerGameboard(const QString& menuText, const QString& boardFile, const QPixmap& pixmap) override; + void registerLanguage(const QString &code, const QString &soundFile, bool enabled) override; void changeLanguage(const QString &langCode); - void playSound(const QString &ref) const; + void playSound(const QString &ref) override; - bool isSoundEnabled() const; + bool isSoundEnabled() const override; void changeGameboard(const QString &gameboard);
