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"