diff --git a/app/SettingsBase.cpp b/app/SettingsBase.cpp index 6fce9105..6cfa1472 100644 --- a/app/SettingsBase.cpp +++ b/app/SettingsBase.cpp @@ -1,428 +1,468 @@ /*************************************************************************** * 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( QWidget * parent ) - : KXmlGuiWindow(parent) +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 ); - setWindowTitle(i18n("System Settings")); - setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-system"))); 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 - 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")); + 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, QVariantList(), &error); + BaseMode * controller = activeService->createInstance(this, {m_mode}, &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"))); + // 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); - const QString parentCategory = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category")).toString(); - const QString parentCategory2 = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category-V2")).toString(); - if ( parentCategory == parent->category() || + 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); - const QString category = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category")).toString(); - const QString category2 = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category-V2")).toString(); + + 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::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 = BaseConfig::activeView(); + 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()); } // 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(); - configureAction->setDisabled(state); + 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 ) { + 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 06d41bea..e6ba07e6 100644 --- a/app/SettingsBase.h +++ b/app/SettingsBase.h @@ -1,97 +1,100 @@ /*************************************************************************** * 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(QWidget * parent = nullptr); + explicit SettingsBase(BaseMode::ApplicationMode mode, QWidget * parent = nullptr); ~SettingsBase() override; + bool isInfoCenterMode() const; bool queryClose() override; 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; }; #endif diff --git a/app/main.cpp b/app/main.cpp index bf36551e..9c5a6692 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,72 +1,102 @@ /** * 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 "SystemSettingsApp.h" #include "SettingsBase.h" int main( int argc, char *argv[] ) { + // Make sure the binary name is either kinfocenter or systemsettings, + // Anything else will just be considered as "systemsettings" + QString binaryName = QString::fromUtf8(argv[0]); + BaseMode::ApplicationMode mode = BaseMode::InfoCenter; + if (binaryName != QStringLiteral("kinfocenter")) { + binaryName = QStringLiteral("systemsettings"); + mode = BaseMode::SystemSettings; + } + //exec is systemsettings5, but we need the QPT to use the right config from the qApp constructor //which is before KAboutData::setApplicationData - QCoreApplication::setApplicationName(QStringLiteral("systemsettings")); + QCoreApplication::setApplicationName(binaryName); KWorkSpace::detectPlatform(argc, argv); SystemSettingsApp application(argc, argv); KQuickAddons::QtQuickSettings::init(); KCrash::initialize(); - KLocalizedString::setApplicationDomain("systemsettings"); + KLocalizedString::setApplicationDomain(binaryName.toUtf8().constData()); - // About data - KAboutData aboutData(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")); + 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")); - if (qEnvironmentVariableIsSet("KDE_FULL_SESSION")) { - aboutData.setDesktopFileName(QStringLiteral("systemsettings")); } else { - aboutData.setDesktopFileName(QStringLiteral("kdesystemsettings")); + 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")); } - KAboutData::setApplicationData(aboutData); - application.setAttribute(Qt::AA_UseHighDpiPixmaps, true); - application.setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-system"))); + QCommandLineParser parser; + aboutData.setupCommandLine(&parser); parser.process(application); aboutData.processCommandLine(&parser); - SettingsBase *mainWindow = new SettingsBase(); + 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); mainWindow->show(); application.setMainWindow(mainWindow); return application.exec(); } diff --git a/core/BaseData.cpp b/core/BaseData.cpp index 76144a80..d0f316e5 100644 --- a/core/BaseData.cpp +++ b/core/BaseData.cpp @@ -1,71 +1,81 @@ /*************************************************************************** * 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 "BaseData.h" #include "MenuItem.h" #include #include class DataHelper { public: DataHelper() {} ~DataHelper() { delete object; } BaseData * object = nullptr; }; Q_GLOBAL_STATIC(DataHelper, internalInstance) BaseData::BaseData() { internalInstance->object = this; } BaseData::~BaseData() { } BaseData *BaseData::instance() { if( !internalInstance->object ) { new BaseData(); } return internalInstance->object; } MenuItem * BaseData::menuItem() { return rootMenu; } void BaseData::setMenuItem( MenuItem * item ) { rootMenu = item; } +MenuItem * BaseData::homeItem() +{ + return m_homeItem; +} + +void BaseData::setHomeItem( MenuItem * item ) +{ + m_homeItem = item; +} + KConfigGroup BaseData::configGroup( const QString& pluginName ) { return KSharedConfig::openConfig()->group( pluginName ); } diff --git a/core/BaseData.h b/core/BaseData.h index 4d162231..13fa2a4b 100644 --- a/core/BaseData.h +++ b/core/BaseData.h @@ -1,88 +1,104 @@ /*************************************************************************** * 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 BASEDATA_H #define BASEDATA_H #include #include "systemsettingsview_export.h" class QString; class MenuItem; class KConfigGroup; /** * @brief Provides a interface sharing common data between modules in System Settings * * BaseData is a standard interface in System Settings to retrieve information that is shared between all modules. * It is a singleton, and will be automatically cleaned up. * * @author Ben Cooksley */ class SYSTEMSETTINGSVIEW_EXPORT BaseData : public QObject { Q_OBJECT Q_DISABLE_COPY(BaseData) private: explicit BaseData(); public: /** * Provides a pointer to access the shared BaseData instance in order to retrieve data. * * @returns Access to the shared instance of BaseData. */ static BaseData* instance(); /** * Normal destructor that handles cleanup. Any objects created through BaseData must be assumed * to be invalid afterwards. */ ~BaseData() override; /** * Provides the shared MenuItem which lists all categories and modules, for use with MenuModel. * * @returns the shared MenuItem. */ MenuItem * menuItem(); /** * Sets the MenuItem which the Singleton will return. * For internal use only. * * @param item A pointer to the MenuItem object */ void setMenuItem( MenuItem * item ); + /** + * Provides the shared MenuItem that corresponds to a KCM which should be used as startup page. + * + * @returns the shared MenuItem. It may be nullptr. + */ + MenuItem * homeItem(); + + /** + * Sets the homescreen MenuItem which the Singleton will return. + * For internal use only. + * + * @param item A pointer to the MenuItem object + */ + void setHomeItem( MenuItem * item ); + /** * Returns the configuration group by the name provided in the current applications configuration file. * * @param pluginName the name of the group that is required. * @returns The configuration group that is required. */ KConfigGroup configGroup( const QString& pluginName ); private: MenuItem * rootMenu; + MenuItem * m_homeItem; }; #endif diff --git a/core/BaseMode.cpp b/core/BaseMode.cpp index ba179410..2b93ae10 100644 --- a/core/BaseMode.cpp +++ b/core/BaseMode.cpp @@ -1,148 +1,164 @@ /*************************************************************************** * 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; KConfigGroup config; bool showToolTips = true; + BaseMode::ApplicationMode applicationMode = BaseMode::SystemSettings; }; -BaseMode::BaseMode( QObject* parent ) +BaseMode::BaseMode( QObject* parent, const QVariantList &args ) : QObject( parent ) , d( new Private() ) { + if (!args.isEmpty() && args.first().canConvert()) { + d->applicationMode = args.first().value(); + } } 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::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 590c0ec2..c1c29432 100644 --- a/core/BaseMode.h +++ b/core/BaseMode.h @@ -1,257 +1,279 @@ /***************************************************************************** * 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 ); + 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; 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 5fd52e07..c6e7c001 100644 --- a/core/MenuItem.cpp +++ b/core/MenuItem.cpp @@ -1,138 +1,141 @@ /* 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; } } diff --git a/core/MenuModel.cpp b/core/MenuModel.cpp index 31964c02..0158f760 100644 --- a/core/MenuModel.cpp +++ b/core/MenuModel.cpp @@ -1,232 +1,247 @@ /************************************************************************** * Copyright (C) 2009 Ben Cooksley * * Copyright (C) 2007 Will Stephenson * * * * 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 "MenuModel.h" #include #include #include "MenuItem.h" - class MenuModel::Private { public: Private() {} MenuItem *rootItem = nullptr; QList exceptions; }; MenuModel::MenuModel( MenuItem * menuRoot, QObject *parent ) : QAbstractItemModel( parent ) , d( new Private() ) { d->rootItem = menuRoot; } MenuModel::~MenuModel() { d->exceptions.clear(); delete d; } QHash MenuModel::roleNames() const { QHash names = QAbstractItemModel::roleNames(); names[DepthRole] = "DepthRole"; names[IsCategoryRole] = "IsCategoryRole"; return names; } int MenuModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED( parent ); return 1; } int MenuModel::rowCount( const QModelIndex &parent ) const { MenuItem * mi; if ( parent.isValid() ) { mi = static_cast( parent.internalPointer() ); } else { mi = d->rootItem; } - return childrenList(mi).count(); } QVariant MenuModel::data( const QModelIndex &index, int role ) const { MenuItem * mi = nullptr; QVariant theData; if ( !index.isValid() ) { return QVariant(); } mi = static_cast( index.internalPointer() ); switch ( role ) { case Qt::DisplayRole: theData.setValue( mi->name() ); break; case Qt::ToolTipRole: theData.setValue( mi->service()->comment() ); break; case Qt::DecorationRole: theData = QVariant( QIcon::fromTheme( mi->service()->icon() ) ); break; case KCategorizedSortFilterProxyModel::CategorySortRole: if ( mi->parent() ) { theData.setValue( QStringLiteral("%1%2").arg( QString::number(mi->parent()->weight()), 5, QLatin1Char('0') ).arg( mi->parent()->name() ) ); } break; case KCategorizedSortFilterProxyModel::CategoryDisplayRole: { MenuItem *candidate = mi->parent(); // The model has an invisible single root item. // So to get the "root category" we don't go up all the way // To the actual root, but to the list of the first childs. // That's why we check for candidate->parent()->parent() while ( candidate && candidate->parent() && candidate->parent()->parent() ) { candidate = candidate->parent(); } if (candidate) { theData.setValue( candidate->name() ); } break; } case MenuModel::MenuItemRole: theData.setValue( mi ); break; case MenuModel::UserFilterRole: theData.setValue( mi->keywords().join( QString() ) ); break; case MenuModel::UserSortRole: theData.setValue( QStringLiteral("%1").arg( QString::number(mi->weight()), 5, QLatin1Char('0') ) ); break; case MenuModel::DepthRole: { MenuItem *candidate = mi; // -1 excludes the invisible root, having main categories at depth 0 int depth = -1; while ( candidate && candidate->parent() ) { candidate = candidate->parent(); ++depth; } theData.setValue( depth ); break; } case MenuModel::IsCategoryRole: theData.setValue( mi->menu() ); break; default: break; } return theData; } Qt::ItemFlags MenuModel::flags( const QModelIndex &index ) const { if ( !index.isValid() ) { return Qt::NoItemFlags; } return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QModelIndex MenuModel::index( int row, int column, const QModelIndex &parent ) const { if ( !hasIndex(row, column, parent) ) { return QModelIndex(); } MenuItem *parentItem; if ( !parent.isValid() ) { parentItem = d->rootItem; } else { parentItem = static_cast( parent.internalPointer() ); } MenuItem *childItem = childrenList(parentItem).value(row); if ( childItem ) { return createIndex( row, column, childItem ); } else { return QModelIndex(); } } QModelIndex MenuModel::parent( const QModelIndex &index ) const { MenuItem *childItem = static_cast( index.internalPointer() ); if( !childItem ) { return QModelIndex(); } MenuItem * parent = parentItem(childItem); MenuItem * grandParent = parentItem(parent); int childRow = 0; if( grandParent ) { childRow = childrenList( grandParent ).indexOf( parent ); } if ( parent == d->rootItem ) { return QModelIndex(); } return createIndex( childRow, 0, parent ); } QList MenuModel::childrenList( MenuItem * parent ) const { QList children = parent->children(); foreach( MenuItem * child, children ) { if( d->exceptions.contains( child ) ) { children.removeOne(child); children.append(child->children()); } } return children; } MenuItem * MenuModel::parentItem( MenuItem * child ) const { MenuItem * parent = child->parent(); if( d->exceptions.contains(parent) ) { parent = parentItem(parent); } return parent; } +QModelIndex MenuModel::indexForItem( MenuItem * item ) const +{ + MenuItem * parent = parentItem(item); + + if (!parent) { + return QModelIndex(); + } + + const int row = childrenList(parent).indexOf(item); + + if (row < 0) { + return QModelIndex(); + } + + return createIndex(row, 0, item); +} + MenuItem * MenuModel::rootItem() const { return d->rootItem; } void MenuModel::addException( MenuItem * exception ) { if( exception == d->rootItem ) { return; } d->exceptions.append(exception); } void MenuModel::removeException( MenuItem * exception ) { d->exceptions.removeAll(exception); } diff --git a/core/MenuModel.h b/core/MenuModel.h index 284b8f01..0151b0ca 100644 --- a/core/MenuModel.h +++ b/core/MenuModel.h @@ -1,182 +1,184 @@ /************************************************************************** * Copyright (C) 2009 Ben Cooksley * * Copyright (C) 2007 Will Stephenson * * * * 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 MENUMODEL_H #define MENUMODEL_H #include #include "systemsettingsview_export.h" class MenuItem; /** * @brief Provides a menu of the MenuItem objects * * Provides a standardised model to be used with views to display the list of modules in a variety of ways.\n * It is recommended to also use this with the MenuProxyModel to provide searching * and correct ordering of modules. * * @author Ben Cooksley * @author Will Stephenson */ class SYSTEMSETTINGSVIEW_EXPORT MenuModel : public QAbstractItemModel { Q_OBJECT public: enum Roles { MenuItemRole = Qt::UserRole, /** * Role used to request the keywords to filter the items when searching. */ UserFilterRole, /** * Role used to request the weight of a module, used to sort the items. */ UserSortRole, DepthRole, IsCategoryRole }; /** * Creates a MenuModel using the MenuItem specified. The MenuItem must always be valid * throughout the life of the MenuModel, otherwise it will cause crashes. * * @param menuRoot The MenuItem to use as the basis for providing information. * @param parent The QObject to use as a parent of the MenuModel. */ explicit MenuModel( MenuItem * menuRoot, QObject *parent = nullptr ); /** * Destroys the MenuModel. The menuRoot will not be destroyed. */ ~MenuModel() override; QHash roleNames() const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides the name, tooltip, icon, category, keywords and the internal MenuItem to views. * * @param index The QModelIndex you want information about. * @param role The information role you want information about. * @returns The data requested for the role provided from the QModelIndex provided. */ QVariant data( const QModelIndex &index, int role ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides the status flags for the QModelIndex specified. * The item always has selectable and enabled for its status unless invalid. * * @returns The flags for the QModelIndex provided. */ Qt::ItemFlags flags( const QModelIndex &index ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides a QModelIndex at the row and column specified who belongs to the parent specified. * * @param row Vertical position in the grid of children. * @param column Horizontal position in the grid of children. * @param parent The parent of the requested child. * @returns The QModelIndex for the item requested. */ QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides the parent QModelIndex for the child specified. * * @param index The child of the parent. * @returns A QModelIndex for the parent. */ QModelIndex parent( const QModelIndex &index ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides the number of MenuItems ( categories or modules ) that the specified parent has. * * @param parent The QModelIndex the count is performed on. * @returns The number of rows ( children ) in the parent. */ int rowCount( const QModelIndex &parent = QModelIndex() ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Returns 1, the number of columns of information about a row. * * @param parent This is ignored, as the count is always 1. * @returns The number of columns ( 1 ) in the parent. */ int columnCount( const QModelIndex &parent = QModelIndex() ) const override; /** * Makes the MenuItem specified be hidden from the list, while still showing its children.\n * Children of exceptions consider their grand parents as their parent. * Parents of exceptions consider the exceptions children as theirs. * * @param exception The MenuItem to give an exception to. */ void addException( MenuItem * exception ); /** * Revokes the exception granted above. After this, the MenuItem's parents will return their children * as normal and the grand children will return their true parents, restoring normal operation. * It has no effect if the MenuItem does not have an exception. * * @param exception The MenuItem to revoke an exception from. */ void removeException( MenuItem * exception ); + QModelIndex indexForItem( MenuItem * item ) const; + protected: /** * Provides the MenuItem which is used internally to provide information. * * @returns The MenuItem used internally. */ MenuItem* rootItem() const; /** * Provides a list of children of an item which has been altered by the exceptions list * * @param parent The parent of the children desired * @returns The list of children for the item specified */ QList childrenList( MenuItem * parent ) const; /** * Provides the parent of the child specified altered by the exceptions list * * @param child The child of the parent * @returns The exceptions list affected parent of the child */ MenuItem* parentItem( MenuItem * child ) const; private: class Private; Private *const d; }; #endif diff --git a/core/ModuleView.cpp b/core/ModuleView.cpp index c898d309..eaaeb456 100644 --- a/core/ModuleView.cpp +++ b/core/ModuleView.cpp @@ -1,431 +1,475 @@ /***************************************************************************** * 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 "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; + 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 ) { 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() ); } // changing state is not needed here as the adding / changing of pages does it } void ModuleView::addModule( KCModuleInfo *module ) { 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 ); 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; } QUrl url( QStringLiteral("help:/")+docPath ); QProcess::startDetached(QStringLiteral("khelpcenter"), QStringList() << url.url()); } 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) { - KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("kcm:") + activeModule->moduleInfo().service()->storageId()), - QStringLiteral("org.kde.systemsettings")); + // 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 f658c6cf..e66f233f 100644 --- a/core/ModuleView.h +++ b/core/ModuleView.h @@ -1,159 +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 ); /** * 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 ); 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 880f3c3e..3194e79a 100644 --- a/icons/IconMode.cpp +++ b/icons/IconMode.cpp @@ -1,218 +1,218 @@ /************************************************************************** * 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; MenuProxyModel * proxyModel; KAboutData * aboutIcon; ModuleView * moduleView; QAction * backAction; }; -IconMode::IconMode( QObject *parent, const QVariantList& ) - : BaseMode( parent ) +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 ); foreach( MenuItem * child, rootItem()->children() ) { model->addException( child ); } d->proxyModel = new MenuProxyModel( this ); d->proxyModel->setCategorizedModel( true ); d->proxyModel->setSourceModel( 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 ) { 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 ); } 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); } 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/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp index 8167f1a5..b7116e69 100644 --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -1,724 +1,751 @@ /************************************************************************** * 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& ) - : BaseMode( parent ) +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 ) { if (!activeModule.isValid()) { return; } d->moduleView->closeModules(); MenuItem *mi = activeModule.data(MenuModel::MenuItemRole).value(); if (!mi) { return; } - setIntroPageVisible(false); + 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 ); } else { d->moduleView->loadModule( activeModule.model()->index(0, 0, activeModule) ); } if (activeModule.model() == d->categorizedModel) { const int newCategoryRow = activeModule.row(); if (d->activeCategoryRow == newCategoryRow) { return; } if( !d->moduleView->resolveChanges() ) { 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) { 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; } - if (introPageVisible) { - d->activeCategoryRow = -1; - emit activeCategoryRowChanged(); - d->activeSubCategoryRow = -1; - emit activeSubCategoryRowChanged(); - d->placeHolderWidget->show(); - d->moduleView->hide(); - } else { + // 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()))); + } } 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 4f86510f..c0cf98dc 100644 --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -1,136 +1,136 @@ /*************************************************************************** * 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& ); + SidebarMode(QObject * parent, const QVariantList &args ); ~SidebarMode() override; QWidget * mainWidget() override; void initEvent() override; void giveFocus() override; KAboutData * aboutData() override; ModuleView * moduleView() const 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); 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