File indexing completed on 2024-04-28 04:37:19

0001 /*
0002     SPDX-FileCopyrightText: 2009 Andreas Pakulat <apaku@gmx.de>
0003     SPDX-FileCopyrightText: 2010 Niko Sams <niko.sams@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "loadedpluginsdialog.h"
0009 
0010 #include <QApplication>
0011 #include <QDialogButtonBox>
0012 #include <QListView>
0013 #include <QPainter>
0014 #include <QPushButton>
0015 #include <QVBoxLayout>
0016 
0017 #include <KAboutData>
0018 #include <KAboutPluginDialog>
0019 #include <KLocalizedString>
0020 #include <KTitleWidget>
0021 #include <KWidgetItemDelegate>
0022 
0023 #include <util/scopeddialog.h>
0024 
0025 #include "core.h"
0026 #include "plugincontroller.h"
0027 
0028 #define MARGIN 5
0029 
0030 namespace {
0031 
0032 KPluginMetaData pluginInfo(KDevelop::IPlugin* plugin)
0033 {
0034     return KDevelop::Core::self()->pluginControllerInternal()->pluginInfo(plugin);
0035 }
0036 
0037 QString displayName(KDevelop::IPlugin* plugin)
0038 {
0039     const auto name = pluginInfo(plugin).name();
0040     return !name.isEmpty() ? name : plugin->componentName();
0041 }
0042 
0043 bool sortPlugins(KDevelop::IPlugin* l, KDevelop::IPlugin* r)
0044 {
0045     return displayName(l) < displayName(r);
0046 }
0047 
0048 }
0049 
0050 class PluginsModel : public QAbstractListModel
0051 {
0052     Q_OBJECT
0053 public:
0054     enum ExtraRoles {
0055         DescriptionRole = Qt::UserRole+1
0056     };
0057     explicit PluginsModel(QObject* parent = nullptr)
0058         : QAbstractListModel(parent)
0059     {
0060         m_plugins = KDevelop::Core::self()->pluginControllerInternal()->loadedPlugins();
0061         std::sort(m_plugins.begin(), m_plugins.end(), sortPlugins);
0062     }
0063 
0064     KDevelop::IPlugin *pluginForIndex(const QModelIndex& index) const
0065     {
0066         if (!index.isValid()) return nullptr;
0067         if (index.parent().isValid()) return nullptr;
0068         if (index.column() != 0) return nullptr;
0069         if (index.row() >= m_plugins.count()) return nullptr;
0070         return m_plugins[index.row()];
0071     }
0072 
0073     QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
0074     {
0075         KDevelop::IPlugin* plugin = pluginForIndex(index);
0076         if (!plugin)
0077             return QVariant();
0078 
0079         switch (role) {
0080         case Qt::DisplayRole:
0081             return displayName(plugin);
0082         case DescriptionRole:
0083             return pluginInfo(plugin).description();
0084         case Qt::DecorationRole: {
0085                 const QString iconName = pluginInfo(plugin).iconName();
0086                 if (iconName.isEmpty()) {
0087                     return QStringLiteral("kdevelop");
0088                 }
0089                 return iconName;
0090             }
0091         default:
0092             return QVariant();
0093         };
0094     }
0095 
0096     int rowCount(const QModelIndex& parent = QModelIndex()) const override
0097     {
0098         if (!parent.isValid()) {
0099             return m_plugins.count();
0100         }
0101         return 0;
0102     }
0103 
0104 private:
0105     QList<KDevelop::IPlugin*> m_plugins;
0106 };
0107 
0108 class LoadedPluginsDelegate : public KWidgetItemDelegate
0109 {
0110     Q_OBJECT
0111 
0112 public:
0113 
0114     explicit LoadedPluginsDelegate(QAbstractItemView *itemView, QObject *parent = nullptr)
0115         : KWidgetItemDelegate(itemView, parent)
0116         , pushButton(new QPushButton)
0117     {
0118         pushButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information"))); // only for getting size matters
0119     }
0120 
0121     ~LoadedPluginsDelegate() override
0122     {
0123         delete pushButton;
0124     }
0125 
0126     QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
0127     {
0128         Q_UNUSED(index);
0129 
0130         int i = 5;
0131         int j = 1;
0132 
0133         const int iconSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
0134         QFont font = titleFont(option.font);
0135         QFontMetrics fmTitle(font);
0136 
0137         return QSize(
0138             iconSize + MARGIN * i + pushButton->sizeHint().width() * j,
0139             qMax(iconSize + MARGIN * 2, fmTitle.height() + option.fontMetrics.height() + MARGIN * 2)
0140         );
0141 
0142     }
0143 
0144     void paint(QPainter *painter, const QStyleOptionViewItem &option,
0145                const QModelIndex &index) const override
0146     {
0147         if (!index.isValid()) {
0148             return;
0149         }
0150 
0151         painter->save();
0152 
0153         QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr);
0154 
0155         int iconSize = option.rect.height() - MARGIN * 2;
0156         QIcon icon = QIcon::fromTheme(index.model()->data(index, Qt::DecorationRole).toString());
0157         icon.paint(painter, QRect(dependantLayoutValue(MARGIN + option.rect.left(), iconSize, option.rect.width()), MARGIN + option.rect.top(), iconSize, iconSize));
0158         QRect contentsRect(dependantLayoutValue(MARGIN * 2 + iconSize + option.rect.left(), option.rect.width() - MARGIN * 3 - iconSize, option.rect.width()), MARGIN + option.rect.top(), option.rect.width() - MARGIN * 3 - iconSize, option.rect.height() - MARGIN * 2);
0159 
0160         int lessHorizontalSpace = MARGIN * 2 + pushButton->sizeHint().width();
0161 
0162         contentsRect.setWidth(contentsRect.width() - lessHorizontalSpace);
0163 
0164         if (option.state & QStyle::State_Selected) {
0165             painter->setPen(option.palette.highlightedText().color());
0166         }
0167 
0168         if (itemView()->layoutDirection() == Qt::RightToLeft) {
0169             contentsRect.translate(lessHorizontalSpace, 0);
0170         }
0171 
0172         painter->save();
0173 
0174         painter->save();
0175         QFont font = titleFont(option.font);
0176         QFontMetrics fmTitle(font);
0177         painter->setFont(font);
0178         painter->drawText(contentsRect, Qt::AlignLeft | Qt::AlignTop, fmTitle.elidedText(index.model()->data(index, Qt::DisplayRole).toString(), Qt::ElideRight, contentsRect.width()));
0179         painter->restore();
0180 
0181         painter->drawText(contentsRect, Qt::AlignLeft | Qt::AlignBottom, option.fontMetrics.elidedText(index.model()->data(index, PluginsModel::DescriptionRole).toString(), Qt::ElideRight, contentsRect.width()));
0182 
0183         painter->restore();
0184         painter->restore();
0185     }
0186 
0187     QList<QWidget *> createItemWidgets(const QModelIndex &index) const override
0188     {
0189         Q_UNUSED(index);
0190 
0191         auto *button = new QPushButton();
0192         button->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
0193         setBlockedEventTypes(button, QList<QEvent::Type>{QEvent::MouseButtonPress, QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick});
0194 
0195         connect(button, &QPushButton::clicked, this, &LoadedPluginsDelegate::info);
0196         return QList<QWidget*>()
0197             << button;
0198     }
0199 
0200     void updateItemWidgets(const QList<QWidget*> widgets,
0201                            const QStyleOptionViewItem &option,
0202                            const QPersistentModelIndex &index) const override
0203     {
0204         Q_UNUSED(index);
0205         if (widgets.isEmpty()) {
0206             return;
0207         }
0208 
0209         auto *aboutPushButton = static_cast<QPushButton*>(widgets[0]);
0210         QSize aboutPushButtonSizeHint = aboutPushButton->sizeHint();
0211         aboutPushButton->resize(aboutPushButtonSizeHint);
0212         aboutPushButton->move(dependantLayoutValue(option.rect.width() - MARGIN - aboutPushButtonSizeHint.width(), aboutPushButtonSizeHint.width(), option.rect.width()), option.rect.height() / 2 - aboutPushButtonSizeHint.height() / 2);
0213     }
0214 
0215     int dependantLayoutValue(int value, int width, int totalWidth) const
0216     {
0217         if (itemView()->layoutDirection() == Qt::LeftToRight) {
0218             return value;
0219         }
0220         return totalWidth - width - value;
0221     }
0222 
0223     QFont titleFont(const QFont &baseFont) const
0224     {
0225         QFont retFont(baseFont);
0226         retFont.setBold(true);
0227         return retFont;
0228     }
0229 
0230 private Q_SLOTS:
0231     void info()
0232     {
0233         auto *m = static_cast<PluginsModel*>(itemView()->model());
0234         KDevelop::IPlugin *p = m->pluginForIndex(focusedIndex());
0235         if (p) {
0236             const KPluginMetaData pluginInfo = ::pluginInfo(p);
0237             if (!pluginInfo.name().isEmpty()) { // Be sure the about data is not completely empty
0238                 KDevelop::ScopedDialog<KAboutPluginDialog> aboutPlugin(pluginInfo, itemView());
0239                 aboutPlugin->exec();
0240                 return;
0241             }
0242         }
0243     }
0244 private:
0245     QPushButton *pushButton;
0246 };
0247 
0248 class PluginsView : public QListView
0249 {
0250     Q_OBJECT
0251 public:
0252     explicit PluginsView(QWidget* parent = nullptr)
0253         :QListView(parent)
0254     {
0255         setModel(new PluginsModel(this));
0256         setItemDelegate(new LoadedPluginsDelegate(this));
0257         setVerticalScrollMode(QListView::ScrollPerPixel);
0258     }
0259 
0260     ~PluginsView() override
0261     {
0262         // explicitly delete the delegate here since otherwise
0263         // we get spammed by warnings that the QPushButton we return
0264         // in createItemWidgets is deleted before the delegate
0265         // *sigh* - even dfaure says KWidgetItemDelegate is a crude hack
0266         delete itemDelegate();
0267     }
0268 
0269     QSize sizeHint() const override
0270     {
0271         QSize ret = QListView::sizeHint();
0272         ret.setWidth(qMax(ret.width(), sizeHintForColumn(0) + 30));
0273         return ret;
0274     }
0275 };
0276 
0277 LoadedPluginsDialog::LoadedPluginsDialog( QWidget* parent )
0278     : QDialog( parent )
0279 {
0280     setWindowTitle(i18nc("@title:window", "Loaded Plugins"));
0281 
0282     auto* vbox = new QVBoxLayout(this);
0283 
0284     auto* title = new KTitleWidget(this);
0285     title->setIcon(qApp->windowIcon(), KTitleWidget::ImageLeft);
0286     title->setText(i18n("<html><font size=\"4\">Plugins loaded for <b>%1</b></font></html>",
0287                         KAboutData::applicationData().displayName()));
0288     vbox->addWidget(title);
0289     vbox->addWidget(new PluginsView());
0290 
0291     auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
0292     connect(buttonBox, &QDialogButtonBox::accepted, this, &LoadedPluginsDialog::accept);
0293     connect(buttonBox, &QDialogButtonBox::rejected, this, &LoadedPluginsDialog::reject);
0294     buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
0295     vbox->addWidget(buttonBox);
0296 
0297     resize(800, 600);
0298 }
0299 
0300 #include "moc_loadedpluginsdialog.cpp"
0301 #include "loadedpluginsdialog.moc"