diff --git a/app/SettingsBase.cpp b/app/SettingsBase.cpp index 5a4a6347..c9739d5c 100644 --- a/app/SettingsBase.cpp +++ b/app/SettingsBase.cpp @@ -1,474 +1,499 @@ /*************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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 "SettingsBase.h" #include "BaseConfig.h" #include "systemsettings_app_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BaseData.h" #include "ModuleView.h" SettingsBase::SettingsBase(BaseMode::ApplicationMode mode, QWidget * parent ) : KXmlGuiWindow(parent), m_mode(mode) { // Ensure delayed loading doesn't cause a crash activeView = nullptr; aboutDialog = nullptr; configDialog = nullptr; lostFound = nullptr; // Prepare the view area stackedWidget = new QStackedWidget( this ); setCentralWidget(stackedWidget); setWindowFlags( windowFlags() | Qt::WindowContextHelpButtonHint ); // Initialise search searchText = new KLineEdit( this ); searchText->setClearButtonEnabled( true ); searchText->setPlaceholderText( i18nc( "Search through a list of control modules", "Search" ) ); searchText->setCompletionMode( KCompletion::CompletionPopup ); if (m_mode == BaseMode::InfoCenter) { actionCollection()->removeAction(configureAction); configureAction = nullptr; setWindowTitle(i18n("Info Center")); setWindowIcon(QIcon::fromTheme(QStringLiteral("hwinfo"))); } else { setWindowTitle(i18n("System Settings")); setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-system"))); } spacerWidget = new QWidget( this ); spacerWidget->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Maximum ); // Initialise the window so we don't flicker initToolBar(); // We can now launch the delayed loading safely QTimer::singleShot(0, this, &SettingsBase::initApplication); } SettingsBase::~SettingsBase() { delete rootModule; } QSize SettingsBase::sizeHint() const { // on smaller or portrait-rotated screens, do not max out height and/or width const QSize screenSize = (QGuiApplication::primaryScreen()->availableSize()*0.9); const QSize targetSize = QSize(1024, 700); return targetSize.boundedTo(screenSize); } void SettingsBase::initApplication() { // Prepare the menu of all modules if (m_mode == BaseMode::InfoCenter) { categories = KServiceTypeTrader::self()->query(QStringLiteral("KInfoCenterCategory")); modules = KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QStringLiteral("[X-KDE-ParentApp] == 'kinfocenter'")); } else { categories = KServiceTypeTrader::self()->query(QStringLiteral("SystemSettingsCategory")); modules = KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QStringLiteral("[X-KDE-System-Settings-Parent-Category] != ''")); modules += KServiceTypeTrader::self()->query(QStringLiteral("SystemSettingsExternalApp")); } rootModule = new MenuItem( true, nullptr ); initMenuList(rootModule); // Handle lost+found modules... if (lostFound) { for (int i = 0; i < modules.size(); ++i) { const KService::Ptr entry = modules.at(i); MenuItem * infoItem = new MenuItem(false, lostFound); infoItem->setService( entry ); qCDebug(SYSTEMSETTINGS_APP_LOG) << "Added " << entry->name(); } } // Prepare the Base Data BaseData::instance()->setMenuItem( rootModule ); BaseData::instance()->setHomeItem( homeModule ); // Load all possible views const KService::List pluginObjects = KServiceTypeTrader::self()->query( QStringLiteral("SystemSettingsView") ); const int nbPlugins = pluginObjects.count(); for( int pluginsDone = 0; pluginsDone < nbPlugins ; ++pluginsDone ) { KService::Ptr activeService = pluginObjects.at( pluginsDone ); QString error; - BaseMode * controller = activeService->createInstance(this, {m_mode}, &error); + BaseMode * controller = activeService->createInstance(this, {m_mode, m_startupModule, m_startupModuleArgs}, &error); if( error.isEmpty() ) { possibleViews.insert( activeService->library(), controller ); controller->init( activeService ); connect(controller, &BaseMode::changeToolBarItems, this, &SettingsBase::changeToolBar); connect(controller, &BaseMode::actionsChanged, this, &SettingsBase::updateViewActions); connect(searchText, &KLineEdit::textChanged, controller, &BaseMode::searchChanged); connect(controller, &BaseMode::viewChanged, this, &SettingsBase::viewChange); } else { qCWarning(SYSTEMSETTINGS_APP_LOG) << QStringLiteral("View load error: ") + error; } } searchText->completionObject()->setIgnoreCase( true ); searchText->completionObject()->setItems( BaseData::instance()->menuItem()->keywords() ); changePlugin(); // enforce minimum window size setMinimumSize(SettingsBase::sizeHint()); activateWindow(); } void SettingsBase::initToolBar() { // Fill the toolbar with default actions // Exit is the very last action quitAction = actionCollection()->addAction( KStandardAction::Quit, QStringLiteral("quit_action"), this, SLOT(close()) ); // Configure goes at the end configureAction = actionCollection()->addAction( KStandardAction::Preferences, QStringLiteral("configure"), this, SLOT(configShow()) ); configureAction->setText( i18n("Configure...") ); // Help after it initHelpMenu(); configureAction->setIcon(QIcon::fromTheme(QStringLiteral("settings-configure"))); // There's nothing to configure in info center mode if (m_mode == BaseMode::InfoCenter) { configureAction->setVisible(false); } // Then a spacer so the search line-edit is kept separate spacerAction = new QWidgetAction( this ); spacerAction->setDefaultWidget(spacerWidget); actionCollection()->addAction( QStringLiteral("spacer"), spacerAction ); // Finally the search line-edit searchAction = new QWidgetAction( this ); searchAction->setDefaultWidget(searchText); connect( searchAction, SIGNAL(triggered(bool)), searchText, SLOT(setFocus())); actionCollection()->addAction( QStringLiteral("searchText"), searchAction ); // Initialise the Window setupGUI(Save|Create,QString()); menuBar()->hide(); // Toolbar & Configuration helpActionMenu->setMenu( dynamic_cast( factory()->container(QStringLiteral("help"), this) ) ); toolBar()->setMovable(false); // We don't allow any changes changeToolBar( BaseMode::Search | BaseMode::Configure ); } void SettingsBase::initHelpMenu() { helpActionMenu = new KActionMenu( QIcon::fromTheme(QStringLiteral("help-contents")), i18n("Help"), this ); helpActionMenu->setDelayed( false ); actionCollection()->addAction( QStringLiteral("help_toolbar_menu"), helpActionMenu ); // Add the custom actions aboutModuleAction = actionCollection()->addAction( KStandardAction::AboutApp, QStringLiteral("help_about_module"), this, SLOT(about()) ); changeAboutMenu( nullptr, aboutModuleAction, i18n("About Active Module") ); aboutViewAction = actionCollection()->addAction( KStandardAction::AboutApp, QStringLiteral("help_about_view"), this, SLOT(about()) ); } void SettingsBase::initConfig() { // Prepare dialog first configDialog = new KConfigDialog( this, QStringLiteral("systemsettingsconfig"), BaseConfig::self() ); configDialog->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); // Add our page QWidget * configPage = new QWidget( configDialog ); configWidget.setupUi(configPage); QString iconName = KAboutData::applicationData().programIconName(); configDialog->addPage( configPage, i18nc("General config for System Settings", "General"), iconName ); QVBoxLayout * configLayout = new QVBoxLayout; // Get the list of modules foreach( BaseMode * mode, possibleViews ) { mode->addConfiguration( configDialog ); QRadioButton * radioButton = new QRadioButton( mode->service()->name(), configWidget.GbViewStyle ); radioButton->setIcon( QIcon::fromTheme(mode->service()->icon()) ); configLayout->addWidget( radioButton ); viewSelection.addButton( radioButton, possibleViews.values().indexOf(mode) ); } configWidget.GbViewStyle->setLayout( configLayout ); configWidget.GbViewStyle->setVisible( possibleViews.count() > 1 ); KWindowConfig::restoreWindowSize(configDialog->windowHandle(), KSharedConfig::openConfig()->group("ConfigDialog")); connect(configDialog, &KConfigDialog::accepted, this, &SettingsBase::configUpdated); } void SettingsBase::initMenuList(MenuItem * parent) { // look for any categories inside this level, and recurse into them for (int i = 0; i < categories.size(); ++i) { const KService::Ptr entry = categories.at(i); QString parentCategory; QString parentCategory2; if (m_mode == BaseMode::InfoCenter) { parentCategory = entry->property(QStringLiteral("X-KDE-KInfoCenter-Parent-Category")).toString(); } else { parentCategory = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category")).toString(); parentCategory2 = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category-V2")).toString(); } if (parentCategory == parent->category() || // V2 entries must not be empty if they want to become a proper category. ( !parentCategory2.isEmpty() && parentCategory2 == parent->category() ) ) { MenuItem * menuItem = new MenuItem(true, parent); menuItem->setService( entry ); if( menuItem->category() == QLatin1String("lost-and-found") ) { lostFound = menuItem; continue; } initMenuList( menuItem ); } } KService::List removeList; // scan for any modules at this level and add them for (int i = 0; i < modules.size(); ++i) { const KService::Ptr entry = modules.at(i); QString category; QString category2; if (m_mode == BaseMode::InfoCenter) { category = entry->property(QStringLiteral("X-KDE-KInfoCenter-Category")).toString(); } else { category = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category")).toString(); category2 = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category-V2")).toString(); } if( !parent->category().isEmpty() && (category == parent->category() || category2 == parent->category()) ) { if (!entry->noDisplay() ) { // Add the module info to the menu MenuItem * infoItem = new MenuItem(false, parent); infoItem->setService( entry ); if (m_mode == BaseMode::InfoCenter && entry->pluginKeyword() == QStringLiteral("kcm-about-distro")) { homeModule = infoItem; } } removeList.append( modules.at(i) ); } } for (int i = 0; i < removeList.size(); ++i) { modules.removeOne( removeList.at(i) ); } parent->sortChildrenByWeight(); } void SettingsBase::configUpdated() { KConfigGroup dialogConfig = KSharedConfig::openConfig()->group("ConfigDialog"); KWindowConfig::saveWindowSize(configDialog->windowHandle(), dialogConfig); BaseConfig::setActiveView( possibleViews.keys().at(viewSelection.checkedId()) ); BaseConfig::setShowToolTips( configWidget.ChTooltips->isChecked() ); activeView->setShowToolTips( configWidget.ChTooltips->isChecked() ); activeView->saveConfiguration(); changePlugin(); } void SettingsBase::configShow() { // Initialise the configuration dialog if it hasn't already if( !configDialog ) { initConfig(); } if( activeView && activeView->moduleView() && !activeView->moduleView()->resolveChanges() ) { return; // It shouldn't be triggering anyway, since the action is disabled } activeView->loadConfiguration(); const QStringList pluginList = possibleViews.keys(); const int configIndex = pluginList.indexOf( BaseConfig::activeView() ); if( configIndex != -1 ) { viewSelection.button( configIndex )->setChecked(true); } configWidget.ChTooltips->setChecked( BaseConfig::showToolTips() ); if( pluginList.isEmpty() ) { KMessageBox::error(this, i18n("System Settings was unable to find any views, and hence nothing is available to configure."), i18n("No views found")); } else { configDialog->show(); } } bool SettingsBase::queryClose() { bool changes = true; if( activeView ) { activeView->saveState(); changes = activeView->moduleView()->resolveChanges(); } BaseConfig::self()->save(); return changes; } +void SettingsBase::setStartupModule(const QString &startupModule) +{ + m_startupModule = startupModule; + + if (activeView) { + activeView->setStartupModule(startupModule); + } +} + +void SettingsBase::setStartupModuleArgs(const QStringList &startupModuleArgs) +{ + m_startupModuleArgs = startupModuleArgs; + + if (activeView) { + activeView->setStartupModuleArgs(startupModuleArgs); + } +} + +void SettingsBase::reloadStartupModule() +{ + if (activeView) { + activeView->reloadStartupModule(); + } +} + void SettingsBase::about() { delete aboutDialog; aboutDialog = nullptr; const KAboutData * about = nullptr; if( sender() == aboutViewAction ) { about = activeView->aboutData(); } else if( sender() == aboutModuleAction && activeView->moduleView() ) { about = activeView->moduleView()->aboutData(); } if( about ) { aboutDialog = new KAboutApplicationDialog(*about, nullptr); aboutDialog->show(); } } void SettingsBase::changePlugin() { if( possibleViews.isEmpty() ) { // We should ensure we have a plugin available to choose KMessageBox::error(this, i18n("System Settings was unable to find any views, and hence has nothing to display."), i18n("No views found")); close(); return; // Halt now! } if( activeView ) { activeView->saveState(); activeView->leaveModuleView(); } const QString viewToUse = m_mode == BaseMode::InfoCenter ? QStringLiteral("systemsettings_sidebar_mode") : BaseConfig::activeView(); if( possibleViews.keys().contains(viewToUse) ) { // First the configuration entry activeView = possibleViews.value(viewToUse); } else { // Otherwise we activate the failsafe activeView = possibleViews.begin().value(); } if( stackedWidget->indexOf(activeView->mainWidget()) == -1 ) { stackedWidget->addWidget(activeView->mainWidget()); } show(); // Handle the tooltips qDeleteAll( tooltipManagers ); tooltipManagers.clear(); if ( BaseConfig::showToolTips() ) { QList theViews = activeView->views(); foreach ( QAbstractItemView* view, theViews ) { tooltipManagers << new ToolTipManager( view ); } } activeView->setShowToolTips( BaseConfig::showToolTips() ); changeAboutMenu( activeView->aboutData(), aboutViewAction, i18n("About Active View") ); viewChange(false); stackedWidget->setCurrentWidget(activeView->mainWidget()); updateViewActions(); activeView->giveFocus(); } void SettingsBase::viewChange(bool state) { KCModuleInfo * moduleInfo = activeView->moduleView()->activeModule(); if (configureAction) { configureAction->setDisabled(state); } if( moduleInfo ) { setCaption( moduleInfo->moduleName(), state ); } else { setCaption( QString(), state ); } changeAboutMenu( activeView->moduleView()->aboutData(), aboutModuleAction, i18n("About Active Module") ); } void SettingsBase::updateViewActions() { guiFactory()->unplugActionList( this, QStringLiteral("viewActions") ); guiFactory()->plugActionList( this, QStringLiteral("viewActions"), activeView->actionsList() ); } void SettingsBase::changeToolBar( BaseMode::ToolBarItems toolbar ) { if( sender() != activeView ) { return; } guiFactory()->unplugActionList( this, QStringLiteral("configure") ); guiFactory()->unplugActionList( this, QStringLiteral("search") ); guiFactory()->unplugActionList( this, QStringLiteral("quit") ); if ( BaseMode::Search & toolbar ) { QList searchBarActions; searchBarActions << spacerAction << searchAction; guiFactory()->plugActionList( this, QStringLiteral("search"), searchBarActions ); actionCollection()->setDefaultShortcut(searchAction, QKeySequence(Qt::CTRL + Qt::Key_F)); } if ( (BaseMode::Configure & toolbar) && configureAction) { QList configureBarActions; configureBarActions << configureAction; guiFactory()->plugActionList( this, QStringLiteral("configure"), configureBarActions ); } if ( BaseMode::Quit & toolbar ) { QList quitBarActions; quitBarActions << quitAction; guiFactory()->plugActionList( this, QStringLiteral("quit"), quitBarActions ); } if (BaseMode::NoItems & toolbar) { // Remove search shortcut when there's no toolbar so it doesn't // interfere with the built-in shortcut for the search field in the QML // sidebar view actionCollection()->setDefaultShortcut(searchAction, QKeySequence()); } toolBar()->setVisible(toolbar != BaseMode::NoItems || (activeView && activeView->actionsList().count() > 0)); } void SettingsBase::changeAboutMenu( const KAboutData * menuAbout, QAction * menuItem, const QString &fallback ) { if( !menuItem ) { return; } if( menuAbout ) { menuItem->setText( i18n( "About %1", menuAbout->displayName() ) ); menuItem->setIcon( QIcon::fromTheme( menuAbout->programIconName() ) ); menuItem->setEnabled(true); } else { menuItem->setText( fallback ); menuItem->setIcon( QIcon::fromTheme( KAboutData::applicationData().programIconName() ) ); menuItem->setEnabled(false); } } diff --git a/app/SettingsBase.h b/app/SettingsBase.h index e6ba07e6..e8381d68 100644 --- a/app/SettingsBase.h +++ b/app/SettingsBase.h @@ -1,100 +1,106 @@ /*************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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 SETTINGS_BASE_H #define SETTINGS_BASE_H #include "MenuItem.h" #include "BaseMode.h" #include "tooltipmanager.h" #include "ui_configDialog.h" #include #include #include #include #include #include #include #include #include class SettingsBase : public KXmlGuiWindow { Q_OBJECT public: explicit SettingsBase(BaseMode::ApplicationMode mode, QWidget * parent = nullptr); ~SettingsBase() override; bool isInfoCenterMode() const; bool queryClose() override; + void setStartupModule(const QString &startupModule); + void setStartupModuleArgs(const QStringList &startupModuleArgs); + void reloadStartupModule(); + protected: QSize sizeHint() const override; private Q_SLOTS: void initApplication(); void initToolBar(); void initHelpMenu(); void initConfig(); void initMenuList(MenuItem * parent); void configUpdated(); void configShow(); void about(); void changePlugin(); void viewChange(bool state); void updateViewActions(); void changeToolBar( BaseMode::ToolBarItems toolbar ); void changeAboutMenu( const KAboutData * menuAbout, QAction * menuItem, const QString &fallback ); private: // The plugins QMap possibleViews; QList tooltipManagers; BaseMode * activeView = nullptr; // The search bar KLineEdit * searchText = nullptr; QWidget * spacerWidget = nullptr; // The toolbar QWidgetAction * searchAction = nullptr; QWidgetAction * spacerAction = nullptr; QAction * configureAction = nullptr; QAction * quitAction = nullptr; // The help menu QAction * aboutViewAction = nullptr; QAction * aboutModuleAction = nullptr; KActionMenu * helpActionMenu = nullptr; // The configuration KConfigDialog * configDialog = nullptr; Ui::ConfigDialog configWidget; QButtonGroup viewSelection; // The control module QStackedWidget * stackedWidget = nullptr; // The module list MenuItem * rootModule = nullptr; MenuItem * homeModule = nullptr; MenuItem * lostFound = nullptr; KService::List categories; KService::List modules; // The about dialog KAboutApplicationDialog * aboutDialog = nullptr; BaseMode::ApplicationMode m_mode = BaseMode::SystemSettings; + QString m_startupModule; + QStringList m_startupModuleArgs; }; #endif diff --git a/app/SystemSettingsApp.cpp b/app/SystemSettingsApp.cpp index acd6ed8b..4f96d3b1 100644 --- a/app/SystemSettingsApp.cpp +++ b/app/SystemSettingsApp.cpp @@ -1,51 +1,62 @@ /** * Copyright (C) 2009 Ben Cooksley * * 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 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 "SystemSettingsApp.h" #include #include SystemSettingsApp::SystemSettingsApp(int& argc, char* argv[]) : QApplication(argc, argv) { setOrganizationDomain(QStringLiteral("kde.org")); KDBusService* service = new KDBusService(KDBusService::Unique, this); - QObject::connect(service, &KDBusService::activateRequested, this, [=]() { - if (window) { - KWindowSystem::forceActiveWindow(window->winId()); + QObject::connect(service, &KDBusService::activateRequested, this, [=](const QStringList &arguments, const QString &workingDirectory) { + if (!window) { + return; } + + if (arguments.size() > 1) { + window->setStartupModule(arguments[1]); + + if (arguments.size() > 2) { + window->setStartupModuleArgs(arguments.mid(2)); + } + window->reloadStartupModule(); + } + + KWindowSystem::forceActiveWindow(window->winId()); } ); } SystemSettingsApp::~SystemSettingsApp() { } void SystemSettingsApp::setMainWindow(SettingsBase * main) { window = main; } void SystemSettingsApp::quit() { if( window && !window->queryClose() ) { return; } QApplication::quit(); } diff --git a/app/main.cpp b/app/main.cpp index a9da2d7b..6f91c6fb 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,102 +1,169 @@ /** * Copyright (C) 2009 Ben Cooksley * * This file was sourced from the System Settings package * Copyright (C) 2005 Benjamin C Meyer * * * 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 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 #include #include +#include #include #include #include +#include +#include #include "SystemSettingsApp.h" #include "SettingsBase.h" +KService::List m_modules; + +static bool caseInsensitiveLessThan(const KService::Ptr s1, const KService::Ptr s2) +{ + const int compare = QString::compare(s1->desktopEntryName(), + s2->desktopEntryName(), + Qt::CaseInsensitive); + return (compare < 0); +} + +static void listModules() +{ + // First condition is what systemsettings does, second what kinfocenter does, make sure this is kept in sync + // We need the exist calls because otherwise the trader language aborts if the property doesn't exist and the second part of the or is not evaluated + const KService::List services = KServiceTypeTrader::self()->query( QStringLiteral("KCModule"), QStringLiteral("(exist [X-KDE-System-Settings-Parent-Category] and [X-KDE-System-Settings-Parent-Category] != '') or (exist [X-KDE-ParentApp] and [X-KDE-ParentApp] == 'kinfocenter')") ); + for( KService::List::const_iterator it = services.constBegin(); it != services.constEnd(); ++it) { + const KService::Ptr s = (*it); + if (!KAuthorized::authorizeControlModule(s->menuId())) + continue; + m_modules.append(s); + } + + std::stable_sort(m_modules.begin(), m_modules.end(), caseInsensitiveLessThan); +} + int main( int argc, char *argv[] ) { // Make sure the binary name is either kinfocenter or systemsettings, // Anything else will just be considered as "systemsettings" const QString executableName = QString::fromUtf8(argv[0]); QString binaryName = QStringLiteral("systemsettings"); BaseMode::ApplicationMode mode = BaseMode::SystemSettings; if (executableName.endsWith(QLatin1String("kinfocenter"))) { binaryName = QStringLiteral("kinfocenter"); mode = BaseMode::InfoCenter; } //exec is systemsettings5, but we need the QPT to use the right config from the qApp constructor //which is before KAboutData::setApplicationData QCoreApplication::setApplicationName(binaryName); KWorkSpace::detectPlatform(argc, argv); SystemSettingsApp application(argc, argv); KQuickAddons::QtQuickSettings::init(); KCrash::initialize(); KLocalizedString::setApplicationDomain(binaryName.toUtf8().constData()); KAboutData aboutData; if (mode == BaseMode::InfoCenter) { // About data aboutData = KAboutData(QStringLiteral("kinfocenter"), i18n("Info Center"), QLatin1String(PROJECT_VERSION), i18n("Centralized and convenient overview of system information."), KAboutLicense::GPL, i18n("(c) 2009, Ben Cooksley")); aboutData.addAuthor(i18n("Ben Cooksley"), i18n("Maintainer"), QStringLiteral("bcooksley@kde.org")); aboutData.addAuthor(i18n("Mathias Soeken"), i18n("Developer"), QStringLiteral("msoeken@informatik.uni-bremen.de")); aboutData.addAuthor(i18n("Will Stephenson"), i18n("Internal module representation, internal module model"), QStringLiteral("wstephenson@kde.org")); } else { aboutData = KAboutData(QStringLiteral("systemsettings"), i18n("System Settings"), QLatin1String(PROJECT_VERSION), i18n("Central configuration center by KDE."), KAboutLicense::GPL, i18n("(c) 2009, Ben Cooksley")); aboutData.addAuthor(i18n("Ben Cooksley"), i18n("Maintainer"), QStringLiteral("bcooksley@kde.org")); aboutData.addAuthor(i18n("Mathias Soeken"), i18n("Developer"), QStringLiteral("msoeken@informatik.uni-bremen.de")); aboutData.addAuthor(i18n("Will Stephenson"), i18n("Internal module representation, internal module model"), QStringLiteral("wstephenson@kde.org")); } application.setAttribute(Qt::AA_UseHighDpiPixmaps, true); QCommandLineParser parser; + parser.addOption(QCommandLineOption(QStringLiteral("list"), i18n("List all possible modules"))); + parser.addPositionalArgument(QStringLiteral("module"), i18n("Configuration module to open")); + parser.addOption(QCommandLineOption(QStringLiteral("args"), i18n("Arguments for the module"), QLatin1String("arguments"))); + aboutData.setupCommandLine(&parser); parser.process(application); aboutData.processCommandLine(&parser); + if (parser.isSet(QStringLiteral("list"))) { + std::cout << i18n("The following modules are available:").toLocal8Bit().data() << std::endl; + + listModules(); + + int maxLen=0; + + for (KService::List::ConstIterator it = m_modules.constBegin(); it != m_modules.constEnd(); ++it) { + int len = (*it)->desktopEntryName().length(); + if (len > maxLen) + maxLen = len; + } + + for (KService::List::ConstIterator it = m_modules.constBegin(); it != m_modules.constEnd(); ++it) { + QString entry(QStringLiteral("%1 - %2")); + + entry = entry.arg((*it)->desktopEntryName().leftJustified(maxLen, QLatin1Char(' '))) + .arg(!(*it)->comment().isEmpty() ? (*it)->comment() + : i18n("No description available")); + + std::cout << entry.toLocal8Bit().data() << std::endl; + } + return 0; + } + if (mode == BaseMode::InfoCenter) { aboutData.setDesktopFileName(QStringLiteral("org.kde.kinfocenter")); application.setWindowIcon(QIcon::fromTheme(QStringLiteral("hwinfo"))); } else { application.setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-system"))); if (qEnvironmentVariableIsSet("KDE_FULL_SESSION")) { aboutData.setDesktopFileName(QStringLiteral("systemsettings")); } else { aboutData.setDesktopFileName(QStringLiteral("kdesystemsettings")); } } KAboutData::setApplicationData(aboutData); SettingsBase *mainWindow = new SettingsBase(mode); application.setMainWindow(mainWindow); + + if (parser.positionalArguments().count() == 1) { + QStringList moduleArgs; + const QString x = parser.value(QStringLiteral("args")); + moduleArgs << x.split(QRegExp(QStringLiteral(" +"))); + + mainWindow->setStartupModule(parser.positionalArguments().first()); + mainWindow->setStartupModuleArgs(moduleArgs); + } + return application.exec(); } diff --git a/core/BaseMode.cpp b/core/BaseMode.cpp index 2b93ae10..b2832309 100644 --- a/core/BaseMode.cpp +++ b/core/BaseMode.cpp @@ -1,164 +1,192 @@ /*************************************************************************** * Copyright (C) 2009 Ben Cooksley * * * * 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 "BaseMode.h" #include #include #include #include #include #include "MenuItem.h" #include "BaseData.h" #include "ModuleView.h" class BaseMode::Private { public: Private() {} QList actionsList; KService::Ptr service; MenuItem *rootItem = nullptr; MenuItem *homeItem = nullptr; + QString startupModule; + QStringList startupModuleArgs; KConfigGroup config; bool showToolTips = true; BaseMode::ApplicationMode applicationMode = BaseMode::SystemSettings; }; BaseMode::BaseMode( QObject* parent, const QVariantList &args ) : QObject( parent ) , d( new Private() ) { - if (!args.isEmpty() && args.first().canConvert()) { + if (args.count() >= 1 && args.first().canConvert()) { d->applicationMode = args.first().value(); } + if (args.count() >= 2 && args[1].canConvert()) { + d->startupModule = args[1].toString(); + } + if (args.count() >= 3 && args[2].canConvert()) { + d->startupModuleArgs = args[2].toStringList(); + } } BaseMode::~BaseMode() { delete d; } void BaseMode::init( const KService::Ptr &modeService ) { d->rootItem = BaseData::instance()->menuItem(); d->homeItem = BaseData::instance()->homeItem(); d->service = modeService; d->config = BaseData::instance()->configGroup( modeService->library() ); initEvent(); connect( moduleView(), &ModuleView::moduleChanged, this, &BaseMode::viewChanged ); } void BaseMode::initEvent() { } QWidget * BaseMode::mainWidget() { return nullptr; } KAboutData * BaseMode::aboutData() { return nullptr; } BaseMode::ApplicationMode BaseMode::applicationMode() const { return d->applicationMode; } ModuleView * BaseMode::moduleView() const { return nullptr; } QList& BaseMode::actionsList() const { return d->actionsList; } const KService::Ptr& BaseMode::service() const { return d->service; } void BaseMode::setShowToolTips( bool show) { d->showToolTips = show; } bool BaseMode::showToolTips() const { return d->showToolTips; } +void BaseMode::setStartupModule(const QString &startupModule) +{ + d->startupModule = startupModule; +} + +QString BaseMode::startupModule() const +{ + return d->startupModule; +} + +void BaseMode::setStartupModuleArgs(const QStringList &startupModuleArgs) +{ + d->startupModuleArgs = startupModuleArgs; +} + +QStringList BaseMode::startupModuleArgs() const +{ + return d->startupModuleArgs; +} + void BaseMode::searchChanged( const QString& text ) { Q_UNUSED( text ); } void BaseMode::saveState() { } void BaseMode::leaveModuleView() { } void BaseMode::giveFocus() { } void BaseMode::addConfiguration( KConfigDialog * config ) { Q_UNUSED( config ); } void BaseMode::loadConfiguration() { } void BaseMode::saveConfiguration() { } MenuItem * BaseMode::rootItem() const { return d->rootItem; } MenuItem * BaseMode::homeItem() const { return d->homeItem; } KConfigGroup& BaseMode::config() const { return d->config; } QList BaseMode::views() const { return QList(); } diff --git a/core/BaseMode.h b/core/BaseMode.h index c1c29432..17e16d23 100644 --- a/core/BaseMode.h +++ b/core/BaseMode.h @@ -1,279 +1,287 @@ /***************************************************************************** * Copyright (C) 2009 Ben Cooksley * * * * 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 BASEMODE_H #define BASEMODE_H #include #include "systemsettingsview_export.h" #include class QAction; class MenuItem; class ModuleView; class KAboutData; class KConfigDialog; class KConfigGroup; class QAbstractItemView; template class QList; /** * @brief Provides a interface for System Settings views * * BaseMode is a standard interface for all plugins to System Settings to allow them to provide * their own interface to KDE control modules.\n * * The developer need only ensure that they perform all initialization of their plugin in * initEvent() to ensure that the plugin is displayed, and initial actions are loaded. * * @author Ben Cooksley * @author Mathias Soeken */ class SYSTEMSETTINGSVIEW_EXPORT BaseMode : public QObject { Q_OBJECT /** * System Settings main application is allowed priviledged access to handle tooltips */ friend class SettingsBase; public: // Main mode of the app. // At the moment SystemSettings and InfoCenter are supported: // Changes mainly the set of module listed on the left menu enum ApplicationMode { SystemSettings = 0, InfoCenter }; Q_ENUM(ApplicationMode); /** * Constructs a BaseMode for use in System Settings.\n * Plugin developers should perform all initialisation in initEvent() not here. * * @param parent The parent of this BaseMode. */ explicit BaseMode( QObject * parent, const QVariantList &args ); /** * Normal destructor. Plugin developers only need destroy what they created * not what is provided by BaseMode itself. */ ~BaseMode() override; /** * These flags are used to control the presence of the Search and Configure actions on the toolbar */ enum ToolBarItemsFlags { NoItems = 0x1, /**< The Toolbar will not have any items added by System Settings */ Search = 0x2, /**< The Toolbar will have the search bar added by System Settings */ Configure = 0x4, /**< The Toolbar will have configure added by System Settings */ Quit = 0x8 /**< The toolbar will have exit added by System Settings */ }; Q_DECLARE_FLAGS(ToolBarItems, ToolBarItemsFlags) /** * Performs internal setup.\n * Plugin developers should perform initialisation in initEvent() not here. * * @param modeService Plugins service object, used for providing extra information to System Settings. */ void init( const KService::Ptr &modeService ); /** * Prepares the BaseMode for use.\n * Plugin developers should perform initialisation here, creating the Models. They should perform widget * initialisation the first time mainWidget() is called, not here. */ virtual void initEvent(); /** * Returns the widget to be displayed in the center of System Settings.\n * The widget should be created the first time this function is called. * * @warning This function is called multiple times, ensure the widget is only created once. * @returns The main widget of the plugin. */ virtual QWidget * mainWidget(); /** * Provides information about the plugin, which is used in the About dialog of System Settings.\n * This does not need to be implemented, and need only be implemented if the author * wants information about the view displayed in the About dialog. * * @returns The about data of the plugin. */ virtual KAboutData * aboutData(); /** * @returns the application mode of this systemsettings process: SystemSettings or InfoCenter */ ApplicationMode applicationMode() const; /** * The state of the plugin ( position of the splitter for instance ) should be saved * to the configuration object when this is called. */ virtual void saveState(); /** * Causes the view to unload all modules in the module view, and return to their module selection state * * @warning Failure to reimplement will cause modules to not be unloaded when changing views. * This must be implemented. */ virtual void leaveModuleView(); /** * Used to give focus to the plugin. Plugin should call setFocus() on the appropriate widget * * @note Failure to reimplement will cause keyboard accessibility and widget focusing problems */ virtual void giveFocus(); /** * Provides access to the ModuleView the application uses to display control modules.\n * * @warning Failure to reimplement will cause modules not to be checked for configuration * changes, and for the module to not be displayed in the About dialog. It must be implemented. * @returns The ModuleView used by the plugin for handling modules. */ virtual ModuleView * moduleView() const; /** * Provides the list of actions the plugin wants System Settings to display in the toolbar when * it is loaded. This function does not need to be implemented if adding actions to the toolbar * is not required. * * @returns The list of actions the plugin provides. */ virtual QList& actionsList() const; /** * Provides the service object, which is used to retrieve information for the configuration dialog. * * @returns the service object of the plugin. */ const KService::Ptr& service() const; /** * tells the config view whether to make use of tooltips or not */ void setShowToolTips( bool show); /** * @returns true if the view should use tooltips */ bool showToolTips() const; + void setStartupModule(const QString &startupModule); + QString startupModule() const; + + void setStartupModuleArgs(const QStringList &startupModuleArgs); + QStringList startupModuleArgs() const; + + virtual void reloadStartupModule() = 0; + public Q_SLOTS: /** * Called when the text in the search box changes allowing the display to be filtered. * * @warning Search will not work in the view if this function is not implemented. */ virtual void searchChanged( const QString& text ); /** * Allows views to add custom configuration pages to the System Settings configure dialog * * @warning Deleting the config object will cause System Settings to crash */ virtual void addConfiguration( KConfigDialog * config ); /** * Allows views to load their configuration before the configuration dialog is shown * Views should revert any changes that have not been saved */ virtual void loadConfiguration(); /** * Should be implemented to ensure that views settings are saved when the user confirms their changes * Views should also apply the configuration at the same time */ virtual void saveConfiguration(); Q_SIGNALS: /** * Triggers a reload of the views actions by the host application. * * @warning Actions previously contained in the list must not be destroyed before this has been emitted. */ void actionsChanged(); /** * Should be emitted when the type ( list of modules / display of module ) * of displayed view is changed. * * @param state Determines whether changes have been made in the view. * @warning Failure to emit this will result in inconsistent application headers and change state. */ void viewChanged( bool state ); /** * Causes System Settings to hide / show the toolbar items specified. * This is used to control the display of the Configure and Search actions * * @param items The items that are wanted in the toolbar */ void changeToolBarItems( BaseMode::ToolBarItems items ); protected: /** * Returns the root item of the list of categorised modules. * This is usually passed to the constructor of MenuModel. * * @warning This is shared between all views, and should not be deleted manually. * @returns The root menu item as provided by System Settings. */ MenuItem * rootItem() const; /** * Returns (if present) an item that corresponds to a KCM which should be used as startup page. * * @warning This is shared between all views, and should not be deleted manually. * @returns The item to load as startup page. It may be nullptr */ MenuItem * homeItem() const; /** * Provides access to the configuration for the plugin. * * @returns The configuration group for the plugin. */ KConfigGroup& config() const; /** * Provides access to item views used by the plugin. * This is currently used to show the enhanced tooltips. * * @returns A list of pointers to item views used by the mode. * The list can be empty. */ virtual QList views() const; private: class Private; Private *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(BaseMode::ToolBarItems) #endif diff --git a/core/MenuItem.cpp b/core/MenuItem.cpp index c6e7c001..b2954cba 100644 --- a/core/MenuItem.cpp +++ b/core/MenuItem.cpp @@ -1,141 +1,158 @@ /* This file is part of the KDE project Copyright 2007 Will Stephenson Copyright (C) 2009 Ben Cooksley 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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, see . */ #include "MenuItem.h" #include #include #include static bool childIsLessThan( MenuItem *left, MenuItem *right ) { return left->weight() < right->weight(); } class MenuItem::Private { public: Private() {} MenuItem *parent; QList children; bool menu; QString name; QString category; int weight; KService::Ptr service; KCModuleInfo item; }; MenuItem::MenuItem( bool isMenu, MenuItem * itsParent ) : d( new Private() ) { d->parent = itsParent; d->menu = isMenu; if ( d->parent ) { d->parent->children().append( this ); } } MenuItem::~MenuItem() { qDeleteAll( d->children ); delete d; } void MenuItem::sortChildrenByWeight() { std::sort( d->children.begin(), d->children.end(), childIsLessThan ); } MenuItem * MenuItem::child( int index ) { return d->children.at(index); } QStringList MenuItem::keywords() { QStringList listOfKeywords; listOfKeywords << d->item.keywords() << d->name; foreach ( MenuItem * child, d->children ) { listOfKeywords += child->keywords(); } return listOfKeywords; } MenuItem* MenuItem::parent() const { return d->parent; } QList& MenuItem::children() const { return d->children; } KService::Ptr& MenuItem::service() const { return d->service; } KCModuleInfo& MenuItem::item() const { return d->item; } QString& MenuItem::name() const { return d->name; } QString& MenuItem::category() const { return d->category; } int MenuItem::weight() { return d->weight; } bool MenuItem::menu() const { return d->menu; } void MenuItem::setService( const KService::Ptr& service ) { d->service = service; d->category = service->property(QStringLiteral("X-KDE-System-Settings-Category")).toString(); if (d->category.isEmpty()) { d->category = service->property(QStringLiteral("X-KDE-KInfoCenter-Category")).toString(); } d->name = service->name(); d->item = KCModuleInfo( service ); const QVariant itemWeight = service->property(QStringLiteral("X-KDE-Weight"), QVariant::Int ); if( itemWeight.isValid() ) { d->weight = itemWeight.toInt(); } else { d->weight = 100; } } + +MenuItem *MenuItem::descendantForModule(const QString &moduleName) +{ + if (d->service && d->service->desktopEntryName() == moduleName) { + return this; + } + + for (auto *child : d->children) { + MenuItem *candidate = child->descendantForModule(moduleName); + if (candidate) { + return candidate; + } + } + + return nullptr; +} + diff --git a/core/MenuItem.h b/core/MenuItem.h index 9f98ff0f..937c3781 100644 --- a/core/MenuItem.h +++ b/core/MenuItem.h @@ -1,165 +1,167 @@ /*************************************************************************** * This file is part of the KDE project * * Copyright 2007 Will Stephenson * * Copyright (C) 2009 Ben Cooksley * * * * 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 MENUITEM_H #define MENUITEM_H #include "systemsettingsview_export.h" #include class QString; class KCModuleInfo; template class QList; /** * @brief Provides a specific item in the list of modules or categories * * This provides convenient access to the list of modules, providing information about them * such as name, module information and its service object.\n * This is created automatically by System Settings, and is shared among all plugins and so should not * be modified under any circumstances.\n * * System Settings creates it in a tree like manner, with categories containing subcategories and modules, * and subcategories repeating this.\n * * The service object must be set, unless it is the top level item, otherwise using applications * will crash when attempting to sort the children by weight * * @author Ben Cooksley * @author Will Stephenson */ class SYSTEMSETTINGSVIEW_EXPORT MenuItem { public: /** * Creates a MenuItem. * @note Will not provide keywords, name, or a module item until a service has been set. * * @param isMenu Specifies if it is a category or not. * @param parent The item it is parented to. Provide 0 for a top level item. */ MenuItem( bool isMenu, MenuItem * parent ); /** * Destroys a MenuItem, including all children, the service object and the module information. * * @warning Destroys the KService and KCModuleInfo objects provided by service() and item(). */ ~MenuItem(); /** * Sorts the children depending on the value of "X-KDE-Weight" in the desktop files of the * category or module. */ void sortChildrenByWeight(); /** * Provides the MenuItem for the child at the specified index. * * @param index The index of the child. * @returns The MenuItem object of the specified child. */ MenuItem * child( int index ); /** * Returns the list of keywords, which is used for searching the list of categories and modules. * * @note The parent items share all the keywords of their children. * @returns The list of keywords the item has. */ QStringList keywords(); /** * Returns the parent of this item. * * @returns The MenuItem object of this items parent. */ MenuItem *parent() const; /** * Provides a list of all the children of this item. * * @returns The list of children this has. */ QList& children() const; /** * Returns the service object of this item, which contains useful information about it. * * @returns The service object of this item if it has been set. */ KService::Ptr& service() const; /** * Provides the KDE control module information item, which can be used to load control modules * by the ModuleView. * * @returns The control module information object of the item, if the service object has been set. */ KCModuleInfo& item() const; /** * Convenience function which provides the name of the current item. * * @returns The name of the item, if the service object has been set. */ QString& name() const; /** * Convenience function which provides the System Settings category of the current item. * * @returns The category of the item, if the service object has been set. */ QString& category() const; /** * Provides the weight of the current item, as determined by its service. * If the service does not specify a weight, it is 100 * * @returns The weight of the service */ int weight(); /** * Provides information on which type the current item is. * * @returns true if it is a category. * @returns false if it is not a category. */ bool menu() const; /** * Sets the service object, which is used to provide the module information, name and keywords * Applications will crash if it is not set, unless it is the top level item. * * @param service The service object to store. */ void setService( const KService::Ptr& service ); + MenuItem *descendantForModule(const QString &moduleName); + private: class Private; Private *const d; }; Q_DECLARE_METATYPE( MenuItem * ) #endif diff --git a/core/ModuleView.cpp b/core/ModuleView.cpp index 9a2bd91b..c5929162 100644 --- a/core/ModuleView.cpp +++ b/core/ModuleView.cpp @@ -1,478 +1,478 @@ /***************************************************************************** * Copyright (C) 2009 Ben Cooksley * * Copyright (C) 2009 by Mathias Soeken * * * * 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 "ModuleView.h" #include "ExternalAppModule.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MenuItem.h" class ModuleView::Private { public: Private() {} QMap mPages; QMap mModules; KPageWidget* mPageWidget = nullptr; QVBoxLayout* mLayout = nullptr; QDialogButtonBox* mButtons = nullptr; KAuth::ObjectDecorator* mApplyAuthorize = nullptr; QPushButton* mApply = nullptr; QPushButton* mReset = nullptr; QPushButton* mDefault = nullptr; QPushButton* mHelp = nullptr; bool pageChangeSupressed = false; bool mSaveStatistics = true; }; ModuleView::ModuleView( QWidget * parent ) : QWidget( parent ) , d( new Private() ) { // Configure a layout first d->mLayout = new QVBoxLayout(this); // Create the Page Widget d->mPageWidget = new KPageWidget(this); d->mPageWidget->layout()->setContentsMargins(0, 0, 0, 0); d->mLayout->addWidget(d->mPageWidget); // Create the dialog d->mButtons = new QDialogButtonBox( Qt::Horizontal, this ); d->mLayout->addWidget(d->mButtons); // Create the buttons in it d->mApply = d->mButtons->addButton( QDialogButtonBox::Apply ); KGuiItem::assign(d->mApply, KStandardGuiItem::apply()); d->mDefault = d->mButtons->addButton( QDialogButtonBox::RestoreDefaults ); KGuiItem::assign(d->mDefault, KStandardGuiItem::defaults()); d->mReset = d->mButtons->addButton( QDialogButtonBox::Reset ); KGuiItem::assign(d->mReset, KStandardGuiItem::reset()); d->mHelp = d->mButtons->addButton( QDialogButtonBox::Help ); KGuiItem::assign(d->mHelp, KStandardGuiItem::help()); // Set some more sensible tooltips d->mReset->setToolTip( i18n("Reset all current changes to previous values") ); // Set Auto-Default mode ( KDE Bug #211187 ) d->mApply->setAutoDefault(true); d->mDefault->setAutoDefault(true); d->mReset->setAutoDefault(true); d->mHelp->setAutoDefault(true); // Prevent the buttons from being used d->mApply->setEnabled(false); d->mDefault->setEnabled(false); d->mReset->setEnabled(false); d->mHelp->setEnabled(false); // Connect up the buttons connect( d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()) ); connect( d->mReset, &QAbstractButton::clicked, this, &ModuleView::moduleLoad ); connect( d->mHelp, &QAbstractButton::clicked, this, &ModuleView::moduleHelp ); connect( d->mDefault, &QAbstractButton::clicked, this, &ModuleView::moduleDefaults ); connect( d->mPageWidget, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(activeModuleChanged(KPageWidgetItem*,KPageWidgetItem*)) ); d->mApplyAuthorize = new KAuth::ObjectDecorator(d->mApply); d->mApplyAuthorize->setAuthAction( KAuth::Action() ); } ModuleView::~ModuleView() { delete d; } KCModuleInfo * ModuleView::activeModule() const { return d->mModules.value( d->mPageWidget->currentPage() ); } const KAboutData * ModuleView::aboutData() const { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); KAboutData * aboutData = nullptr; if( activeModule ) { aboutData = const_cast( activeModule->aboutData() ); } return aboutData; } -void ModuleView::loadModule( const QModelIndex &menuItem ) +void ModuleView::loadModule( const QModelIndex &menuItem, const QStringList &args ) { if ( !menuItem.isValid() ) { return; } QList indexes; for ( int done = 0; menuItem.model()->rowCount( menuItem ) > done; done = 1 + done ) { indexes << menuItem.model()->index( done, 0, menuItem ); } if ( indexes.empty() ) { indexes << menuItem; } foreach ( const QModelIndex &module, indexes ) { MenuItem *newMenuItem = module.data( Qt::UserRole ).value(); - addModule( &newMenuItem->item() ); + addModule( &newMenuItem->item(), args ); } // changing state is not needed here as the adding / changing of pages does it } -void ModuleView::addModule( KCModuleInfo *module ) +void ModuleView::addModule( KCModuleInfo *module, const QStringList &args ) { if( !module ) { return; } if( !module->service() ) { qWarning() << "ModuleInfo has no associated KService" ; return; } if ( !KAuthorized::authorizeControlModule( module->service()->menuId() ) ) { qWarning() << "Not authorised to load module" ; return; } if( module->service()->noDisplay() ) { return; } // Create the scroller QScrollArea * moduleScroll = new QScrollArea( this ); // Prepare the scroll area moduleScroll->setWidgetResizable( true ); moduleScroll->setFrameStyle( QFrame::NoFrame ); moduleScroll->viewport()->setAutoFillBackground( false ); // Create the page KPageWidgetItem *page = new KPageWidgetItem( moduleScroll, module->moduleName() ); // Provide information to the users if( module->service()->hasServiceType(QStringLiteral("SystemSettingsExternalApp")) || // Is it an external app? module->service()->substituteUid() ) { // ...or does it require UID substitution? QWidget * externalWidget = new ExternalAppModule( this, module ); moduleScroll->setWidget( externalWidget ); } else { // It must be a normal module then - KCModuleProxy * moduleProxy = new KCModuleProxy( *module, moduleScroll ); + KCModuleProxy * moduleProxy = new KCModuleProxy( *module, moduleScroll, args ); moduleScroll->setWidget( moduleProxy ); moduleProxy->setAutoFillBackground( false ); connect( moduleProxy, SIGNAL(changed(bool)), this, SLOT(stateChanged())); d->mPages.insert( page, moduleProxy ); } d->mModules.insert( page, module ); updatePageIconHeader( page, true ); // Add the new page d->mPageWidget->addPage( page ); } void ModuleView::updatePageIconHeader( KPageWidgetItem * page, bool light ) { if( !page ) { // Page is invalid. Probably means we have a race condition during closure of everyone so do nothing return; } KCModuleProxy * moduleProxy = d->mPages.value( page ); KCModuleInfo * moduleInfo = d->mModules.value( page ); if( !moduleInfo ) { // Seems like we have some form of a race condition going on here... return; } page->setHeader( moduleInfo->moduleName() ); page->setIcon( QIcon::fromTheme( moduleInfo->icon() ) ); //HACK: not much other ways to detect is a qml kcm if ( moduleProxy && moduleProxy->realModule()->inherits("KCModuleQml") ) { page->setHeaderVisible(false); } if( light ) { return; } if( moduleProxy && moduleProxy->realModule()->useRootOnlyMessage() ) { page->setHeader( moduleInfo->moduleName() + QStringLiteral("
") + moduleProxy->realModule()->rootOnlyMessage() + QStringLiteral("") ); } } bool ModuleView::resolveChanges() { KCModuleProxy * currentProxy = d->mPages.value( d->mPageWidget->currentPage() ); return resolveChanges(currentProxy); } bool ModuleView::resolveChanges(KCModuleProxy * currentProxy) { if( !currentProxy || !currentProxy->changed() ) { return true; } // Let the user decide const int queryUser = KMessageBox::warningYesNoCancel( this, i18n("The settings of the current module have changed.\n" "Do you want to apply the changes or discard them?"), i18n("Apply Settings"), KStandardGuiItem::apply(), KStandardGuiItem::discard(), KStandardGuiItem::cancel() ); switch (queryUser) { case KMessageBox::Yes: return moduleSave(currentProxy); case KMessageBox::No: currentProxy->load(); return true; case KMessageBox::Cancel: return false; default: Q_ASSERT(false); return false; } } void ModuleView::closeModules() { d->pageChangeSupressed = true; d->mApplyAuthorize->setAuthAction( KAuth::Action() ); // Ensure KAuth knows that authentication is now pointless... QMap::iterator page = d->mModules.begin(); QMap::iterator pageEnd = d->mModules.end(); for ( ; page != pageEnd; ++page ) { d->mPageWidget->removePage( page.key() ); } d->mPages.clear(); d->mModules.clear(); d->pageChangeSupressed = false; } bool ModuleView::moduleSave() { KCModuleProxy * moduleProxy = d->mPages.value( d->mPageWidget->currentPage() ); return moduleSave( moduleProxy ); } bool ModuleView::moduleSave(KCModuleProxy *module) { if( !module ) { return false; } module->save(); return true; } void ModuleView::moduleLoad() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); if( activeModule ) { activeModule->load(); } } void ModuleView::moduleDefaults() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); if( activeModule ) { activeModule->defaults(); } } void ModuleView::moduleHelp() { KCModuleInfo * activeModule = d->mModules.value( d->mPageWidget->currentPage() ); if( !activeModule ) { return; } QString docPath = activeModule->docPath(); if( docPath.isEmpty() ) { return; } // UrlHandler from KGUIAddons sets a handler for help:/ urls, which opens khelpcenter // if it's available or falls back to opening the relevant page at docs.kde.org QDesktopServices::openUrl(QUrl(QStringLiteral("help:/") + docPath)); } void ModuleView::activeModuleChanged(KPageWidgetItem * current, KPageWidgetItem * previous) { d->mPageWidget->blockSignals(true); d->mPageWidget->setCurrentPage(previous); KCModuleProxy * previousModule = d->mPages.value(previous); if( resolveChanges(previousModule) ) { d->mPageWidget->setCurrentPage(current); } d->mPageWidget->blockSignals(false); if( d->pageChangeSupressed ) { return; } // We need to get the state of the now active module stateChanged(); KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); if (activeModule) { // TODO: if we'll ever need statistics for kinfocenter modules, save them with an URL like "kinfo:" if (d->mSaveStatistics) { KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("kcm:") + activeModule->moduleInfo().service()->storageId()), QStringLiteral("org.kde.systemsettings")); } if (activeModule->realModule() && activeModule->realModule()->inherits("KCModuleQml")) { d->mButtons->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin), style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); d->mLayout->setContentsMargins(0, 0, 0, 0); } else { d->mButtons->setContentsMargins(0, 0, 0, 0); d->mLayout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin), style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); } } } void ModuleView::stateChanged() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); KAuth::Action moduleAction; bool change = false; bool defaulted = false; KCModule::Buttons buttons = KCModule::NoAdditionalButton; if( activeModule ) { buttons = activeModule->buttons(); change = activeModule->changed(); defaulted = activeModule->defaulted(); disconnect( d->mApplyAuthorize, SIGNAL(authorized(KAuth::Action)), this, SLOT(moduleSave()) ); disconnect( d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()) ); if( activeModule->realModule()->authAction().isValid() ) { connect( d->mApplyAuthorize, SIGNAL(authorized(KAuth::Action)), this, SLOT(moduleSave()) ); moduleAction = activeModule->realModule()->authAction(); } else { connect( d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()) ); } } updatePageIconHeader( d->mPageWidget->currentPage() ); d->mApplyAuthorize->setAuthAction( moduleAction ); d->mDefault->setEnabled(!defaulted); d->mDefault->setVisible(buttons & KCModule::Default); d->mApply->setEnabled(change); d->mApply->setVisible(buttons & KCModule::Apply); d->mReset->setEnabled(change); d->mReset->setVisible(buttons & KCModule::Apply); d->mHelp->setEnabled(buttons & KCModule::Help ); d->mHelp->setVisible(buttons & KCModule::Help ); emit moduleChanged( change ); } void ModuleView::keyPressEvent ( QKeyEvent * event ) { if ( event->key() == Qt::Key_F1 && d->mHelp->isVisible() && d->mHelp->isEnabled()) { d->mHelp->animateClick(); event->accept(); return; } else if ( event->key() == Qt::Key_Escape ) { event->accept(); emit closeRequest(); return; } else if ( event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier ) { QWhatsThis::enterWhatsThisMode(); event->accept(); return; } QWidget::keyPressEvent( event ); } void ModuleView::setFaceType(KPageView::FaceType type) { d->mPageWidget->setFaceType(type); } KPageView::FaceType ModuleView::faceType() const { return d->mPageWidget->faceType(); } void ModuleView::setSaveStatistics(bool save) { d->mSaveStatistics = save; } bool ModuleView::saveStatistics() const { return d->mSaveStatistics; } void ModuleView::setApplyVisible(bool visible) { d->mApply->setVisible(visible); } bool ModuleView::isApplyVisible() const { return d->mApply->isVisible(); } void ModuleView::setDefaultsVisible(bool visible) { d->mDefault->setVisible(visible); } bool ModuleView::isDefaultsVisible() const { return d->mDefault->isVisible(); } void ModuleView::setResetVisible(bool visible) { d->mReset->setVisible(visible); } bool ModuleView::isResetVisible() const { return d->mReset->isVisible(); } diff --git a/core/ModuleView.h b/core/ModuleView.h index e66f233f..fc4d104d 100644 --- a/core/ModuleView.h +++ b/core/ModuleView.h @@ -1,201 +1,201 @@ /***************************************************************************** * Copyright (C) 2009 Ben Cooksley * * Copyright (C) 2009 by Mathias Soeken * * * * 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 MODULE_VIEW_H #define MODULE_VIEW_H #include #include #include #include "systemsettingsview_export.h" class KAboutData; class KCModuleInfo; class KCModuleProxy; class KPageWidgetItem; /** * @brief Provides a convenient way to display modules * * Provides a standardised interface for displaying multiple modules simultaneously * and provides facilities to access the current module, and to load its help, restore * default settings, save new settings and revert changes to settings * * It also provides checking for when a module has changed its configuration, and will prompt * if the user tries to change module or view if BaseMode is reimplemented correctly * * It also provides signals for active module changes, configuration changes and for when it has * been requested to close by button press * * @author Mathias Soeken * @author Ben Cooksley */ class SYSTEMSETTINGSVIEW_EXPORT ModuleView : public QWidget { Q_OBJECT public: /** * Constructs a ModuleView, with the parent specified. */ explicit ModuleView(QWidget * parent = nullptr); /** * Destroys the module view, along with all modules loaded, and any changes present in them. * * @warning The user will not be prompted to save changes if any exist. */ ~ModuleView() override; /** * Provides the module information, which is used to set the caption of the window when either the * active module or configuration changes. */ KCModuleInfo * activeModule() const; /** * Provides the about data of the active module, used for the about dialog. */ const KAboutData * aboutData() const; /** * Resolves any changes in the currently active module by prompting the user if they exist. * * @returns true if the user saved or discarded changes, or there were no changes at all. * @returns false if the user canceled the module change. */ bool resolveChanges(); /** * Closes all active modules, after checking there are no active changes. * * @warning This forces all modules to be destroyed regardless of if changes exist or not * If possible, always check with resolveChanges() first. */ void closeModules(); void setFaceType(KPageView::FaceType type); KPageView::FaceType faceType() const; /** * Sets whether Systemsettings should save statisctics about * most used modules using KActivities::Stats */ void setSaveStatistics(bool save); /** * @returns whether Systemsettings should save statisctics about * most used module */ bool saveStatistics() const; /** * Shows or hides the Apply button. */ void setApplyVisible(bool visible); /** * @returns True if the Apply button is visible. */ bool isApplyVisible() const; /** * Shows or hides the Defaults button. */ void setDefaultsVisible(bool visible); /** * @returns True if the Defaults button is visible. */ bool isDefaultsVisible() const; /** * Shows or hides the Reset button. */ void setResetVisible(bool visible); /** * @returns True if the Reset button is visible. */ bool isResetVisible() const; public Q_SLOTS: /** * Loads the module specified by menuItem.\n * If the module has children, they will all be loaded instead of the module. * * @param menuItem the QModelIndex that you want to load. Must be sourced from either MenuModel or MenuProxyModel */ - void loadModule(const QModelIndex &menuItem ); + void loadModule(const QModelIndex &menuItem, const QStringList &args ); /** * Will open KHelpCenter, and load the help for the active module. */ void moduleHelp(); /** * Causes the active module to reload its configuration, reverting all changes. */ void moduleLoad(); /** * Causes the active module to save its configuration, applying all changes. */ bool moduleSave(); /** * Causes the active module to revert all changes to the configuration, and return to defaults. */ void moduleDefaults(); /** * Reimplemented for internal reasons.\n */ void keyPressEvent( QKeyEvent * event ) override; private: bool resolveChanges( KCModuleProxy *currentProxy ); - void addModule( KCModuleInfo *module ); + void addModule( KCModuleInfo *module, const QStringList &args ); bool moduleSave( KCModuleProxy *module ); void updatePageIconHeader( KPageWidgetItem * page, bool light = false ); private Q_SLOTS: void activeModuleChanged( KPageWidgetItem* current, KPageWidgetItem* previous); void stateChanged(); Q_SIGNALS: /** * Emitted when the currently active module is changed. This occurs whenever the active module or * its configuration changes. This causes the window caption to update. */ void moduleChanged( bool state ); /** * Emitted when the ModuleView is asked to close.\n */ void closeRequest(); private: class Private; Private *const d; }; #endif diff --git a/icons/IconMode.cpp b/icons/IconMode.cpp index 3194e79a..078606de 100644 --- a/icons/IconMode.cpp +++ b/icons/IconMode.cpp @@ -1,218 +1,241 @@ /************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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 "IconMode.h" #include "CategoryDrawer.h" #include "CategorizedView.h" #include "MenuItem.h" #include "MenuModel.h" #include "ModuleView.h" #include "MenuProxyModel.h" #include #include #include #include #include #include K_PLUGIN_FACTORY( IconModeFactory, registerPlugin(); ) class IconMode::Private { public: Private() : categoryDrawer( nullptr ), categoryView( nullptr ), moduleView( nullptr ) {} virtual ~Private() { delete aboutIcon; } KCategoryDrawer * categoryDrawer; KCategorizedView * categoryView; QStackedWidget * mainWidget; + MenuModel * model; MenuProxyModel * proxyModel; KAboutData * aboutIcon; ModuleView * moduleView; QAction * backAction; }; IconMode::IconMode( QObject *parent, const QVariantList &args ) : BaseMode( parent, args ) , d( new Private() ) { d->aboutIcon = new KAboutData( QStringLiteral("IconView"), i18n( "Icon View" ), QStringLiteral("1.0"), i18n( "Provides a categorized icons view of control modules." ), KAboutLicense::GPL, i18n( "(c) 2009, Ben Cooksley" ) ); d->aboutIcon->addAuthor( i18n( "Ben Cooksley" ), i18n( "Author" ), QStringLiteral("bcooksley@kde.org") ); d->aboutIcon->addAuthor( i18n( "Mathias Soeken" ), i18n( "Developer" ), QStringLiteral("msoeken@informatik.uni-bremen.de") ); d->backAction = KStandardAction::back( this, SLOT(backToOverview()), this ); d->backAction->setText( i18n( "All Settings" ) ); d->backAction->setToolTip( i18n("Keyboard Shortcut: %1", d->backAction->shortcut().toString( QKeySequence::NativeText )) ); d->backAction->setEnabled( false ); actionsList() << d->backAction; } IconMode::~IconMode() { delete d; } KAboutData * IconMode::aboutData() { return d->aboutIcon; } ModuleView * IconMode::moduleView() const { return d->moduleView; } QWidget * IconMode::mainWidget() { if( !d->categoryView ) { initWidget(); } return d->mainWidget; } QList IconMode::views() const { QList list; list.append( d->categoryView ); return list; } void IconMode::initEvent() { - MenuModel * model = new MenuModel( rootItem(), this ); + d->model = new MenuModel( rootItem(), this ); foreach( MenuItem * child, rootItem()->children() ) { - model->addException( child ); + d->model->addException( child ); } d->proxyModel = new MenuProxyModel( this ); d->proxyModel->setCategorizedModel( true ); - d->proxyModel->setSourceModel( model ); + d->proxyModel->setSourceModel( d->model ); d->proxyModel->sort( 0 ); d->mainWidget = new QStackedWidget(); d->moduleView = new ModuleView( d->mainWidget ); connect( d->moduleView, &ModuleView::moduleChanged, this, &IconMode::moduleLoaded ); connect( d->moduleView, &ModuleView::closeRequest, this, &IconMode::backToOverview ); d->categoryView = nullptr; } void IconMode::searchChanged( const QString& text ) { d->proxyModel->setFilterRegExp( text ); if ( d->categoryView ) { QAbstractItemModel *model = d->categoryView->model(); const int column = d->categoryView->modelColumn(); const QModelIndex root = d->categoryView->rootIndex(); for ( int i = 0; i < model->rowCount(); ++i ) { const QModelIndex index = model->index( i, column, root ); if ( model->flags( index ) & Qt::ItemIsEnabled ) { d->categoryView->scrollTo( index ); break; } } } } void IconMode::changeModule( const QModelIndex& activeModule ) +{ + changeModuleWithArgs(activeModule, QStringList()); +} + +void IconMode::changeModuleWithArgs( const QModelIndex& activeModule, const QStringList &args ) { d->moduleView->closeModules(); d->mainWidget->setCurrentWidget( d->moduleView ); // avoid double titles by setting the right face type before loading the module if ( d->categoryView->model()->rowCount(activeModule) > 1 ) { d->moduleView->setFaceType(KPageView::List); } else { d->moduleView->setFaceType(KPageView::Plain); } - d->moduleView->loadModule( activeModule ); + d->moduleView->loadModule( activeModule, args ); } void IconMode::moduleLoaded() { d->backAction->setEnabled( true ); emit changeToolBarItems(BaseMode::NoItems); } void IconMode::backToOverview() { if( d->moduleView->resolveChanges() ) { d->mainWidget->setCurrentWidget( d->categoryView ); d->moduleView->closeModules(); d->backAction->setEnabled( false ); emit changeToolBarItems( BaseMode::Search | BaseMode::Configure | BaseMode::Quit ); emit viewChanged( false ); } } void IconMode::initWidget() { // Create the widget d->categoryView = new CategorizedView( d->mainWidget ); d->categoryDrawer = new CategoryDrawer(d->categoryView); d->categoryView->setSelectionMode( QAbstractItemView::SingleSelection ); //PORT QT5 d->categoryView->setSpacing( KDialog::spacingHint() ); d->categoryView->setCategoryDrawer( d->categoryDrawer ); d->categoryView->setViewMode( QListView::IconMode ); d->categoryView->setMouseTracking( true ); d->categoryView->viewport()->setAttribute( Qt::WA_Hover ); KFileItemDelegate *delegate = new KFileItemDelegate( d->categoryView ); delegate->setWrapMode( QTextOption::WordWrap ); d->categoryView->setItemDelegate( delegate ); d->categoryView->setFrameShape( QFrame::NoFrame ); d->categoryView->setModel( d->proxyModel ); connect( d->categoryView, &QAbstractItemView::activated, this, &IconMode::changeModule ); d->mainWidget->addWidget( d->categoryView ); d->mainWidget->addWidget( d->moduleView ); d->mainWidget->setCurrentWidget( d->categoryView ); emit changeToolBarItems( BaseMode::Search | BaseMode::Configure | BaseMode::Quit ); d->mainWidget->installEventFilter(this); + + if (!startupModule().isEmpty()) { + MenuItem *item = rootItem()->descendantForModule(startupModule()); + if (item) { + changeModuleWithArgs(d->proxyModel->mapFromSource(d->model->indexForItem(item)), startupModuleArgs()); + } + } +} + +void IconMode::reloadStartupModule() +{ + if (!startupModule().isEmpty()) { + MenuItem *item = rootItem()->descendantForModule(startupModule()); + if (item) { + changeModuleWithArgs(d->proxyModel->mapFromSource(d->model->indexForItem(item)), startupModuleArgs()); + } + } } bool IconMode::eventFilter(QObject* watched, QEvent* event) { if (watched == d->mainWidget && event->type() == QEvent::Show) { emit changeToolBarItems( BaseMode::Search | BaseMode::Configure | BaseMode::Quit ); } return BaseMode::eventFilter(watched, event); } void IconMode::leaveModuleView() { d->moduleView->closeModules(); // We have to force it here backToOverview(); } void IconMode::giveFocus() { d->categoryView->setFocus(); } #include "IconMode.moc" diff --git a/icons/IconMode.h b/icons/IconMode.h index 102bca15..714307b0 100644 --- a/icons/IconMode.h +++ b/icons/IconMode.h @@ -1,62 +1,65 @@ /*************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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 ICONMODE_H #define ICONMODE_H #include "BaseMode.h" class ModuleView; class KAboutData; class QModelIndex; class QAbstractItemView; class IconMode : public BaseMode { Q_OBJECT public: explicit IconMode(QObject * parent, const QVariantList& ); ~IconMode() override; QWidget * mainWidget() override; void initEvent() override; void giveFocus() override; void leaveModuleView() override; KAboutData * aboutData() override; ModuleView * moduleView() const override; + void reloadStartupModule() override; protected: QList views() const override; bool eventFilter(QObject* watched, QEvent* event) override; public Q_SLOTS: void searchChanged( const QString& text ) override; private Q_SLOTS: - void changeModule( const QModelIndex& activeModule ); void moduleLoaded(); void backToOverview(); void initWidget(); private: + void changeModule( const QModelIndex& activeModule); + void changeModuleWithArgs( const QModelIndex& activeModule, const QStringList &args ); + class Private; Private *const d; }; #endif diff --git a/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp index 05f329d5..4ada3924 100644 --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -1,752 +1,769 @@ /************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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 "SidebarMode.h" #include "MenuItem.h" #include "MenuModel.h" #include "ModuleView.h" #include "MenuProxyModel.h" #include "BaseData.h" #include "ToolTips/tooltipmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; K_PLUGIN_FACTORY( SidebarModeFactory, registerPlugin(); ) FocusHackWidget::FocusHackWidget(QWidget *parent) : QWidget(parent) {} FocusHackWidget::~FocusHackWidget() {} void FocusHackWidget::focusNext() { focusNextChild(); } void FocusHackWidget::focusPrevious() { focusNextPrevChild(false); } SubcategoryModel::SubcategoryModel(QAbstractItemModel *parentModel, QObject *parent) : QStandardItemModel(parent), m_parentModel(parentModel) {} QString SubcategoryModel::title() const { return m_title; } void SubcategoryModel::setParentIndex(const QModelIndex &activeModule) { blockSignals(true); //make the view receive a single signal when the new subcategory is loaded, //never make the view believe there are zero items if this is not the final count //this avoids the brief flash it had clear(); const int subRows = activeModule.isValid() ? m_parentModel->rowCount(activeModule) : 0; if ( subRows > 1) { for (int i = 0; i < subRows; ++i) { const QModelIndex& index = m_parentModel->index(i, 0, activeModule); QStandardItem *item = new QStandardItem(m_parentModel->data(index, Qt::DecorationRole).value(), m_parentModel->data(index, Qt::DisplayRole).toString()); item->setData(index.data(Qt::UserRole), Qt::UserRole); appendRow(item); } } blockSignals(false); beginResetModel(); endResetModel(); m_title = activeModule.data(Qt::DisplayRole).toString(); emit titleChanged(); } class MostUsedModel : public QSortFilterProxyModel { public: explicit MostUsedModel(QObject *parent = nullptr) : QSortFilterProxyModel (parent) { sort(0, Qt::DescendingOrder); setSortRole(ResultModel::ScoreRole); setDynamicSortFilter(true); //prepare default items m_defaultModel = new QStandardItemModel(this); QStandardItem *item = new QStandardItem(); item->setData(QUrl(QStringLiteral("kcm:kcm_lookandfeel.desktop")), ResultModel::ResourceRole); m_defaultModel->appendRow(item); item = new QStandardItem(); item->setData(QUrl(QStringLiteral("kcm:user_manager.desktop")), ResultModel::ResourceRole); m_defaultModel->appendRow(item); item = new QStandardItem(); item->setData(QUrl(QStringLiteral("kcm:screenlocker.desktop")), ResultModel::ResourceRole); m_defaultModel->appendRow(item); item = new QStandardItem(); item->setData(QUrl(QStringLiteral("kcm:powerdevilprofilesconfig.desktop")), ResultModel::ResourceRole); m_defaultModel->appendRow(item); item = new QStandardItem(); item->setData(QUrl(QStringLiteral("kcm:kcm_kscreen.desktop")), ResultModel::ResourceRole); m_defaultModel->appendRow(item); } void setResultModel(ResultModel *model) { if (m_resultModel == model) { return; } auto updateModel = [this]() { if (m_resultModel->rowCount() >= 5) { setSourceModel(m_resultModel); } else { setSourceModel(m_defaultModel); } }; m_resultModel = model; connect(m_resultModel, &QAbstractItemModel::rowsInserted, this, updateModel); connect(m_resultModel, &QAbstractItemModel::rowsRemoved, this, updateModel); updateModel(); } QHash roleNames() const override { QHash roleNames; roleNames.insert(Qt::DisplayRole, "display"); roleNames.insert(Qt::DecorationRole, "decoration"); roleNames.insert(ResultModel::ScoreRole, "score"); return roleNames; } bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override { const QString desktopName = sourceModel()->index(source_row, 0, source_parent).data(ResultModel::ResourceRole).toUrl().path(); KService::Ptr service = KService::serviceByStorageId(desktopName); return service; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { MenuItem *mi; const QString desktopName = QSortFilterProxyModel::data(index, ResultModel::ResourceRole).toUrl().path(); if (m_menuItems.contains(desktopName)) { mi = m_menuItems.value(desktopName); } else { mi = new MenuItem(false, nullptr); const_cast(this)->m_menuItems.insert(desktopName, mi); KService::Ptr service = KService::serviceByStorageId(desktopName); if (!service || !service->isValid()) { qWarning()<forgetResource(QStringLiteral("kcm:") % desktopName); return QVariant(); } mi->setService(service); } switch (role) { case Qt::UserRole: return QVariant::fromValue(mi); case Qt::DisplayRole: if (mi->service() && mi->service()->isValid()) { return mi->service()->name(); } else { return QVariant(); } case Qt::DecorationRole: if (mi->service() && mi->service()->isValid()) { return mi->service()->icon(); } else { return QVariant(); } case ResultModel::ScoreRole: return QSortFilterProxyModel::data(index, ResultModel::ScoreRole).toInt(); default: return QVariant(); } } private: QHash m_menuItems; // Model when there is nothing from kactivities-stat QStandardItemModel *m_defaultModel; // Model fed by kactivities-stats ResultModel *m_resultModel; }; class SidebarMode::Private { public: Private() : quickWidget( nullptr ), moduleView( nullptr ), collection( nullptr ), activeCategoryRow( -1 ), activeSubCategoryRow( -1 ) {} virtual ~Private() { delete aboutIcon; } ToolTipManager *toolTipManager = nullptr; ToolTipManager *mostUsedToolTipManager = nullptr; QQuickWidget * quickWidget = nullptr; KPackage::Package package; SubcategoryModel * subCategoryModel = nullptr; MostUsedModel * mostUsedModel = nullptr; FocusHackWidget * mainWidget = nullptr; QQuickWidget * placeHolderWidget = nullptr; QHBoxLayout * mainLayout = nullptr; KDeclarative::KDeclarative kdeclarative; MenuModel * model = nullptr; MenuProxyModel * categorizedModel = nullptr; MenuProxyModel * searchModel = nullptr; KDescendantsProxyModel * flatModel = nullptr; KAboutData * aboutIcon = nullptr; ModuleView * moduleView = nullptr; KActionCollection *collection = nullptr; QPersistentModelIndex activeCategoryIndex; int activeCategoryRow = -1; int activeSubCategoryRow = -1; int activeSearchRow = -1; bool m_actionMenuVisible = false; void setActionMenuVisible(SidebarMode* sidebarMode, const bool &actionMenuVisible) { if (m_actionMenuVisible == actionMenuVisible) { return; } m_actionMenuVisible = actionMenuVisible; emit sidebarMode->actionMenuVisibleChanged(); } bool m_introPageVisible = true; }; SidebarMode::SidebarMode( QObject *parent, const QVariantList &args ) : BaseMode( parent, args ) , d( new Private() ) { qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); d->aboutIcon = new KAboutData( QStringLiteral("SidebarView"), i18n( "Sidebar View" ), QStringLiteral("1.0"), i18n( "Provides a categorized sidebar for control modules." ), KAboutLicense::GPL, i18n( "(c) 2017, Marco Martin" ) ); d->aboutIcon->addAuthor( i18n( "Marco Martin" ), i18n( "Author" ), QStringLiteral("mart@kde.org") ); d->aboutIcon->addAuthor( i18n( "Ben Cooksley" ), i18n( "Author" ), QStringLiteral("bcooksley@kde.org") ); d->aboutIcon->addAuthor( i18n( "Mathias Soeken" ), i18n( "Developer" ), QStringLiteral("msoeken@informatik.uni-bremen.de") ); qmlRegisterType(); qmlRegisterType(); } SidebarMode::~SidebarMode() { delete d; } KAboutData * SidebarMode::aboutData() { return d->aboutIcon; } ModuleView * SidebarMode::moduleView() const { return d->moduleView; } QWidget * SidebarMode::mainWidget() { if( !d->quickWidget ) { initWidget(); } return d->mainWidget; } QAbstractItemModel * SidebarMode::categoryModel() const { return d->categorizedModel; } QAbstractItemModel * SidebarMode::searchModel() const { return d->searchModel; } QAbstractItemModel * SidebarMode::subCategoryModel() const { return d->subCategoryModel; } QAbstractItemModel * SidebarMode::mostUsedModel() const { return d->mostUsedModel; } QList SidebarMode::views() const { QList list; //list.append( d->categoryView ); return list; } void SidebarMode::initEvent() { d->model = new MenuModel( rootItem(), this ); foreach( MenuItem * child, rootItem()->children() ) { d->model->addException( child ); } d->categorizedModel = new MenuProxyModel( this ); d->categorizedModel->setCategorizedModel( true ); d->categorizedModel->setSourceModel( d->model ); d->categorizedModel->sort( 0 ); d->categorizedModel->setFilterHighlightsEntries( false ); d->flatModel = new KDescendantsProxyModel( this ); d->flatModel->setSourceModel( d->model ); d->searchModel = new MenuProxyModel( this ); d->searchModel->setCategorizedModel( true ); d->searchModel->setFilterHighlightsEntries( false ); d->searchModel->setSourceModel( d->flatModel ); d->mostUsedModel = new MostUsedModel( this ); d->subCategoryModel = new SubcategoryModel( d->categorizedModel, this ); d->mainWidget = new FocusHackWidget(); d->mainWidget->installEventFilter(this); d->mainLayout = new QHBoxLayout(d->mainWidget); d->mainLayout->setContentsMargins(0, 0, 0, 0); d->moduleView = new ModuleView( d->mainWidget ); connect( d->moduleView, &ModuleView::moduleChanged, this, &SidebarMode::moduleLoaded ); d->quickWidget = nullptr; moduleView()->setFaceType(KPageView::Plain); if (applicationMode() == BaseMode::InfoCenter) { d->moduleView->setSaveStatistics(false); d->moduleView->setApplyVisible(false); d->moduleView->setDefaultsVisible(false); d->moduleView->setResetVisible(false); } } QAction *SidebarMode::action(const QString &name) const { if (!d->collection) { return nullptr; } return d->collection->action(name); } QString SidebarMode::actionIconName(const QString &name) const { if (QAction *a = action(name)) { return a->icon().name(); } return QString(); } void SidebarMode::requestToolTip(const QModelIndex &index, const QRectF &rect) { if (showToolTips() && index.model()) { d->toolTipManager->setModel(index.model()); d->toolTipManager->requestToolTip(index, rect.toRect()); } } void SidebarMode::requestMostUsedToolTip(int index, const QRectF &rect) { if (showToolTips()) { d->mostUsedToolTipManager->requestToolTip(d->mostUsedModel->index(index, 0), rect.toRect()); } } void SidebarMode::hideToolTip() { d->toolTipManager->hideToolTip(); } void SidebarMode::hideMostUsedToolTip() { d->mostUsedToolTipManager->hideToolTip(); } void SidebarMode::showActionMenu(const QPoint &position) { QMenu *menu = new QMenu(); connect(menu, &QMenu::aboutToHide, this, [this] () { d->setActionMenuVisible(this, false); } ); menu->setAttribute(Qt::WA_DeleteOnClose); const QStringList actionList { QStringLiteral("configure"), QStringLiteral("help_contents"), QStringLiteral("help_about_app"), QStringLiteral("help_about_kde") }; for (const QString &actionName : actionList) { menu->addAction(d->collection->action(actionName)); } menu->popup(position); d->setActionMenuVisible(this, true); } -void SidebarMode::loadModule( const QModelIndex& activeModule ) +void SidebarMode::loadModule( const QModelIndex& activeModule, const QStringList &args ) { if (!activeModule.isValid()) { return; } if( !d->moduleView->resolveChanges() ) { return; } d->moduleView->closeModules(); MenuItem *mi = activeModule.data(MenuModel::MenuItemRole).value(); if (!mi) { return; } if (homeItem()) { d->m_introPageVisible = activeModule == d->categorizedModel->mapFromSource(d->model->indexForItem(homeItem())); emit introPageVisibleChanged(); } else { setIntroPageVisible(false); } if ( mi->children().length() < 1) { - d->moduleView->loadModule( activeModule ); + d->moduleView->loadModule( activeModule, args ); } else { - d->moduleView->loadModule( activeModule.model()->index(0, 0, activeModule) ); + d->moduleView->loadModule( activeModule.model()->index(0, 0, activeModule), args ); } if (activeModule.model() == d->categorizedModel) { const int newCategoryRow = activeModule.row(); if (d->activeCategoryRow == newCategoryRow) { return; } d->activeCategoryIndex = activeModule; d->activeCategoryRow = newCategoryRow; d->activeSubCategoryRow = 0; d->subCategoryModel->setParentIndex( activeModule ); if (d->activeSearchRow > -1) { d->activeSearchRow = -1; emit activeSearchRowChanged(); } emit activeCategoryRowChanged(); emit activeSubCategoryRowChanged(); } else if (activeModule.model() == d->subCategoryModel) { if (d->activeSearchRow > -1) { d->activeSearchRow = -1; emit activeSearchRowChanged(); } d->activeSubCategoryRow = activeModule.row(); emit activeSubCategoryRowChanged(); } else if (activeModule.model() == d->searchModel) { QModelIndex originalIndex = d->categorizedModel->mapFromSource( d->flatModel->mapToSource( d->searchModel->mapToSource(activeModule))); if (originalIndex.isValid()) { //are we in a subcategory of the top categories? if (originalIndex.parent().isValid() && mi->parent()->menu()) { d->activeCategoryRow = originalIndex.parent().row(); d->activeSubCategoryRow = originalIndex.row(); // Is this kcm directly at the top level without a top category? } else { d->activeCategoryRow = originalIndex.row(); d->activeSubCategoryRow = -1; } d->subCategoryModel->setParentIndex( originalIndex.parent() ); emit activeCategoryRowChanged(); emit activeSubCategoryRowChanged(); } d->activeSearchRow = activeModule.row(); emit activeSearchRowChanged(); - } else if (activeModule.model() == d->mostUsedModel) { + } else { if (d->activeSearchRow > -1) { d->activeSearchRow = -1; emit activeSearchRowChanged(); } QModelIndex flatIndex; // search the corresponding item on the main model for (int i = 0; i < d->flatModel->rowCount(); ++i) { QModelIndex idx = d->flatModel->index(i, 0); MenuItem *otherMi = idx.data(MenuModel::MenuItemRole).value(); if (otherMi->item() == mi->item()) { flatIndex = idx; break; } } if (flatIndex.isValid()) { QModelIndex idx = d->categorizedModel->mapFromSource(d->flatModel->mapToSource(flatIndex)); MenuItem *parentMi = idx.parent().data(MenuModel::MenuItemRole).value(); if (idx.isValid()) { if (parentMi && parentMi->menu()) { d->subCategoryModel->setParentIndex( idx.parent() ); d->activeCategoryRow = idx.parent().row(); d->activeSubCategoryRow = idx.row(); } else { d->activeCategoryRow = idx.row(); d->activeSubCategoryRow = -1; } emit activeCategoryRowChanged(); emit activeSubCategoryRowChanged(); } } } } void SidebarMode::moduleLoaded() { d->placeHolderWidget->hide(); d->moduleView->show(); } int SidebarMode::activeSearchRow() const { return d->activeSearchRow; } int SidebarMode::activeCategoryRow() const { return d->activeCategoryRow; } void SidebarMode::setIntroPageVisible(const bool &introPageVisible) { if (d->m_introPageVisible == introPageVisible) { return; } // TODO: Make the intro page of SystemSettings a KCM as well if (homeItem()) { d->placeHolderWidget->hide(); d->moduleView->show(); if (introPageVisible) { loadModule(d->categorizedModel->mapFromSource(d->model->indexForItem(homeItem()))); } } else { if (introPageVisible) { d->activeCategoryRow = -1; emit activeCategoryRowChanged(); d->activeSubCategoryRow = -1; emit activeSubCategoryRowChanged(); d->placeHolderWidget->show(); d->moduleView->hide(); } else { d->placeHolderWidget->hide(); d->moduleView->show(); } } d->m_introPageVisible = introPageVisible; emit introPageVisibleChanged(); } int SidebarMode::width() const { return d->mainWidget->width(); } bool SidebarMode::actionMenuVisible() const { return d->m_actionMenuVisible; } int SidebarMode::activeSubCategoryRow() const { return d->activeSubCategoryRow; } bool SidebarMode::introPageVisible() const { return (d->m_introPageVisible); } void SidebarMode::initWidget() { // Create the widgets if (!KMainWindow::memberList().isEmpty()) { KXmlGuiWindow *mainWindow = qobject_cast(KMainWindow::memberList().first()); if (mainWindow) { d->collection = mainWindow->actionCollection(); } } d->quickWidget = new QQuickWidget(d->mainWidget); d->quickWidget->quickWindow()->setTitle(i18n("Sidebar")); d->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); d->quickWidget->engine()->rootContext()->setContextProperty(QStringLiteral("systemsettings"), this); d->package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("KPackage/GenericQML")); d->package.setPath(QStringLiteral("org.kde.systemsettings.sidebar")); d->kdeclarative.setDeclarativeEngine(d->quickWidget->engine()); d->kdeclarative.setupEngine(d->quickWidget->engine()); d->kdeclarative.setupContext(); d->quickWidget->setSource(QUrl::fromLocalFile(d->package.filePath("mainscript"))); if (!d->quickWidget->rootObject()) { for (const auto &err : d->quickWidget->errors()) { qWarning() << err.toString(); } qFatal("Fatal error while loading the sidebar view qml component"); } const int rootImplicitWidth = d->quickWidget->rootObject()->property("implicitWidth").toInt(); if (rootImplicitWidth != 0) { d->quickWidget->setFixedWidth(rootImplicitWidth); } else { d->quickWidget->setFixedWidth(240); } connect(d->quickWidget->rootObject(), &QQuickItem::implicitWidthChanged, this, [this]() { const int rootImplicitWidth = d->quickWidget->rootObject()->property("implicitWidth").toInt(); if (rootImplicitWidth != 0) { d->quickWidget->setFixedWidth(rootImplicitWidth); } else { d->quickWidget->setFixedWidth(240); } }); connect(d->quickWidget->rootObject(), SIGNAL(focusNextRequest()), d->mainWidget, SLOT(focusNext())); connect(d->quickWidget->rootObject(), SIGNAL(focusPreviousRequest()), d->mainWidget, SLOT(focusPrevious())); d->quickWidget->installEventFilter(this); d->placeHolderWidget = new QQuickWidget(d->mainWidget); d->placeHolderWidget->quickWindow()->setTitle(i18n("Most Used")); d->placeHolderWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); d->placeHolderWidget->engine()->rootContext()->setContextObject(new KLocalizedContext(d->placeHolderWidget)); d->placeHolderWidget->engine()->rootContext()->setContextProperty(QStringLiteral("systemsettings"), this); d->placeHolderWidget->setSource(QUrl::fromLocalFile(d->package.filePath("ui", QStringLiteral("introPage.qml")))); connect(d->placeHolderWidget->rootObject(), SIGNAL(focusNextRequest()), d->mainWidget, SLOT(focusNext())); connect(d->placeHolderWidget->rootObject(), SIGNAL(focusPreviousRequest()), d->mainWidget, SLOT(focusPrevious())); d->placeHolderWidget->installEventFilter(this); d->mainLayout->addWidget( d->quickWidget ); d->moduleView->hide(); d->mainLayout->addWidget( d->moduleView ); d->mainLayout->addWidget( d->placeHolderWidget ); emit changeToolBarItems(BaseMode::NoItems); d->toolTipManager = new ToolTipManager(d->categorizedModel, d->quickWidget, ToolTipManager::ToolTipPosition::Right); d->mostUsedToolTipManager = new ToolTipManager(d->mostUsedModel, d->placeHolderWidget, ToolTipManager::ToolTipPosition::BottomCenter); d->mostUsedModel->setResultModel(new ResultModel( AllResources | Agent(QStringLiteral("org.kde.systemsettings")) | HighScoredFirst | Limit(5), this)); if (homeItem()) { d->placeHolderWidget->hide(); d->moduleView->show(); loadModule(d->categorizedModel->mapFromSource(d->model->indexForItem(homeItem()))); } + + if (!startupModule().isEmpty()) { + MenuItem *item = rootItem()->descendantForModule(startupModule()); + if (item) { + loadModule(d->model->indexForItem(item), startupModuleArgs()); + } + } +} + +void SidebarMode::reloadStartupModule() +{ + if (!startupModule().isEmpty()) { + MenuItem *item = rootItem()->descendantForModule(startupModule()); + if (item) { + loadModule(d->model->indexForItem(item), startupModuleArgs()); + } + } } bool SidebarMode::eventFilter(QObject* watched, QEvent* event) { //FIXME: those are all workarounds around the QQuickWidget brokeness if ((watched == d->quickWidget || watched == d->placeHolderWidget) && event->type() == QEvent::KeyPress) { //allow tab navigation inside the qquickwidget QKeyEvent *ke = static_cast(event); QQuickWidget *qqw = static_cast(watched); if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) { QCoreApplication::sendEvent(qqw->quickWindow(), event); return true; } } else if ((watched == d->quickWidget || watched == d->placeHolderWidget) && event->type() == QEvent::FocusIn) { QFocusEvent *fe = static_cast(event); QQuickWidget *qqw = static_cast(watched); if (qqw && qqw->rootObject()) { if (fe->reason() == Qt::TabFocusReason) { QMetaObject::invokeMethod(qqw->rootObject(), "focusFirstChild"); } else if (fe->reason() == Qt::BacktabFocusReason) { QMetaObject::invokeMethod(qqw->rootObject(), "focusLastChild"); } } } else if (watched == d->quickWidget && event->type() == QEvent::Leave) { QCoreApplication::sendEvent(d->quickWidget->quickWindow(), event); } else if (watched == d->mainWidget && event->type() == QEvent::Resize) { emit widthChanged(); } else if (watched == d->mainWidget && event->type() == QEvent::Show) { emit changeToolBarItems(BaseMode::NoItems); } return BaseMode::eventFilter(watched, event); } void SidebarMode::giveFocus() { d->quickWidget->setFocus(); } #include "SidebarMode.moc" diff --git a/sidebar/SidebarMode.h b/sidebar/SidebarMode.h index c0cf98dc..08a2d823 100644 --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -1,136 +1,138 @@ /*************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * 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 SIDEBARMODE_H #define SIDEBARMODE_H #include "BaseMode.h" #include #include class ModuleView; class KAboutData; class QModelIndex; class QAbstractItemView; class QAbstractItemModel; class QAction; class FocusHackWidget : public QWidget { Q_OBJECT public: explicit FocusHackWidget(QWidget *parent = nullptr); ~FocusHackWidget() override; public Q_SLOTS: void focusNext(); void focusPrevious(); }; class SubcategoryModel : public QStandardItemModel { Q_OBJECT Q_PROPERTY(QString title READ title NOTIFY titleChanged) public: explicit SubcategoryModel(QAbstractItemModel *parentModel, QObject *parent = nullptr); QString title() const; void setParentIndex(const QModelIndex &activeModule); Q_SIGNALS: void titleChanged(); private: QAbstractItemModel *m_parentModel; QString m_title; }; class SidebarMode : public BaseMode { Q_OBJECT Q_PROPERTY(QAbstractItemModel *categoryModel READ categoryModel CONSTANT) Q_PROPERTY(QAbstractItemModel *searchModel READ searchModel CONSTANT) Q_PROPERTY(QAbstractItemModel *subCategoryModel READ subCategoryModel CONSTANT) Q_PROPERTY(QAbstractItemModel *mostUsedModel READ mostUsedModel CONSTANT) Q_PROPERTY(int activeCategoryRow READ activeCategoryRow NOTIFY activeCategoryRowChanged) Q_PROPERTY(int activeSearchRow READ activeSearchRow NOTIFY activeSearchRowChanged) Q_PROPERTY(int activeSubCategoryRow READ activeSubCategoryRow NOTIFY activeSubCategoryRowChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(bool actionMenuVisible READ actionMenuVisible NOTIFY actionMenuVisibleChanged) Q_PROPERTY(bool introPageVisible READ introPageVisible WRITE setIntroPageVisible NOTIFY introPageVisibleChanged) public: SidebarMode(QObject * parent, const QVariantList &args ); ~SidebarMode() override; QWidget * mainWidget() override; void initEvent() override; void giveFocus() override; KAboutData * aboutData() override; ModuleView * moduleView() const override; + void reloadStartupModule() override; + QAbstractItemModel *categoryModel() const; QAbstractItemModel *searchModel() const; QAbstractItemModel *subCategoryModel() const; QAbstractItemModel *mostUsedModel() const; int activeCategoryRow() const; int activeSubCategoryRow() const; int activeSearchRow() const; int width() const; bool actionMenuVisible() const; bool introPageVisible() const; void setIntroPageVisible(const bool &introPageVisible); Q_INVOKABLE QAction *action(const QString &name) const; // QML doesn't understand QIcon, otherwise we could get it from the QAction itself Q_INVOKABLE QString actionIconName(const QString &name) const; Q_INVOKABLE void requestToolTip(const QModelIndex &index, const QRectF &rect); Q_INVOKABLE void requestMostUsedToolTip(int index, const QRectF &rect); Q_INVOKABLE void hideToolTip(); Q_INVOKABLE void hideMostUsedToolTip(); Q_INVOKABLE void showActionMenu(const QPoint &position); - Q_INVOKABLE void loadModule(const QModelIndex& activeModule); + Q_INVOKABLE void loadModule(const QModelIndex& activeModule, const QStringList &args = QStringList()); protected: QList views() const override; bool eventFilter(QObject* watched, QEvent* event) override; private Q_SLOTS: void moduleLoaded(); void initWidget(); Q_SIGNALS: void activeCategoryRowChanged(); void activeSubCategoryRowChanged(); void activeSearchRowChanged(); void widthChanged(); void actionMenuVisibleChanged(); void introPageVisibleChanged(); private: class Private; Private *const d; }; #endif