diff --git a/app/SettingsBase.h b/app/SettingsBase.h --- a/app/SettingsBase.h +++ b/app/SettingsBase.h @@ -46,6 +46,9 @@ bool isInfoCenterMode() const; bool queryClose() override; + void setStartupModule(const QString &startupModule); + void setStartupModuleArgs(const QStringList &startupModuleArgs); + protected: QSize sizeHint() const override; @@ -96,5 +99,7 @@ // The about dialog KAboutApplicationDialog * aboutDialog = nullptr; BaseMode::ApplicationMode m_mode = BaseMode::SystemSettings; + QString m_startupModule; + QStringList m_startupModuleArgs; }; #endif diff --git a/app/SettingsBase.cpp b/app/SettingsBase.cpp --- a/app/SettingsBase.cpp +++ b/app/SettingsBase.cpp @@ -129,7 +129,7 @@ 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 ); @@ -332,6 +332,16 @@ return changes; } +void SettingsBase::setStartupModule(const QString &startupModule) +{ + m_startupModule = startupModule; +} + +void SettingsBase::setStartupModuleArgs(const QStringList &startupModuleArgs) +{ + m_startupModuleArgs = startupModuleArgs; +} + void SettingsBase::about() { delete aboutDialog; diff --git a/app/main.cpp b/app/main.cpp --- a/app/main.cpp +++ b/app/main.cpp @@ -25,13 +25,41 @@ #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, @@ -75,10 +103,39 @@ 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() << 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"))); @@ -98,5 +155,15 @@ 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.h b/core/BaseMode.h --- a/core/BaseMode.h +++ b/core/BaseMode.h @@ -183,6 +183,9 @@ */ bool showToolTips() const; + QString startupModule() const; + QStringList startupModuleArgs() const; + public Q_SLOTS: /** * Called when the text in the search box changes allowing the display to be filtered. diff --git a/core/BaseMode.cpp b/core/BaseMode.cpp --- a/core/BaseMode.cpp +++ b/core/BaseMode.cpp @@ -38,6 +38,8 @@ KService::Ptr service; MenuItem *rootItem = nullptr; MenuItem *homeItem = nullptr; + QString startupModule; + QStringList startupModuleArgs; KConfigGroup config; bool showToolTips = true; BaseMode::ApplicationMode applicationMode = BaseMode::SystemSettings; @@ -47,9 +49,15 @@ : 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() @@ -111,6 +119,16 @@ return d->showToolTips; } +QString BaseMode::startupModule() const +{ + return d->startupModule; +} + +QStringList BaseMode::startupModuleArgs() const +{ + return d->startupModuleArgs; +} + void BaseMode::searchChanged( const QString& text ) { Q_UNUSED( text ); diff --git a/core/MenuItem.h b/core/MenuItem.h --- a/core/MenuItem.h +++ b/core/MenuItem.h @@ -155,6 +155,8 @@ */ void setService( const KService::Ptr& service ); + MenuItem *descendantForModule(const QString &moduleName); + private: class Private; Private *const d; diff --git a/core/MenuItem.cpp b/core/MenuItem.cpp --- a/core/MenuItem.cpp +++ b/core/MenuItem.cpp @@ -139,3 +139,20 @@ 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/ModuleView.h b/core/ModuleView.h --- a/core/ModuleView.h +++ b/core/ModuleView.h @@ -144,7 +144,7 @@ * * @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. @@ -173,7 +173,7 @@ 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 ); diff --git a/core/ModuleView.cpp b/core/ModuleView.cpp --- a/core/ModuleView.cpp +++ b/core/ModuleView.cpp @@ -133,7 +133,7 @@ return aboutData; } -void ModuleView::loadModule( const QModelIndex &menuItem ) +void ModuleView::loadModule( const QModelIndex &menuItem, const QStringList &args ) { if ( !menuItem.isValid() ) { return; @@ -149,12 +149,12 @@ 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; @@ -186,7 +186,7 @@ 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())); diff --git a/icons/IconMode.h b/icons/IconMode.h --- a/icons/IconMode.h +++ b/icons/IconMode.h @@ -49,12 +49,14 @@ 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; }; diff --git a/icons/IconMode.cpp b/icons/IconMode.cpp --- a/icons/IconMode.cpp +++ b/icons/IconMode.cpp @@ -46,6 +46,7 @@ KCategoryDrawer * categoryDrawer; KCategorizedView * categoryView; QStackedWidget * mainWidget; + MenuModel * model; MenuProxyModel * proxyModel; KAboutData * aboutIcon; ModuleView * moduleView; @@ -101,14 +102,14 @@ 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(); @@ -136,6 +137,11 @@ } 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 ); @@ -147,7 +153,7 @@ d->moduleView->setFaceType(KPageView::Plain); } - d->moduleView->loadModule( activeModule ); + d->moduleView->loadModule( activeModule, args ); } void IconMode::moduleLoaded() @@ -194,6 +200,13 @@ 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()); + } + } } bool IconMode::eventFilter(QObject* watched, QEvent* event) diff --git a/sidebar/SidebarMode.h b/sidebar/SidebarMode.h --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -110,7 +110,7 @@ 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; diff --git a/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -440,7 +440,7 @@ d->setActionMenuVisible(this, true); } -void SidebarMode::loadModule( const QModelIndex& activeModule ) +void SidebarMode::loadModule( const QModelIndex& activeModule, const QStringList &args ) { if (!activeModule.isValid()) { return; @@ -466,9 +466,9 @@ } 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) { @@ -526,7 +526,7 @@ d->activeSearchRow = activeModule.row(); emit activeSearchRowChanged(); - } else if (activeModule.model() == d->mostUsedModel) { + } else { if (d->activeSearchRow > -1) { d->activeSearchRow = -1; emit activeSearchRowChanged(); @@ -709,6 +709,13 @@ 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()); + } + } } bool SidebarMode::eventFilter(QObject* watched, QEvent* event)