diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) +include(KDEPackageAppTemplates) include(ECMAddQch) include(GenerateExportHeader) @@ -54,6 +55,7 @@ ki18n_install(po) endif() add_subdirectory( src ) +add_subdirectory(templates) # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Parts") diff --git a/ExtraDesktop.sh b/ExtraDesktop.sh new file mode 100644 --- /dev/null +++ b/ExtraDesktop.sh @@ -0,0 +1,4 @@ +#! /bin/sh +#This file outputs in a separate line each file with a .desktop syntax +#that needs to be translated but has a non .desktop extension +find -name \*.kdevtemplate -print diff --git a/templates/CMakeLists.txt b/templates/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/templates/CMakeLists.txt @@ -0,0 +1,5 @@ +set(apptemplate_DIRS + kpartsapp +) + +kde_package_app_templates(TEMPLATES ${apptemplate_DIRS} INSTALL_DIR ${KDE_INSTALL_KTEMPLATESDIR}) diff --git a/templates/kpartsapp/CMakeLists.txt b/templates/kpartsapp/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0) + +project(%{APPNAMELC}) + +set(REQUIRED_ECM_VERSION "5.23.0") +find_package(ECM ${REQUIRED_ECM_VERSION} REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDECompilerSettings NO_POLICY_SCOPE) +include(ECMInstallIcons) +include(FeatureSummary) + +set(QT_MIN_VERSION "5.6.0") +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS + Widgets +) + +set(REQUIRED_KF5_VERSION "5.23.0") +find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS + I18n + Parts +) + +add_subdirectory(src) +add_subdirectory(icons) + +feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/templates/kpartsapp/README b/templates/kpartsapp/README new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/README @@ -0,0 +1,46 @@ +How To Build This Project +-=-=-=-=-=-=-=-=-=-=-=-=-= + +--- On Unix: + +cd %{dest} +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=$PROJECTINSTALLDIR -DCMAKE_BUILD_TYPE=Debug .. <- do not forget the .. +make +make install or su -c 'make install' + +where $PROJECTINSTALLDIR points to your installation prefix. + +to uninstall the project: +make uninstall or su -c 'make uninstall' + +Note: you can use another build path. Then cd in your build dir and: +export KDE_SRC=path_to_your_src +cmake $KDE_SRC -DCMAKE_INSTALL_PREFIX=$PROJECTINSTALLDIR -DCMAKE_BUILD_TYPE=Debug + +--- On Windows: + +cd %{dest} +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=%PROJECTINSTALLDIR% -DCMAKE_BUILD_TYPE=Debug .. <- do not forget the .. +[n]make +[n]make install + +where %PROJECTINSTALLDIR% points to your installation prefix. + +to uninstall the project: +[n]make uninstall + +Note: use nmake if you're building with the Visual Studio compiler, or make +if you're using the minGW compiler + + +Tutorials +-=-=-=-=- +KParts docs +https://techbase.kde.org/Development/Architecture/KDE4/KParts +https://api.kde.org/frameworks/kparts/html/index.html +Tutorial +https://techbase.kde.org/Development/Tutorials/Using_KParts diff --git a/templates/kpartsapp/icons/16-apps-%{APPNAMELC}.png b/templates/kpartsapp/icons/16-apps-%{APPNAMELC}.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ + * + * 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 %{APPNAMEUC}PART_H +#define %{APPNAMEUC}PART_H + +// KF headers +#include + +class QTextEdit; +class QWidget; +class QAction; +class QTextDocument; + +/** + * @short %{APPNAME} Part + */ +class %{APPNAME}Part : public KParts::ReadWritePart +{ + Q_OBJECT + +public: + /** + * Default constructor, with arguments as expected by KPluginFactory + */ + %{APPNAME}Part(QWidget* parentWidget, QObject* parent, const QVariantList& arg); + + /** + * Destructor + */ + ~%{APPNAME}Part() override; + + /** + * Reimplemented to update the internal UI + */ + void setReadWrite(bool rw) override; + + /** + * Reimplemented to disable and enable Save action + */ + void setModified(bool modified) override; + +protected: // KParts::ReadWritePart API + bool openFile() override; + bool saveFile() override; + +private: + void setupActions(); + +private Q_SLOTS: + void fileSave(); + void fileSaveAs(); + +private: + QTextEdit* m_textEditWidget; + QAction* m_saveAction; + QTextDocument* m_textDocument; +}; + +#endif // %{APPNAMEUC}PART_H diff --git a/templates/kpartsapp/src/part/%{APPNAMELC}part.cpp b/templates/kpartsapp/src/part/%{APPNAMELC}part.cpp new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/part/%{APPNAMELC}part.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) %{CURRENT_YEAR} by %{AUTHOR} <%{EMAIL}> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . + */ + +#include "%{APPNAMELC}part.h" + +// KF headers +#include +#include +#include +#include +#include + +// Qt headers +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(%{APPNAME}PartFactory, registerPlugin<%{APPNAME}Part>();) + + +%{APPNAME}Part::%{APPNAME}Part(QWidget* parentWidget, QObject* parent, const QVariantList& /*args*/) + : KParts::ReadWritePart(parent) +{ + // set component data + // the first arg must be the same as the subdirectory into which the part's rc file is installed + KAboutData aboutData("%{APPNAMELC}part", i18n("%{APPNAME}Part"), QStringLiteral("%{VERSION}")); + aboutData.addAuthor(i18n("%{AUTHOR}"), i18n("Author"), QStringLiteral("%{EMAIL}")); + setComponentData(aboutData); + + // set internal UI + // TODO: replace with your custom UI + m_textEditWidget = new QTextEdit(parentWidget); + setWidget(m_textEditWidget); + + // set KXMLUI resource file + setXMLFile(QStringLiteral("%{APPNAMELC}partui.rc")); + + // setup actions + setupActions(); + + // starting with empty data model, not modified at begin + // TODO: replace with your custom data model + m_textDocument = new QTextDocument(this); + m_textEditWidget->setDocument(m_textDocument); + setModified(false); + + // set part read-write by default + setReadWrite(true); +} + +%{APPNAME}Part::~%{APPNAME}Part() +{ +} + +void %{APPNAME}Part::setupActions() +{ + m_saveAction = KStandardAction::save(this, &%{APPNAME}Part::fileSave, actionCollection()); + KStandardAction::saveAs(this, &%{APPNAME}Part::fileSaveAs, actionCollection()); +} + +void %{APPNAME}Part::setReadWrite(bool rw) +{ + // update internal UI + m_textEditWidget->setReadOnly(!rw); + + // connect to modified state of data model + if (rw) { + connect(m_textDocument, &QTextDocument::modificationChanged, + this, &%{APPNAME}Part::setModified); + } else { + disconnect(m_textDocument, &QTextDocument::modificationChanged, + this, &%{APPNAME}Part::setModified); + } + + ReadWritePart::setReadWrite(rw); +} + +void %{APPNAME}Part::setModified(bool modified) +{ + // update actions + m_saveAction->setEnabled(modified); + + ReadWritePart::setModified(modified); +} + +bool %{APPNAME}Part::openFile() +{ + QFile file(localFilePath()); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + + // TODO: replace with your custom file reading + QTextStream stream(&file); + QString text; + while (!stream.atEnd()) { + text += stream.readLine() + QLatin1Char('\n'); + } + + file.close(); + + m_textDocument->setPlainText(text); + + return true; +} + +bool %{APPNAME}Part::saveFile() +{ + // protect against wrong calls, as recommended in the ReadWritePart API dox + if (!isReadWrite()) { + return false; + } + + QFile file(localFilePath()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + return false; + } + + // TODO: replace with your custom file writing + QTextStream stream(&file); + stream << m_textDocument->toPlainText(); + + file.close(); + + // set current state in the data model as saved + m_textDocument->setModified(false); + + return true; +} + +void %{APPNAME}Part::fileSave() +{ + if (url().isValid()) { + save(); + } else { + fileSaveAs(); + } +} + +void %{APPNAME}Part::fileSaveAs() +{ + const QUrl url = QFileDialog::getSaveFileUrl(); + if (url.isValid()) { + saveAs(url); + } +} + +// needed for K_PLUGIN_FACTORY +#include <%{APPNAMELC}part.moc> diff --git a/templates/kpartsapp/src/part/%{APPNAMELC}part.desktop b/templates/kpartsapp/src/part/%{APPNAMELC}part.desktop new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/part/%{APPNAMELC}part.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Service +Name=%{APPNAME}Part +Icon=%{APPNAMELC} +# TODO: replace with your custom supported mime types +MimeType=text/plain; +X-KDE-ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart +X-KDE-Library=%{APPNAMELC}part diff --git a/templates/kpartsapp/src/part/%{APPNAMELC}partui.rc b/templates/kpartsapp/src/part/%{APPNAMELC}partui.rc new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/part/%{APPNAMELC}partui.rc @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/templates/kpartsapp/src/part/CMakeLists.txt b/templates/kpartsapp/src/part/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/part/CMakeLists.txt @@ -0,0 +1,16 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"%{APPNAMELC}part\") + +set(%{APPNAMELC}_PART_SRCS + %{APPNAMELC}part.cpp +) + +add_library(%{APPNAMELC}part MODULE ${%{APPNAMELC}_PART_SRCS}) + +target_link_libraries(%{APPNAMELC}part + KF5::I18n + KF5::Parts +) + +install(TARGETS %{APPNAMELC}part DESTINATION ${KDE_INSTALL_PLUGINDIR}) +install(PROGRAMS %{APPNAMELC}part.desktop DESTINATION ${KDE_INSTALL_APPDIR}) +install(FILES %{APPNAMELC}partui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/%{APPNAMELC}part) diff --git a/templates/kpartsapp/src/part/Messages.sh b/templates/kpartsapp/src/part/Messages.sh new file mode 100755 --- /dev/null +++ b/templates/kpartsapp/src/part/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc` >> rc.cpp +$XGETTEXT `find . -name \*.cpp` -o $podir/%{APPNAMELC}part.pot diff --git a/templates/kpartsapp/src/shell/%{APPNAMELC}.desktop b/templates/kpartsapp/src/shell/%{APPNAMELC}.desktop new file mode 100755 --- /dev/null +++ b/templates/kpartsapp/src/shell/%{APPNAMELC}.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=%{APPNAME} +Icon=%{APPNAMELC} +# TODO: replace with your custom supported mime types +MimeType=text/plain; +Exec=%{APPNAMELC} %U +Terminal=false diff --git a/templates/kpartsapp/src/shell/%{APPNAMELC}shell.h b/templates/kpartsapp/src/shell/%{APPNAMELC}shell.h new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/shell/%{APPNAMELC}shell.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) %{CURRENT_YEAR} by %{AUTHOR} <%{EMAIL}> + * + * 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 %{APPNAMEUC}SHELL_H +#define %{APPNAMEUC}SHELL_H + +// KF headers +#include + +namespace KParts { +class ReadWritePart; +} + +/** + * @short %{APPNAME} Shell + */ +class %{APPNAME}Shell : public KParts::MainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + %{APPNAME}Shell(); + + /** + * Default Destructor + */ + ~%{APPNAME}Shell() override; + + /** + * Use this method to load whatever file/URL you have + * @param url document to load + */ + void loadDocument(const QUrl& url); + +private Q_SLOTS: + void fileNew(); + void fileOpen(); + +private: + void setupActions(); + +private: + KParts::ReadWritePart* m_part; +}; + +#endif // %{APPNAMEUC}_H diff --git a/templates/kpartsapp/src/shell/%{APPNAMELC}shell.cpp b/templates/kpartsapp/src/shell/%{APPNAMELC}shell.cpp new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/shell/%{APPNAMELC}shell.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) %{CURRENT_YEAR} by %{AUTHOR} <%{EMAIL}> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . + */ + +#include "%{APPNAMELC}shell.h" + +// KF headers +#include +#include +#include +#include +#include +#include +#include + +// Qt headers +#include +#include + +%{APPNAME}Shell::%{APPNAME}Shell() + : KParts::MainWindow() +{ + // set KXMLUI resource file + setXMLFile(QStringLiteral("%{APPNAMELC}ui.rc")); + + // setup our actions + setupActions(); + + // find and load the part + // Doing it by name, which is a bad idea usually but alright here + // since this part is made for this shell + KPluginLoader loader(QStringLiteral("%{APPNAMELC}part")); + auto factory = loader.factory(); + if (!factory) { + // can't do anything useful without part, thus quit the app + KMessageBox::error(this, i18n("Could not find %{APPNAME} part!")); + + qApp->quit(); + // return here, because qApp->quit() only means "exit the + // next time we enter the event loop... + return; + } + + m_part = factory->create(this); + + if (m_part) { + // integrate and setup + setCentralWidget(m_part->widget()); + setupGUI(ToolBar | Keys | StatusBar | Save); + createGUI(m_part); + } +} + +%{APPNAME}Shell::~%{APPNAME}Shell() +{ +} + +void %{APPNAME}Shell::loadDocument(const QUrl& url) +{ + m_part->openUrl(url); +} + +void %{APPNAME}Shell::setupActions() +{ + KStandardAction::openNew(this, &%{APPNAME}Shell::fileNew, actionCollection()); + KStandardAction::open(this, &%{APPNAME}Shell::fileOpen, actionCollection()); + + KStandardAction::quit(qApp, &QApplication::closeAllWindows, actionCollection()); +} + +void %{APPNAME}Shell::fileNew() +{ + // open a new window if the document is _not_ in its initial state + if (!m_part->url().isValid() || m_part->isModified()) { + (new %{APPNAME}Shell)->show(); + }; +} + +void %{APPNAME}Shell::fileOpen() +{ + const QUrl url = QFileDialog::getOpenFileUrl(this); + + if (url.isValid()) { + // open a new window if the document is _not_ in its initial state + if (!m_part->url().isValid() || m_part->isModified()) { + // open the file in a new window + auto window = new %{APPNAME}Shell; + window->loadDocument(url); + window->show(); + } else { + // open the file in this window + loadDocument(url); + } + } +} diff --git a/templates/kpartsapp/src/shell/%{APPNAMELC}ui.rc b/templates/kpartsapp/src/shell/%{APPNAMELC}ui.rc new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/shell/%{APPNAMELC}ui.rc @@ -0,0 +1,30 @@ + + + + &File + + + + + + + + &Settings + + + + + + + + + + + + +Main Toolbar + + + + + diff --git a/templates/kpartsapp/src/shell/CMakeLists.txt b/templates/kpartsapp/src/shell/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/shell/CMakeLists.txt @@ -0,0 +1,15 @@ +set(%{APPNAMELC}_SRCS + main.cpp + %{APPNAMELC}shell.cpp +) + +add_executable(%{APPNAMELC} ${%{APPNAMELC}_SRCS}) + +target_link_libraries(%{APPNAMELC} + KF5::I18n + KF5::Parts +) + +install(TARGETS %{APPNAMELC} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(PROGRAMS %{APPNAMELC}.desktop DESTINATION ${KDE_INSTALL_APPDIR}) +install(FILES %{APPNAMELC}ui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/%{APPNAMELC}) diff --git a/templates/kpartsapp/src/shell/Messages.sh b/templates/kpartsapp/src/shell/Messages.sh new file mode 100755 --- /dev/null +++ b/templates/kpartsapp/src/shell/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc` >> rc.cpp +$XGETTEXT `find . -name \*.cpp` -o $podir/%{APPNAMELC}.pot diff --git a/templates/kpartsapp/src/shell/main.cpp b/templates/kpartsapp/src/shell/main.cpp new file mode 100644 --- /dev/null +++ b/templates/kpartsapp/src/shell/main.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) %{CURRENT_YEAR} by %{AUTHOR} <%{EMAIL}> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . + */ + +#include "%{APPNAMELC}shell.h" + +// KF headers +#include +#include + +// Qt headers +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + KLocalizedString::setApplicationDomain("%{APPNAMELC}"); + + KAboutData aboutData(QStringLiteral("%{APPNAMELC}"), + i18n("%{APPNAME}"), + QStringLiteral("%{VERSION}"), + i18n("A KPart Application"), + KAboutLicense::GPL, + i18n("Copyright %{CURRENT_YEAR} %{AUTHOR}")); + aboutData.addAuthor(i18n("%{AUTHOR}"), i18n("Author"), QStringLiteral("%{EMAIL}")); + aboutData.setOrganizationDomain("example.org"); + aboutData.setDesktopFileName(QStringLiteral("org.example.%{APPNAMELC}")); + + KAboutData::setApplicationData(aboutData); + app.setWindowIcon(QIcon::fromTheme(QStringLiteral("%{APPNAMELC}"))); + + QCommandLineParser parser; + parser.addHelpOption(); + parser.addVersionOption(); + aboutData.setupCommandLine(&parser); + parser.addPositionalArgument(QStringLiteral("urls"), i18n("Document(s) to load."), QStringLiteral("[urls...]")); + + parser.process(app); + aboutData.processCommandLine(&parser); + + const auto urls = parser.positionalArguments(); + + if (urls.isEmpty()) { + auto window = new %{APPNAME}Shell; + window->show(); + } else { + for (const auto &url : urls) { + auto window = new %{APPNAME}Shell; + window->show(); + window->loadDocument(QUrl::fromUserInput(url, QDir::currentPath(), QUrl::AssumeLocalFile)); + } + } + + return app.exec(); +}