ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvPluginInfoDlg.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <QDebug>
9 #include <QDir>
10 #include <QSortFilterProxyModel>
11 #include <QStandardItemModel>
12 
13 // CV_CORE_LIB
14 #include <CVTools.h>
15 
16 #include "ecvPluginInfoDlg.h"
17 #include "ecvPluginManager.h"
18 #include "ecvStdPluginInterface.h"
19 #include "ui_ecvPluginInfoDlg.h"
20 
21 static QString sFormatReferenceList(
23  const QString linkFormat(
24  " <a href=\"%1\" style=\"text-decoration:none\">&#x1F517;</a>");
25  QString formattedText;
26  int referenceNum = 1;
27 
28  for (const ccPluginInterface::Reference &reference : list) {
29  formattedText +=
30  QStringLiteral("%1. ").arg(QString::number(referenceNum++));
31 
32  formattedText += reference.article;
33 
34  if (!reference.url.isEmpty()) {
35  formattedText += linkFormat.arg(reference.url);
36  }
37 
38  formattedText += QStringLiteral("<br/>");
39  }
40 
41  return formattedText;
42 }
43 
45  const QString &pluginName) {
46  const QString emailFormat(
47  "&lt;<a href=\"mailto:%1?Subject=CLOUDVIEWER %2\">%1</a>&gt;");
48  QString formattedText;
49 
50  for (const ccPluginInterface::Contact &contact : list) {
51  formattedText += contact.name;
52 
53  if (!contact.email.isEmpty()) {
54  formattedText += emailFormat.arg(contact.email, pluginName);
55  }
56 
57  formattedText += QStringLiteral("<br/>");
58  }
59 
60  return formattedText;
61 }
62 
63 namespace {
64 class _Icons {
65 public:
66  static QIcon sGetIcon(CC_PLUGIN_TYPE inPluginType) {
67  if (sIconMap.empty()) {
68  _init();
69  }
70 
71  return sIconMap[inPluginType];
72  }
73 
74 private:
75  static void _init() {
76  if (!sIconMap.empty()) {
77  return;
78  }
79 
80  sIconMap[ECV_STD_PLUGIN] =
81  QIcon(":/CC/pluginManager/images/std_plugin.png");
82  sIconMap[ECV_PCL_ALGORITHM_PLUGIN] =
83  QIcon(":/CC/pluginManager/images/algorithm_plugin.png");
84  sIconMap[ECV_IO_FILTER_PLUGIN] =
85  QIcon(":/CC/pluginManager/images/io_plugin.png");
86  }
87 
88  static QMap<CC_PLUGIN_TYPE, QIcon> sIconMap;
89 };
90 
91 QMap<CC_PLUGIN_TYPE, QIcon> _Icons::sIconMap;
92 } // namespace
93 
95  : QDialog(parent),
96  m_UI(new Ui::ccPluginInfoDlg),
97  m_ProxyModel(new QSortFilterProxyModel(this)),
98  m_ItemModel(new QStandardItemModel(this)) {
99  m_UI->setupUi(this);
100 
101  setWindowTitle(tr("About Plugins"));
102 
103  m_UI->mWarningLabel->setText(tr("Enabling/disabling plugins will take "
104  "effect next time you run %1")
105  .arg(QApplication::applicationName()));
106  m_UI->mWarningLabel->setStyleSheet(QStringLiteral(
107  "QLabel { background-color : #FFFF99; color : black; }"));
108  m_UI->mWarningLabel->hide();
109 
110  m_UI->mSearchLineEdit->setStyleSheet(
111  "QLineEdit, QLineEdit:focus { border: none; }");
112  m_UI->mSearchLineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
113 
114  m_ProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
115  m_ProxyModel->setSourceModel(m_ItemModel);
116 
117  m_UI->mPluginListView->setModel(m_ProxyModel);
118  m_UI->mPluginListView->setFocus();
119 
120  connect(m_UI->mSearchLineEdit, &QLineEdit::textEdited, m_ProxyModel,
121  &QSortFilterProxyModel::setFilterFixedString);
122  connect(m_UI->mPluginListView->selectionModel(),
123  &QItemSelectionModel::currentChanged, this,
124  &ccPluginInfoDlg::selectionChanged);
125 }
126 
128 
129 void ccPluginInfoDlg::setPluginPaths(const QStringList &pluginPaths) {
130  QString paths;
131 
132  for (const QString &path : pluginPaths) {
133  paths += QDir::toNativeSeparators(path);
134  paths += QStringLiteral("\n");
135  }
136 
137  m_UI->mPluginPathTextEdit->setText(paths);
138 }
139 
141  const QList<ccPluginInterface *> &pluginList) {
142  m_ItemModel->clear();
143  m_ItemModel->setRowCount(pluginList.count());
144  m_ItemModel->setColumnCount(1);
145 
146  int row = 0;
147 
148  for (const ccPluginInterface *plugin : pluginList) {
149  auto name = plugin->getName();
150  auto tooltip = tr("%1 Plugin").arg(plugin->getName());
151 
152  if (plugin->isCore()) {
153  tooltip += tr(" (core)");
154  } else {
155  name += " 👽";
156  tooltip += tr(" (3rd Party)");
157  }
158 
159  QStandardItem *item = new QStandardItem(name);
160 
161  item->setCheckable(true);
162 
163  if (ccPluginManager::get().isEnabled(plugin)) {
164  item->setCheckState(Qt::Checked);
165  }
166 
167  item->setData(QVariant::fromValue(plugin), PLUGIN_PTR);
168  item->setIcon(_Icons::sGetIcon(plugin->getType()));
169  item->setToolTip(tooltip);
170 
171  m_ItemModel->setItem(row, 0, item);
172 
173  ++row;
174  }
175 
176  if (!pluginList.empty()) {
177  m_ItemModel->sort(0);
178 
179  QModelIndex index = m_ItemModel->index(0, 0);
180 
181  m_UI->mPluginListView->setCurrentIndex(index);
182  }
183 
184  connect(m_ItemModel, &QStandardItemModel::itemChanged, this,
185  &ccPluginInfoDlg::itemChanged);
186 }
187 
188 const ccPluginInterface *ccPluginInfoDlg::pluginFromItemData(
189  const QStandardItem *item) const {
190  return item->data(PLUGIN_PTR).value<const ccPluginInterface *>();
191  ;
192 }
193 
194 void ccPluginInfoDlg::selectionChanged(const QModelIndex &current,
195  const QModelIndex &previous) {
196  Q_UNUSED(previous);
197 
198  auto sourceItem = m_ProxyModel->mapToSource(current);
199  auto item = m_ItemModel->itemFromIndex(sourceItem);
200 
201  if (item == nullptr) {
202  // This happens if we are filtering and there are no results
203  updatePluginInfo(nullptr);
204  return;
205  }
206 
207  auto plugin = pluginFromItemData(item);
208 
209  updatePluginInfo(plugin);
210 }
211 
212 void ccPluginInfoDlg::itemChanged(QStandardItem *item) {
213  bool checked = item->checkState() == Qt::Checked;
214  auto plugin = pluginFromItemData(item);
215 
216  if (plugin != nullptr) {
217  ccPluginManager::get().setPluginEnabled(plugin, checked);
218 
219  if (m_UI->mWarningLabel->isHidden()) {
220  CVLog::Warning(m_UI->mWarningLabel->text());
221 
222  m_UI->mWarningLabel->show();
223  }
224  }
225 }
226 
227 void ccPluginInfoDlg::updatePluginInfo(const ccPluginInterface *plugin) {
228  if (plugin == nullptr) {
229  m_UI->mIcon->setPixmap(QPixmap());
230  m_UI->mNameLabel->setText(tr("(No plugin selected)"));
231  m_UI->mDescriptionTextEdit->clear();
232  m_UI->mReferencesTextBrowser->clear();
233  m_UI->mAuthorsTextBrowser->clear();
234  m_UI->mMaintainerTextBrowser->clear();
235  return;
236  }
237 
238  const QSize iconSize(64, 64);
239 
240  QPixmap iconPixmap;
241 
242  if (!plugin->getIcon().isNull()) {
243  iconPixmap = plugin->getIcon().pixmap(iconSize);
244  }
245 
246  switch (plugin->getType()) {
247  case ECV_STD_PLUGIN: {
248  if (iconPixmap.isNull()) {
249  iconPixmap = QPixmap(":/CC/pluginManager/images/std_plugin.png")
250  .scaled(iconSize);
251  }
252 
253  m_UI->mPluginTypeLabel->clear();
254  break;
255  }
256 
258  if (iconPixmap.isNull()) {
259  iconPixmap = QPixmap(":/CC/pluginManager/images/"
260  "algorithm_plugin.png")
261  .scaled(iconSize);
262  }
263 
264  m_UI->mPluginTypeLabel->setText(tr("PCL Algorithm"));
265  break;
266  }
267 
268  case ECV_IO_FILTER_PLUGIN: {
269  if (iconPixmap.isNull()) {
270  iconPixmap = QPixmap(":/CC/pluginManager/images/io_plugin.png")
271  .scaled(iconSize);
272  }
273 
274  m_UI->mPluginTypeLabel->setText(tr("I/O"));
275  break;
276  }
277  }
278 
279  m_UI->mIcon->setPixmap(iconPixmap);
280 
281  m_UI->mNameLabel->setText(plugin->getName());
282  m_UI->mDescriptionTextEdit->setHtml(plugin->getDescription());
283 
284  const QString referenceText = sFormatReferenceList(plugin->getReferences());
285 
286  if (!referenceText.isEmpty()) {
287  m_UI->mReferencesTextBrowser->setHtml(referenceText);
288  m_UI->mReferencesLabel->show();
289  m_UI->mReferencesTextBrowser->show();
290  } else {
291  m_UI->mReferencesLabel->hide();
292  m_UI->mReferencesTextBrowser->hide();
293  m_UI->mReferencesTextBrowser->clear();
294  }
295 
296  const QString authorsText =
297  sFormatContactList(plugin->getAuthors(), plugin->getName());
298 
299  if (!authorsText.isEmpty()) {
300  m_UI->mAuthorsTextBrowser->setHtml(authorsText);
301  } else {
302  m_UI->mAuthorsTextBrowser->clear();
303  }
304 
305  const QString maintainersText =
306  sFormatContactList(plugin->getMaintainers(), plugin->getName());
307 
308  if (!maintainersText.isEmpty()) {
309  m_UI->mMaintainerTextBrowser->setHtml(maintainersText);
310  } else {
311  m_UI->mMaintainerTextBrowser->clear();
312  }
313 }
std::string name
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
ccPluginInfoDlg(QWidget *parent=nullptr)
void setPluginPaths(const QStringList &pluginPaths)
void setPluginList(const QList< ccPluginInterface * > &pluginList)
Standard ECV plugin interface.
virtual QString getDescription() const =0
Returns long name/description (for tooltip, etc.)
QList< Reference > ReferenceList
virtual CC_PLUGIN_TYPE getType() const =0
Returns plugin type (standard or OpenGL filter)
virtual ContactList getAuthors() const
virtual ContactList getMaintainers() const
virtual ReferenceList getReferences() const
virtual QIcon getIcon() const
Returns icon.
virtual QString getName() const =0
Returns (short) name (for menu entry, etc.)
QList< Contact > ContactList
void setPluginEnabled(const ccPluginInterface *plugin, bool enabled)
static ccPluginManager & get()
static QString sFormatReferenceList(const ccPluginInterface::ReferenceList &list)
static QString sFormatContactList(const ccPluginInterface::ContactList &list, const QString &pluginName)
CC_PLUGIN_TYPE
Plugin type.
@ ECV_STD_PLUGIN
@ ECV_IO_FILTER_PLUGIN
@ ECV_PCL_ALGORITHM_PLUGIN
constexpr QRegularExpression::PatternOption CaseInsensitive
Definition: QtCompat.h:174
static const std::string path
Definition: PointCloud.cpp:59