File indexing completed on 2024-04-21 16:22:52

0001 /*
0002  * SPDX-FileCopyrightText: 2009 Ben Cooksley <bcooksley@kde.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "IconMode.h"
0008 #include "CategorizedView.h"
0009 #include "CategoryDrawer.h"
0010 
0011 #include "MenuItem.h"
0012 #include "MenuModel.h"
0013 #include "MenuProxyModel.h"
0014 #include "ModuleView.h"
0015 
0016 #include <QStackedWidget>
0017 
0018 #include <KAboutData>
0019 #include <KFileItemDelegate>
0020 #include <KLocalizedString>
0021 #include <KPluginFactory>
0022 #include <KStandardAction>
0023 #include <QAction>
0024 
0025 K_PLUGIN_CLASS_WITH_JSON(IconMode, "settings-icon-view.json")
0026 
0027 class IconMode::Private
0028 {
0029 public:
0030     Private()
0031     {
0032     }
0033 
0034     virtual ~Private()
0035     {
0036         delete aboutIcon;
0037     }
0038 
0039     KCategoryDrawer *categoryDrawer = nullptr;
0040     KCategorizedView *categoryView = nullptr;
0041     QStackedWidget *mainWidget = nullptr;
0042     MenuModel *model = nullptr;
0043     MenuProxyModel *proxyModel = nullptr;
0044     KAboutData *aboutIcon = nullptr;
0045     ModuleView *moduleView = nullptr;
0046     QAction *backAction = nullptr;
0047 };
0048 
0049 IconMode::IconMode(QObject *parent, const QVariantList &args)
0050     : BaseMode(parent, args)
0051     , d(new Private())
0052 {
0053     d->aboutIcon = new KAboutData(QStringLiteral("IconView"),
0054                                   i18n("Icon View"),
0055                                   QStringLiteral("1.0"),
0056                                   i18n("Provides a categorized icons view of control modules."),
0057                                   KAboutLicense::GPL,
0058                                   i18n("(c) 2009, Ben Cooksley"));
0059     d->aboutIcon->addAuthor(i18n("Ben Cooksley"), i18n("Author"), QStringLiteral("bcooksley@kde.org"));
0060     d->aboutIcon->addAuthor(i18n("Mathias Soeken"), i18n("Developer"), QStringLiteral("msoeken@informatik.uni-bremen.de"));
0061 
0062     d->backAction = KStandardAction::back(this, SLOT(backToOverview()), this);
0063     d->backAction->setText(i18n("All Settings"));
0064     d->backAction->setToolTip(i18n("Keyboard Shortcut: %1", d->backAction->shortcut().toString(QKeySequence::NativeText)));
0065     d->backAction->setEnabled(false);
0066     actionsList() << d->backAction;
0067 }
0068 
0069 IconMode::~IconMode()
0070 {
0071     delete d;
0072 }
0073 
0074 KAboutData *IconMode::aboutData()
0075 {
0076     return d->aboutIcon;
0077 }
0078 
0079 ModuleView *IconMode::moduleView() const
0080 {
0081     return d->moduleView;
0082 }
0083 
0084 QWidget *IconMode::mainWidget()
0085 {
0086     if (!d->categoryView) {
0087         initWidget();
0088     }
0089     return d->mainWidget;
0090 }
0091 
0092 QList<QAbstractItemView *> IconMode::views() const
0093 {
0094     QList<QAbstractItemView *> list;
0095     list.append(d->categoryView);
0096     return list;
0097 }
0098 
0099 void IconMode::initEvent()
0100 {
0101     d->model = new MenuModel(rootItem(), this);
0102     foreach (MenuItem *child, rootItem()->children()) {
0103         d->model->addException(child);
0104     }
0105 
0106     d->proxyModel = new MenuProxyModel(this);
0107     d->proxyModel->setCategorizedModel(true);
0108     d->proxyModel->setSourceModel(d->model);
0109     d->proxyModel->sort(0);
0110 
0111     d->mainWidget = new QStackedWidget();
0112     d->moduleView = new ModuleView(d->mainWidget);
0113     connect(d->moduleView, &ModuleView::moduleChanged, this, &IconMode::moduleLoaded);
0114     connect(d->moduleView, &ModuleView::closeRequest, this, &IconMode::backToOverview);
0115     d->categoryView = nullptr;
0116 }
0117 
0118 void IconMode::searchChanged(const QString &text)
0119 {
0120 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0121     d->proxyModel->setFilterRegExp(text);
0122 #else
0123     d->proxyModel->setFilterRegularExpression(text);
0124 #endif
0125     if (d->categoryView) {
0126         QAbstractItemModel *model = d->categoryView->model();
0127         const int column = d->categoryView->modelColumn();
0128         const QModelIndex root = d->categoryView->rootIndex();
0129         for (int i = 0; i < model->rowCount(); ++i) {
0130             const QModelIndex index = model->index(i, column, root);
0131             if (model->flags(index) & Qt::ItemIsEnabled) {
0132                 d->categoryView->scrollTo(index);
0133                 break;
0134             }
0135         }
0136     }
0137 }
0138 
0139 void IconMode::changeModule(const QModelIndex &activeModule)
0140 {
0141     // Already loaded?
0142     MenuItem *item = activeModule.data(Qt::UserRole).value<MenuItem *>();
0143     if (d->moduleView->activeModuleName() == item->name())
0144         return;
0145 
0146     changeModuleWithArgs(activeModule, QStringList());
0147 }
0148 
0149 void IconMode::changeModuleWithArgs(const QModelIndex &activeModule, const QStringList &args)
0150 {
0151     // Always reopen because args might have changed
0152     d->moduleView->closeModules();
0153     d->mainWidget->setCurrentWidget(d->moduleView);
0154 
0155     const QModelIndex categoryIndex = activeModule.parent();
0156     const bool hasCategory = categoryIndex.isValid();
0157     const bool hasChildren = d->categoryView->model()->rowCount(activeModule) > 1;
0158 
0159     // We have no children or any other items in the category, so we load a single KCM
0160     if (!hasCategory && !hasChildren) {
0161         d->moduleView->setFaceType(KPageView::Plain);
0162         d->moduleView->loadModule(activeModule, args);
0163         return;
0164     }
0165 
0166     d->moduleView->setFaceType(KPageView::List);
0167 
0168     // In the case of Appearance -> GlobalTheme we will load its children items
0169     // Otherwise, load the modules that belong into that category
0170     const QModelIndex rootIndex = hasCategory ? categoryIndex : activeModule;
0171 
0172     for (int row = 0; row < d->categoryView->model()->rowCount(rootIndex); row++) {
0173         const QModelIndex subpageItem = d->categoryView->model()->index(row, 0, rootIndex);
0174         const QStringList &effectiveArgs = activeModule.row() == row ? args : QStringList(); // Only set args for the active module
0175 
0176         d->moduleView->loadModule(subpageItem, effectiveArgs);
0177     }
0178 
0179     MenuItem *item = activeModule.data(Qt::UserRole).value<MenuItem *>();
0180     if (item) {
0181         d->moduleView->setActiveModule(item->name());
0182     }
0183 }
0184 
0185 void IconMode::moduleLoaded()
0186 {
0187     d->backAction->setEnabled(true);
0188     Q_EMIT changeToolBarItems(BaseMode::NoItems);
0189 }
0190 
0191 void IconMode::backToOverview()
0192 {
0193     if (d->moduleView->resolveChanges()) {
0194         d->mainWidget->setCurrentWidget(d->categoryView);
0195         d->moduleView->closeModules();
0196         d->backAction->setEnabled(false);
0197         Q_EMIT changeToolBarItems(BaseMode::Search | BaseMode::Configure | BaseMode::Quit);
0198         Q_EMIT viewChanged(false);
0199     }
0200 }
0201 
0202 void IconMode::initWidget()
0203 {
0204     // Create the widget
0205     d->categoryView = new CategorizedView(d->mainWidget);
0206     d->categoryDrawer = new CategoryDrawer(d->categoryView);
0207 
0208     d->categoryView->setSelectionMode(QAbstractItemView::SingleSelection);
0209     // PORT QT5 d->categoryView->setSpacing( KDialog::spacingHint() );
0210     d->categoryView->setCategoryDrawer(d->categoryDrawer);
0211     d->categoryView->setViewMode(QListView::IconMode);
0212     d->categoryView->setMouseTracking(true);
0213     d->categoryView->viewport()->setAttribute(Qt::WA_Hover);
0214 
0215     KFileItemDelegate *delegate = new KFileItemDelegate(d->categoryView);
0216     delegate->setWrapMode(QTextOption::WordWrap);
0217     d->categoryView->setItemDelegate(delegate);
0218 
0219     d->categoryView->setFrameShape(QFrame::NoFrame);
0220     d->categoryView->setModel(d->proxyModel);
0221     connect(d->categoryView, &QAbstractItemView::activated, this, &IconMode::changeModule);
0222 
0223     d->mainWidget->addWidget(d->categoryView);
0224     d->mainWidget->addWidget(d->moduleView);
0225     d->mainWidget->setCurrentWidget(d->categoryView);
0226     Q_EMIT changeToolBarItems(BaseMode::Search | BaseMode::Configure | BaseMode::Quit);
0227     d->mainWidget->installEventFilter(this);
0228 
0229     if (!startupModule().isEmpty()) {
0230         MenuItem *item = rootItem()->descendantForModule(startupModule());
0231         if (item) {
0232             changeModuleWithArgs(d->proxyModel->mapFromSource(d->model->indexForItem(item)), startupModuleArgs());
0233         }
0234     }
0235 }
0236 
0237 void IconMode::reloadStartupModule()
0238 {
0239     if (!startupModule().isEmpty()) {
0240         MenuItem *item = rootItem()->descendantForModule(startupModule());
0241         if (item) {
0242             changeModuleWithArgs(d->proxyModel->mapFromSource(d->model->indexForItem(item)), startupModuleArgs());
0243         }
0244     }
0245 }
0246 
0247 bool IconMode::eventFilter(QObject *watched, QEvent *event)
0248 {
0249     if (watched == d->mainWidget && event->type() == QEvent::Show) {
0250         Q_EMIT changeToolBarItems(BaseMode::Search | BaseMode::Configure | BaseMode::Quit);
0251     }
0252     return BaseMode::eventFilter(watched, event);
0253 }
0254 
0255 void IconMode::leaveModuleView()
0256 {
0257     d->moduleView->closeModules(); // We have to force it here
0258     backToOverview();
0259 }
0260 
0261 void IconMode::giveFocus()
0262 {
0263     d->categoryView->setFocus();
0264 }
0265 
0266 #include "IconMode.moc"