HAL  v4.5.0-131-gda71a6012
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
gui_plugin_manager.cpp
Go to the documentation of this file.
2 #include "gui/gui_globals.h"
10 #include "hal_core/utilities/log.h"
11 #include <QMap>
12 #include <QDir>
13 #include <iostream>
14 #include <filesystem>
15 #include <QDateTime>
16 #include <QSettings>
17 #include <QBrush>
18 #include <QApplication>
19 #include <QPainter>
20 #include <QPushButton>
21 #include <QMouseEvent>
22 #include <QVBoxLayout>
23 #include <QHBoxLayout>
24 #include <QHeaderView>
25 #include <QMessageBox>
26 #include <QFileDialog>
27 
28 namespace hal {
29 
30 
32  : QWidget(parent),
33  mDefaultTextColor(Qt::lightGray),
34  mHilightTextColor(Qt::yellow),
35  mHilightBackgroundColor(Qt::black)
36  {
37  QVBoxLayout* layout = new QVBoxLayout(this);
38 
39  mGuiPluginView = new GuiPluginView(this);
40  mGuiPluginTable = new GuiPluginTable(this);
41  connect(mGuiPluginTable,&GuiPluginTable::toggleEnableGuiContribution,this,&GuiPluginManager::handleToggleGuiContribution);
42  connect(mGuiPluginTable,&GuiPluginTable::showCliOptions,this,&GuiPluginManager::handleShowCliOptions);
45  gPluginRelay->mGuiPluginTable = mGuiPluginTable;
46  mGuiPluginView->setModel(mGuiPluginTable);
47 
48  mGuiPluginDelegate = new GuiPluginDelegate(this);
50  mGuiPluginView->setItemDelegateForColumn(10,mGuiPluginDelegate);
51  mGuiPluginView->setItemDelegateForColumn(8,mGuiPluginDelegate);
52  mGuiPluginView->setItemDelegateForColumn(7,mGuiPluginDelegate);
56  layout->addWidget(mGuiPluginView,Qt::AlignLeft);
57 
58  QLabel* legendHeader = new QLabel("Buttons used in table above:", this);
59  legendHeader->setFixedHeight(48);
60  layout->addWidget(legendHeader,Qt::AlignLeft);
61  QLabel* ltext[4] = {
62  new QLabel("Load HAL plugin and dependencies (if any)", this),
63  new QLabel("Unload HAL plugin unless needed as dependency by other plugin", this),
64  new QLabel("This plugin contributes CLI options. Click button for description of options.", this),
65  new QLabel("Enable or disable plugin contributions to GUI context menu.", this)};
66 
67  for (int i=0; i<4; i++)
68  {
69  QHBoxLayout* legend = new QHBoxLayout;
70  mIconLegend[i] = new QLabel(this);
71  mIconLegend[i]->setAlignment(Qt::AlignCenter);
72  mIconLegend[i]->setFixedSize(52,52);
73  legend->addWidget(mIconLegend[i]);
74  ltext[i]->setIndent(8);
75  legend->addWidget(ltext[i]);
76  layout->addLayout(legend);
77  }
78 
79  QHBoxLayout* buttonLayout = new QHBoxLayout;
80  buttonLayout->setAlignment(Qt::AlignRight);
81  QPushButton* butCancel = new QPushButton("Back to netlist", this);
83  buttonLayout->addWidget(butCancel);
84  QPushButton* butRefresh = new QPushButton("Refresh", this);
85  connect(butRefresh,&QPushButton::clicked,mGuiPluginTable,&GuiPluginTable::handleRefresh);
86  buttonLayout->addWidget(butRefresh);
87  layout->addLayout(buttonLayout);
88  }
89 
91  {
92  bool hasEntries = false;
94  for(auto it = guiExtensionMap.constBegin(); it!= guiExtensionMap.constEnd(); ++it)
95  {
96  GuiExtensionInterface* geif = it.value();
97 
98  if (!geif->is_contribution_enabled())
99  continue;
100 
101  std::vector<PluginParameter> plugParams = geif->get_parameter();
102  if (plugParams.empty())
103  continue;
104 
106  if (label.isEmpty()) label = it.key();
107  menu->addAction(label, [label,geif,menu]() { PluginParameterDialog ppd(label,geif,menu); ppd.exec(); });
108  hasEntries = true;
109  }
110 
111  menu->setEnabled(hasEntries);
112  }
113 
114 
116  {
117  QStyle* s = style();
118 
119  s->unpolish(this);
120  s->polish(this);
121  mGuiPluginDelegate->updateQss(this);
122  QIcon tmpIcon;
123  for (int i=0; i<4; i++)
124  {
125  switch (i)
126  {
127  case 0: tmpIcon = gui_utility::getStyledSvgIcon(loadIconStyle(),loadIconPath()); break;
128  case 1: tmpIcon = gui_utility::getStyledSvgIcon(unloadIconStyle(),unloadIconPath()); break;
129  case 2: tmpIcon = gui_utility::getStyledSvgIcon(cliIconStyle(),cliIconPath()); break;
131  }
132  mIconLegend[i]->setPixmap(tmpIcon.pixmap(32,32));
133  }
134  }
135 
137  {
139  }
140 
142  {
143  QString filename = QFileDialog::getOpenFileName(this, "Load HAL Plugin", ".", "Shared library files (*.so)");
144  if (filename.isEmpty()) return;
145  int irowAdded = mGuiPluginTable->addExternalPlugin(filename);
146  if (irowAdded >= 0)
147  std::cerr << "library file opened <" << filename.toStdString() << ">" << std::endl;
148  else
149  std::cerr << "failed to load library file <" << filename.toStdString() << ">" << std::endl;
150  }
151 
152 
153  void GuiPluginManager::handleToggleGuiContribution(const QString &pluginName)
154  {
156  GuiExtensionInterface* geif = getGuiExtensions().value(pluginName);
157  if (geif)
158  {
161  }
162  mGuiPluginTable->setGuiExtensionState(pluginName, newState);
163  }
164 
165  void GuiPluginManager::handleShowCliOptions(const QString &pluginName, const QString &cliOptions)
166  {
167  QMessageBox* msg = new QMessageBox(QMessageBox::NoIcon, "CLI Options for "+pluginName, cliOptions);
168  msg->setStyleSheet("QLabel{min-width: 800px;}");
169  msg->exec();
170  }
171 
173  {
174  return mLoadIconPath;
175  }
176 
178  {
179  return mLoadIconStyle;
180  }
181 
183  {
184  return mUnloadIconPath;
185  }
186 
188  {
189  return mUnloadIconStyle;
190  }
191 
193  {
194  return mCliIconPath;
195  }
196 
198  {
199  return mCliIconStyle;
200  }
201 
203  {
204  return mGuiIconEnabledPath;
205  }
206 
208  {
209  return mGuiIconDisabledPath;
210  }
211 
213  {
214  return mGuiIconEnabledStyle;
215  }
216 
218  {
219  return mGuiIconDisabledStyle;
220  }
221 
223  {
224  return mDefaultTextColor;
225  }
226 
228  {
229  return mHilightTextColor;
230  }
231 
233  {
234  return mHilightBackgroundColor;
235  }
236 
238  {
239  mLoadIconPath = s;
240  }
241 
243  {
244  mLoadIconStyle = s;
245  }
246 
248  {
249  mUnloadIconPath = s;
250  }
251 
253  {
254  mUnloadIconStyle = s;
255  }
256 
258  {
259  mCliIconPath = s;
260  }
261 
263  {
264  mCliIconStyle = s;
265  }
266 
268  {
269  mGuiIconEnabledPath = s;
270  }
271 
273  {
274  mGuiIconDisabledPath = s;
275  }
276 
278  {
279  mGuiIconEnabledStyle = s;
280  }
281 
283  {
284  mGuiIconDisabledStyle = s;
285  }
286 
288  {
289  mDefaultTextColor = c;
290  }
291 
293  {
294  mHilightTextColor = c;
295  }
296 
298  {
299  mHilightBackgroundColor = c;
300  }
301 
302  void GuiPluginManager::addPluginSubmenus(QMenu* contextMenu, Netlist* netlist,
303  const std::vector<u32>& modules,
304  const std::vector<u32>& gates,
305  const std::vector<u32>& nets)
306  {
307  bool addedSeparator = false;
308 
310  for(auto it = guiExtensionMap.constBegin(); it!= guiExtensionMap.constEnd(); ++it)
311  {
312  GuiExtensionInterface* geif = it.value();
313  geif->netlist_loaded(netlist);
314 
315  if (!geif->is_contribution_enabled())
316  continue;
317 
318  auto contribution = geif->get_context_contribution(netlist, modules, gates, nets);
319  if(contribution.size() <= 0)
320  continue;
321 
322  if(!addedSeparator)
323  {
324  contextMenu->addSeparator();
325  contextMenu->addAction("Plugin actions:")->setEnabled(false);
326  addedSeparator = true;
327  }
328 
329  QString subMenuEntry = QString::fromStdString(geif->contribution_top_label());
330  QMenu *subMenu = contextMenu->addMenu(" " + (subMenuEntry.isEmpty() ? it.key() : subMenuEntry));
331  for (ContextMenuContribution cmc : contribution)
332  {
333  QAction *act = subMenu->addAction(QString::fromStdString(cmc.mEntry));
334  connect(act,&QAction::triggered,contextMenu,[cmc, netlist, modules, gates, nets]()
335  {cmc.mContributer->execute_function(cmc.mTagname, netlist, modules, gates, nets);}
336  );
337  }
338  }
339  }
340 
341  //_________________VIEW_______________________________
343  : QTableView(parent)
344  {
346  }
347 
348  //_________________TABLE______________________________
350  : QAbstractTableModel(parent), mPluginMgr(parent)
351  {
352  mWaitForRefresh = true;
353  populateTable(false);
354  mWaitForRefresh = false;
355  }
356 
358  {
359  clearMemory();
360  }
361 
363  {
364  if (mWaitForRefresh) return;
365 
366  beginResetModel();
367  mWaitForRefresh = true;
368  clearMemory();
369  populateTable(true);
370  mWaitForRefresh = false;
371  endResetModel();
372  }
373 
374  void GuiPluginTable::populateTable(bool refresh)
375  {
377  QMap<QString,GuiPluginEntry*> pluginEntries;
378  mSettings = new QSettings(userConfigDir.absoluteFilePath("plugins.ini"),QSettings::IniFormat);
379 
380  std::set<std::string> loadedPlugins = plugin_manager::get_plugin_names();
381 
382  int nentry = mSettings->beginReadArray("plugins");
383  for (int i=0; i<nentry; i++)
384  {
385  mSettings->setArrayIndex(i);
386  GuiPluginEntry* gpe = new GuiPluginEntry(mSettings);
387  pluginEntries.insert(gpe->mName,gpe);
388  }
389  mSettings->endArray();
390 
391  for (std::filesystem::path plugDir : utils::get_plugin_directories())
392  {
393  QDir dir(QString::fromStdString(plugDir.string()));
394  if (!dir.exists()) continue;
395  for (QFileInfo info : dir.entryInfoList())
396  {
397  if (!info.isFile()) continue;
398  if (!plugin_manager::has_valid_file_extension(info.absoluteFilePath().toStdString())) continue;
399 
400  QString pluginName = info.baseName();
401  GuiPluginEntry* gpe = pluginEntries.value(pluginName);
402  bool needUpdate = true;
403  if (gpe)
404  {
405  needUpdate = gpe->mFileModified != info.lastModified();
406  gpe->setFileFound(true);
407  }
408  else
409  {
410  gpe = new GuiPluginEntry(info);
411  pluginEntries.insert(pluginName,gpe);
412  }
413 
414  BasePluginInterface* bpif = plugin_manager::get_plugin_instance(pluginName.toStdString(), true, true);
415  if (bpif && !refresh)
416  {
417  // plugin already loaded ?
418  bool alreadyLoaded = loadedPlugins.find(pluginName.toStdString())!=loadedPlugins.end();
419  gpe->updateFromLoaded(bpif, alreadyLoaded);
420  log_debug("gui", "GuiPluginManager: '{}' already loaded.", pluginName.toStdString());
421  continue;
422  }
423  else if (gpe->requestLoad())
424  {
425  // user requested this plugin explicitly in previous session
426  bpif = load(pluginName, info.absoluteFilePath());
427  if (!bpif)
428  {
429  gpe->mLoadState = GuiPluginEntry::NotAPlugin; // however, load failed
430  log_warning("gui", "GuiPluginManager: loading of requested plugin '{}' failed.", pluginName.toStdString());
431  }
432  else
433  {
434  gpe->updateFromLoaded(bpif, true, info.lastModified());
435  log_info("gui", "GuiPluginManager: '{}' loaded upon user request.", pluginName.toStdString());
436  }
437  continue;
438  }
439 
440  if (!needUpdate)
441  {
442  log_debug("gui", "GuiPluginManager: '{}' not modified, no update required.", pluginName.toStdString());
443  // known plugin, load only when needed
444  if (gpe->isPlugin())
445  gpe->mLoadState = GuiPluginEntry::NotLoaded;
446  continue;
447  }
448 
449  std::set<std::string> pluginsBeforeLoad = plugin_manager::get_plugin_names();
450 
451  // plugin updated or not known yet, try dummy load to retrieve information
452  bpif = load(pluginName, info.absoluteFilePath());
453  if (!bpif)
454  {
455  log_warning("gui", "GuiPluginManager: loading of '{}' failed, is it really a HAL plugin?", pluginName.toStdString());
456  gpe->mLoadState = GuiPluginEntry::NotAPlugin;
457  continue;
458  }
459 
460  if (bpif->get_name().empty() || bpif->get_version().empty())
461  {
462  log_warning("gui", "GuiPluginManager: '{}' has empty name or version, plugin ignored.", pluginName.toStdString());
463  gpe->mLoadState = GuiPluginEntry::NotAPlugin;
464  }
465  else
466  {
467  log_info("gui", "GuiPluginManager: '{}' loaded to retrieve information about plugin features.", pluginName.toStdString());
468  gpe->updateFromLoaded(bpif, false, info.lastModified()); // load success
469  plugin_manager::unload(gpe->mName.toStdString()); // unload since plugin was neither requested nor dependency
470  gpe->mLoadState = GuiPluginEntry::NotLoaded;
471  // clean up dependecies
472  std::set<std::string> pluginStateRestored = plugin_manager::get_plugin_names();
473  for (int i=0; i<1000; i++)
474  {
475  auto it = pluginStateRestored.begin();
476  while (it != pluginStateRestored.end())
477  {
478  auto jt = pluginsBeforeLoad.find(*it);
479  if (jt != pluginsBeforeLoad.end()) // was loaded before, ignore
480  it = pluginStateRestored.erase(it);
481  else if (plugin_manager::unload(*it))
482  it = pluginStateRestored.erase(it); // could unload, consider it done
483  else
484  ++it; // deal with next round
485  }
486  if (pluginStateRestored.empty()) break;
487  }
488  for (QString dep : gpe->mDependencies)
489  {
490  // unload dependencies unless they are required from other side
491  if (pluginEntries.contains(dep) && pluginEntries.value(dep)->mLoadState > GuiPluginEntry::NotLoaded) continue;
493  }
494  }
495  }
496  }
497 
498  // update entry if plugin loaded in meantime as dependency
499  for (std::string loadedPluginName: plugin_manager::get_plugin_names())
500  {
501  GuiPluginEntry* gpe = pluginEntries.value(QString::fromStdString(loadedPluginName));
502  if (!gpe || gpe->isLoaded()) continue;
503  BasePluginInterface* bpif = plugin_manager::get_plugin_instance(loadedPluginName,false,true);
504  if (!bpif) continue;
505  QFileInfo info(gpe->mFilePath);
506  gpe->updateFromLoaded(bpif,false,info.exists()?info.lastModified():QDateTime());
507  }
508 
509  mEntries.clear();
510  mAvoid.clear();
511  mLookup.clear();
512  auto it = pluginEntries.begin();
513  while (it != pluginEntries.end())
514  {
515  bool removeEntry = false;
516  GuiPluginEntry* gpe = it.value();
517  if (gpe->isFileFound())
518  {
519  if (gpe->isPlugin())
520  {
521  mLookup.insert(gpe->mName,mEntries.size());
522  mEntries.append(gpe);
523  }
524  else
525  {
526  mAvoid.append(gpe);
527  }
528  }
529  else if (gpe->mExternalPath.isEmpty() || !gpe->requestLoad() )
530  {
531  // plugin does no longer exist or no longer required
532  removeEntry = true;
533  }
534  else
535  {
536  // loaded plugin as binary from user provided path
537  QFileInfo info(gpe->mExternalPath);
538  if (info.exists() && info.isReadable())
539  {
540  mLookup.insert(gpe->mName,mEntries.size());
541  mEntries.append(gpe);
542  BasePluginInterface* bpif = load(gpe->mName, info.absoluteFilePath());
543  if (!bpif)
544  {
545  log_warning("gui", "GuiPluginManager: loading of requested plugin '{}' failed.", gpe->mName.toStdString());
546  removeEntry = true;
547  }
548  else
549  {
550  gpe->updateFromLoaded(bpif, true, info.lastModified());
551  log_info("gui", "GuiPluginManager: '{}' loaded external plugin upon user request.", gpe->mName.toStdString());
552  }
553  }
554  else
555  {
556  removeEntry = true;
557  }
558  }
559  if (removeEntry)
560  it = pluginEntries.erase(it);
561  else
562  ++it;
563  }
564  persist();
565  }
566 
568  {
569  QFileInfo finfo(path);
570  GuiPluginEntry* gpe = new GuiPluginEntry(finfo);
571  int n = mEntries.size();
573  mLookup.insert(gpe->mName,n);
574  mEntries.append(gpe);
575  endInsertRows();
576  BasePluginInterface* bpif = load(finfo.baseName(),path);
577  if (bpif)
578  {
579  gpe->updateFromLoaded(bpif,true);
580  gpe->mExternalPath = path;
582  persist();
583  }
584  else
585  {
587  gpe = mEntries.takeLast();
588  mLookup.remove(gpe->mName);
589  endRemoveRows();
590  delete gpe;
591  n = -1; // return value : no row was inserted
592  }
593  return n;
594  }
595 
596 
597  void GuiPluginTable::handlePluginLoaded(const QString& pluginName, const QString&)
598  {
599  if (mWaitForRefresh) return;
600  changeState(pluginName,GuiPluginEntry::AutoLoad);
601  }
602 
603  void GuiPluginTable::handlePluginUnloaded(const QString& pluginName, const QString&)
604  {
605  if (mWaitForRefresh) return;
606  changeState(pluginName,GuiPluginEntry::NotLoaded);
607  }
608 
609  void GuiPluginTable::changeState(const QString& pluginName, GuiPluginEntry::LoadState state)
610  {
611  int irow = mLookup.value(pluginName,-1);
612  if (irow < 0 || irow >= mEntries.size()) return;
613  GuiPluginEntry* gpe = mEntries.at(irow);
614  gpe->mLoadState = state;
615  if (state >= GuiPluginEntry::AutoLoad)
616  {
619  if (bpif)
620  {
621  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
622  {
623  GuiExtensionInterface* geif = dynamic_cast<GuiExtensionInterface*>(aeif);
624  if (geif)
625  {
626  newGuiExtensionState = gpe->enforceGuiExtensionState(geif);
627  break;
628  }
629  }
630  }
631  gpe->mGuiExtensionState = newGuiExtensionState;
632  }
633  Q_EMIT dataChanged(index(irow,0),index(irow,10));
634  }
635 
637  {
638  SupportedFileFormats retval(ft);
639  for (GuiPluginEntry* gpe : mEntries)
640  {
641  if (gpe->mFeature != ft) continue;
642  for (QString arg : gpe->mFeatureArguments)
643  {
644  retval.insert(arg,gpe->mDescription);
645  }
646  }
647  return retval;
648  }
649 
651  {
652  QMap<QString,QString> pluginMap = *this; // might want to add hal format temporarily
653  if (addHalFormat)
654  pluginMap.insert(".hal", "HAL progress files ");
655 
656  QString reStr = "(.*)";
658  reStr = "Default (.*) Parser";
660  reStr = "Default (.*) Writer";
662 
663  QString retval;
664 
665  if (pluginMap.size() > 1)
666  {
667  for (const QString& ext: pluginMap.keys())
668  {
669  if (!retval.isEmpty()) retval += " ";
670  retval += "*" + ext;
671  }
672  retval+= ")";
673  retval.prepend("All supported files (");
674  }
675  QMap<QString,QString> formatMap;
676  for (auto it = pluginMap.constBegin(); it != pluginMap.constEnd(); ++it)
677  {
678  QString label = it.value();
679  QRegularExpressionMatch reFileTypeMatch = reFileType.match(label);
680  QString fileFmt = (reFileTypeMatch.hasMatch())
681  ? reFileTypeMatch.captured(1) + QString(" files ")
682  : label.remove(QChar(':'));
683  if (formatMap.contains(fileFmt))
684  formatMap[fileFmt] += " *" + it.key();
685  else
686  formatMap[fileFmt] = "*" + it.key();
687  }
688  for (auto it = formatMap.constBegin(); it != formatMap.constEnd(); ++it)
689  {
690  if (!retval.isEmpty()) retval += ";;";
691  retval += it.key() + "(" + it.value() + ")";
692  }
693  return retval;
694  }
695 
697  {
698  for (GuiPluginEntry* gpe : mEntries)
699  {
700  if (gpe->isLoaded() || gpe->mFeature != ft) continue;
701  if (!extension.isEmpty())
702  {
703  bool extensionFound = false;
704  for (QString arg : gpe->mFeatureArguments)
705  {
706  if (arg==extension)
707  {
708  extensionFound = true;
709  break;
710  }
711  }
712  if (!extensionFound) continue;
713  }
714  BasePluginInterface* bpif = load(gpe->mName, gpe->mFilePath);
715  if (bpif) gpe->updateFromLoaded(bpif,false);
716  }
717  }
718 
719  BasePluginInterface* GuiPluginTable::load(const QString& pluginName, const QString& path) const
720  {
721  std::string pnam = pluginName.toStdString();
722  if (!plugin_manager::load(pnam,path.toStdString()))
723  {
724  log_warning("gui", "Error loading plugin '{}' from location '{}'",
725  pnam,path.toStdString());
726  return nullptr;
727  }
729  }
730 
732  {
733  mSettings->clear();
734  mSettings->beginWriteArray("plugins");
735  int inx = 0;
736 
737  for (GuiPluginEntry* gpe : mEntries)
738  {
739  mSettings->setArrayIndex(inx++);
740  gpe->persist(mSettings);
741  }
742  for (GuiPluginEntry* gpe : mAvoid)
743  {
744  mSettings->setArrayIndex(inx++);
745  gpe->persist(mSettings);
746  }
747  mSettings->endArray();
748  mSettings->sync();
749  }
750 
752  {
754  for (const std::string& pluginName : plugin_manager::get_plugin_names())
755  {
757  if (!bpif) continue;
758  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
759  {
760  GuiExtensionInterface* geif = dynamic_cast<GuiExtensionInterface*>(aeif);
761  if (geif) retval.insert(QString::fromStdString(pluginName),geif);
762  }
763  }
764  return retval;
765  }
766 
767  void GuiPluginTable::clearMemory()
768  {
769 
770  for (GuiPluginEntry* entry : mEntries)
771  delete entry;
772 
773  mEntries.clear();
774  mAvoid.clear();
775  mLookup.clear();
776  mSettings->deleteLater();
777  mSettings = nullptr;
778  }
779 
781  {
782  return mEntries.size();
783  }
784 
786  {
787  return 11;
788  }
789 
790  QVariant GuiPluginTable::headerData(int section, Qt::Orientation orientation, int role) const
791  {
792  if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
793  {
794  switch(section)
795  {
796  case 0: return "Name";
797  case 1: return "Description";
798  case 2: return "Filename";
799  case 3: return "Timestamp";
800  case 4: return "Dependencies";
801  case 5: return "Category";
802  case 6: return "Extensions";
803  case 7: return "GUI";
804  case 8: return "CLI";
805  case 9: return "State";
806  case 10: return QString();
807  default: return QVariant();
808  }
809  }
810  return QAbstractTableModel::headerData(section,orientation,role);
811  }
812 
814  {
815  return mEntries.at(irow);
816  }
817 
819  {
820  if (mWaitForRefresh) return;
821 
822  int irow = buttonIndex.row();
823  if (irow >= mEntries.size()) return;
824  GuiPluginEntry* gpe = mEntries.at(irow);
825  switch (buttonIndex.column())
826  {
827  case 7:
828  if (gpe->mName == "hal_gui") return;
829  if (gpe->isLoaded())
831  return;
832  case 8:
833  if (!gpe->mCliOptions.isEmpty())
835  return;
836  }
837  if (gpe->mName == "hal_gui") return;
838 
839  bool success = false;
840  if (gpe->isLoaded())
841  {
842  QStringList need = neededBy(gpe->mName);
843  if (need.isEmpty())
844  {
845  success = plugin_manager::unload(gpe->mName.toStdString());
846  if (success)
848  else
849  QMessageBox::warning(qApp->activeWindow(),"Unload failed",
850  QString("Unload of plugin %1\nrefused by plugin_manager")
851  .arg(gpe->mName));
852  }
853  else
854  {
855  QMessageBox::warning(qApp->activeWindow(),"Unload failed",
856  QString("Cannot unload plugin %1\nwhich is required by %2")
857  .arg(gpe->mName).arg(need.join(',')));
858  }
859  }
860  else
861  {
863  {
864  success = true;
866  }
867  else
868  QMessageBox::warning(qApp->activeWindow(),"Load failed",
869  QString("Could not load plugin %1\nplease check plugin_manager log")
870  .arg(gpe->mName));
871  }
872  if (success)
873  {
874  Q_EMIT dataChanged(index(irow,0),index(irow,columnCount()-1));
875  persist();
876  }
877  }
878 
880  {
881  switch (role)
882  {
883  case Qt::ForegroundRole:
884  if (mEntries.at(index.row())->isLoaded())
885  return mPluginMgr->hilightTextColor();
886  return mPluginMgr->defaultTextColor();
887  case Qt::BackgroundRole:
888  if (mEntries.at(index.row())->isLoaded())
889  return mPluginMgr->hilightBackgroundColor();
890  return QBrush();
891  case Qt::DisplayRole:
892  break; // handle below
893  default:
894  return QVariant();
895  }
896 
897  QVariant v = mEntries.at(index.row())->data(index.column());
898  if (index.column() >= 7 && index.column() <= 8)
899  return (v.toBool() ? "X" : " ");
900  return v;
901  }
902 
904  {
905  if (index.row() >= mEntries.size()) return false;
906  return mEntries.at(index.row())->mName == "hal_gui";
907  }
908 
910  {
911  if (index.row() >= mEntries.size()) return GuiPluginEntry::Unknown;
912  return mEntries.at(index.row())->mGuiExtensionState;
913  }
914 
916  {
917  int irow = mLookup.value(pluginName,-1);
918  if (irow < 0 || irow >= mEntries.size()) return;
919  mEntries.at(irow)->mGuiExtensionState = state;
920  persist();
921  }
922 
924  {
925  if (index.row() >= mEntries.size()) return false;
926  return !mEntries.at(index.row())->mCliOptions.isEmpty();
927  }
928 
930  {
931  if (index.row() >= mEntries.size()) return false;
932  return mEntries.at(index.row())->isLoaded();
933  }
934 
936  {
937  QStringList retval;
938  for (GuiPluginEntry* gpe : mEntries)
939  {
940  if (gpe->mName == pluginName) continue;
941  for (QString dep : gpe->mDependencies)
942  if (dep == pluginName)
943  {
944  // dependency found, check if loaded
945  if (gpe->isLoaded())
946  retval.append(gpe->mName);
947  }
948  }
949  return retval;
950  }
951 
952  //------------------------------------------------------------
953 
955  : mLoadState(NotAPlugin), mName(info.baseName()),
956  mFilePath(info.absoluteFilePath()),
957  mFileModified(info.lastModified()),
958  mFeature(FacExtensionInterface::FacUnknown),
959  mUserInterface(false),
960  mGuiExtensionState(Unknown),
961  mFileFound(true)
962  {;}
963 
965  {
966  switch (icol)
967  {
968  case 0: return mName;
969  case 1: return mDescription;
970  case 2: return QFileInfo(mFilePath).fileName();
971  case 3: return mFileModified.toString("dd.MM.yy hh:mm");
972  case 4: return mDependencies.join(' ');
973  case 5:
974  {
975  switch (mFeature)
976  {
977  case FacExtensionInterface::FacNetlistParser: return "Netlist parser";
978  case FacExtensionInterface::FacNetlistWriter: return "Netlist writer";
979  case FacExtensionInterface::FacGatelibParser: return "Gate library parser";
980  case FacExtensionInterface::FacGatelibWriter: return "Gate library writer";
981  default: break;
982  }
983  if (mUserInterface) return "HAL user interface";
984  return "Other HAL plugin";
985  }
986  case 6: return mFeatureArguments.join(' ');
987  case 7: return mGuiExtensionState;
988  case 8: return mCliOptions;
989  case 9: if (!isLoaded()) return "-";
990  else if (mExternalPath.isEmpty()) return "LOADED";
991  else return "EXTERN";
992  case 10: return (int) mLoadState;
993  }
994  return QVariant();
995  }
996 
998  {
999  if (!geif) return NotAnExtension;
1000 
1001  switch (mGuiExtensionState) // enforce user setting
1002  {
1003  case Disabled:
1004  geif->set_contribution_enabled(false);
1005  break;
1006  case Enabled:
1007  geif->set_contribution_enabled(true);
1008  break;
1009  default:
1010  break;
1011  }
1012  return (geif->is_contribution_enabled() ? Enabled : Disabled);
1013  }
1014 
1015 
1016  void GuiPluginEntry::persist(QSettings* settings) const
1017  {
1018  settings->setValue("state", (int) mLoadState);
1019  settings->setValue("name", mName);
1020  settings->setValue("version", mVersion);
1021  settings->setValue("description", mDescription);
1022  settings->setValue("file_modified", mFileModified);
1023  settings->setValue("dependencies", mDependencies);
1024  settings->setValue("feature_code", (int) mFeature);
1025  settings->setValue("feature_args", mFeatureArguments);
1026  settings->setValue("user_interface", mUserInterface);
1027  settings->setValue("gui_extension_state", (int) mGuiExtensionState);
1028  settings->setValue("cli_options", mCliOptions);
1029  settings->setValue("ext_path", mExternalPath);
1030  }
1031 
1033  : mFileFound(false)
1034  {
1035  mLoadState = (LoadState) settings->value("state").toInt();
1036  mName = settings->value("name").toString();
1037  mVersion = settings->value("version").toString();
1038  mDescription = settings->value("description").toString();
1039  mFileModified = settings->value("file_modified").toDateTime();
1040  mDependencies = settings->value("dependencies").toStringList();
1041  mFeature = (FacExtensionInterface::Feature) settings->value("feature_code").toInt();
1042  mFeatureArguments = settings->value("feature_args").toStringList();
1043  mUserInterface = settings->value("user_interface").toBool();
1044  mGuiExtensionState = (GuiExtensionState) settings->value("gui_extension_state").toInt();
1045  mCliOptions = settings->value("cli_options").toString();
1046  mExternalPath = settings->value("ext_path").toString();
1048  }
1049 
1050  void GuiPluginEntry::updateFromLoaded(const BasePluginInterface *bpif, bool isUser, const QDateTime& modified)
1051  {
1052  mLoadState = isUser ? UserLoad : AutoLoad;
1053  QString desc = QString::fromStdString(bpif->get_name());
1054  if (desc != mName) mDescription = desc;
1055  if (!bpif->get_description().empty()) mDescription = QString::fromStdString(bpif->get_description());
1057  mDependencies.clear();
1058  for (std::string dep : bpif->get_dependencies())
1062  {
1063  mDescription = QString::fromStdString(feature.description);
1064  mFeature = feature.feature;
1066  for (std::string arg : feature.args)
1068  }
1069  mCliOptions.clear();
1070 
1071  GuiExtensionState newGuiExtensionState = Unknown; // not initialized
1072  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
1073  {
1074  CliExtensionInterface* ceif = nullptr;
1075  GuiExtensionInterface* geif = nullptr;
1076  if (geif = dynamic_cast<GuiExtensionInterface*>(aeif))
1077  {
1078  mGuiExtensionState = newGuiExtensionState = enforceGuiExtensionState(geif);
1079  }
1080  else if ((ceif=dynamic_cast<CliExtensionInterface*>(aeif)))
1082  }
1083  if (!newGuiExtensionState) mGuiExtensionState = NotAnExtension; // no GUI extension
1084 
1085  mUserInterface = (dynamic_cast<const UIPluginInterface*>(bpif) != nullptr);
1086  if (modified.isValid()) mFileModified = modified;
1087  }
1088 
1089  //------------------------------------------------
1091  : QItemDelegate(parent)
1092  {
1093  // int bgBright[3] = {26, 46, 80};
1094  for (int i=0; i<3; i++)
1095  {
1096  mTemplateButton[i] = new QPushButton;
1097 // mTemplateButton[i]->setStyleSheet(QString("background-color : rgb(%1,%1,%1)").arg(bgBright[i]));
1098  }
1099 
1100  }
1101 
1103  {
1104  mIconLoad = gui_utility::getStyledSvgIcon(gpm->loadIconStyle(),gpm->loadIconPath());
1105  mIconUnload = gui_utility::getStyledSvgIcon(gpm->unloadIconStyle(), gpm->unloadIconPath());
1106  mIconCliOptions = gui_utility::getStyledSvgIcon(gpm->cliIconStyle(), gpm->cliIconPath());
1107  mIconEnableGuiContribution = gui_utility::getStyledSvgIcon(gpm->guiIconEnabledStyle(), gpm->guiIconEnabledPath());
1108  mIconDisableGuiContribution = gui_utility::getStyledSvgIcon(gpm->guiIconDisabledStyle(), gpm->guiIconDisabledPath());
1109  }
1110 
1112  {
1113  for (int i=0; i<3; i++)
1114  delete mTemplateButton[i];
1115  }
1116 
1117  void GuiPluginDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
1118  {
1119  const int w = 28;
1120  const GuiPluginTable* plt = dynamic_cast<const GuiPluginTable*>(index.model());
1121  if (!plt) return;
1122  QStyleOptionButton button;
1123  bool drawIcon = true;
1125  switch (index.column())
1126  {
1127  case 7:
1128  button.iconSize = QSize(w-4,w-4);
1129  iconState = plt->guiExtensionState(index);
1130 
1131  if (!iconState)
1132  {
1133  QString pluginName = plt->at(index.row())->name();
1134  GuiExtensionInterface* geif = GuiPluginManager::getGuiExtensions().value(pluginName);
1135  if (!plt->isLoaded(index))
1136  iconState = GuiPluginEntry::Unknown;
1137  else if (!geif)
1138  iconState = GuiPluginEntry::NotAnExtension;
1139  else if (geif->is_contribution_enabled())
1140  iconState = GuiPluginEntry::Enabled;
1141  else
1142  iconState = GuiPluginEntry::Disabled;
1143  }
1144 
1145  switch (iconState)
1146  {
1148  {
1149  drawIcon = false;
1150  break;
1151  }
1153  drawIcon = false;
1154  break;
1156  button.icon = mIconDisableGuiContribution;
1157  break;
1159  button.icon = mIconEnableGuiContribution;
1160  break;
1161  }
1162  break;
1163  case 8:
1164  button.iconSize = QSize(w-4,w-4);
1165  if (plt->hasCliExtension(index))
1166  button.icon = mIconCliOptions;
1167  else
1168  drawIcon = false;
1169  break;
1170  case 10:
1171  if (plt->isHalGui(index))
1172  drawIcon = false;
1173  else
1174  {
1175  if (plt->isLoaded(index))
1176  button.icon = mIconUnload;
1177  else
1178  button.icon = mIconLoad;
1179  button.iconSize = QSize(w-12,w-12);
1180  }
1181  break;
1182  default:
1183  drawIcon = false;
1184  break;
1185  }
1186 
1187  State stat = Normal;
1188  QColor bgColor = plt->data(index,Qt::BackgroundRole).value<QColor>();
1189  if (mMouseIndex.isValid() && mMouseIndex == index)
1190  {
1191  stat = Pressed;
1192  bgColor = Qt::red;
1193  }
1194 
1195  QRect r = option.rect;
1196  int x = r.left() + r.width() - w;
1197  int y = r.top();
1198  int h = w;
1199  button.rect = QRect(x,y,w,h);
1201 
1202  if (bgColor.isValid())
1203  painter->fillRect(r,QBrush(bgColor));
1204  if (drawIcon)
1205  QApplication::style()->drawControl( QStyle::CE_PushButton, &button, painter, mTemplateButton[stat]);
1206  }
1207 
1209  {
1210  return QSize(30,30);
1211  }
1212 
1214  {
1215  QMouseEvent* mev = dynamic_cast<QMouseEvent*>(event);
1216  if (!mev) return false;
1217  switch (mev->type())
1218  {
1220  mMousePos = mev->pos();
1221  mMouseIndex = index;
1222  return true;
1224  if ((mMousePos - mev->pos()).manhattanLength()<4)
1225  {
1227  }
1228  mMousePos = QPoint();
1229  mMouseIndex = QModelIndex();
1230  return true;
1231  default:
1232  break;
1233  }
1234  return false;
1235  }
1236 }
virtual std::set< std::string > get_dependencies() const
virtual std::string get_name() const =0
virtual std::string get_description() const
virtual std::string get_version() const =0
virtual std::vector< AbstractExtensionInterface * > get_extensions() const
virtual ProgramOptions get_cli_options() const =0
virtual std::vector< ContextMenuContribution > get_context_contribution(const Netlist *nl, const std::vector< u32 > &mods, const std::vector< u32 > &gats, const std::vector< u32 > &nets)
void set_contribution_enabled(bool enabled)
std::string contribution_top_label() const
virtual void netlist_loaded(Netlist *nl)
virtual std::vector< PluginParameter > get_parameter() const
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
GuiPluginDelegate(QObject *parent=nullptr)
void buttonPressed(QModelIndex index)
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
void updateQss(GuiPluginManager *gpm)
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
GuiPluginEntry(const QFileInfo &info)
LoadState
@ AutoLoad
@ NotAPlugin
@ UserLoad
@ NotLoaded
void persist(QSettings *settings) const
QStringList mFeatureArguments
QDateTime mFileModified
QString mCliOptions
QString name() const
QStringList mDependencies
QString mExternalPath
bool isLoaded() const
QString mName
FacExtensionInterface::Feature mFeature
QString mDescription
QVariant data(int icol) const
GuiExtensionState enforceGuiExtensionState(GuiExtensionInterface *geif) const
enum hal::GuiPluginEntry::LoadState mLoadState
enum hal::GuiPluginEntry::GuiExtensionState mGuiExtensionState
QString mVersion
void updateFromLoaded(const BasePluginInterface *bpif, bool isUser, const QDateTime &modified=QDateTime())
QString mFilePath
GuiExtensionState
@ Enabled
@ Disabled
@ NotAnExtension
@ Unknown
bool mUserInterface
void setLoadIconStyle(const QString &s)
void setGuiIconEnabledPath(const QString &s)
void addPluginActions(QMenu *menu) const
void setDefaultTextColor(QColor &c)
QString guiIconEnabledPath() const
GuiPluginManager(QWidget *parent=nullptr)
void setHilightTextColor(QColor &c)
void setUnloadIconPath(const QString &s)
QString unloadIconStyle() const
void setHilightBackgroundColor(QColor &c)
static void addPluginSubmenus(QMenu *contextMenu, Netlist *netlist, const std::vector< u32 > &modules, const std::vector< u32 > &gates, const std::vector< u32 > &nets)
void setGuiIconDisabledPath(const QString &s)
QColor hilightBackgroundColor() const
static QMap< QString, GuiExtensionInterface * > getGuiExtensions()
void setCliIconPath(const QString &s)
void setLoadIconPath(const QString &s)
void setGuiIconEnabledStyle(const QString &s)
void setCliIconStyle(const QString &s)
QString guiIconEnabledStyle() const
QString guiIconDisabledPath() const
QString unloadIconPath() const
void setGuiIconDisabledStyle(const QString &s)
void setUnloadIconStyle(const QString &s)
QString guiIconDisabledStyle() const
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
SupportedFileFormats listFacFeature(FacExtensionInterface::Feature ft) const
bool isHalGui(const QModelIndex &index) const
bool isLoaded(const QModelIndex &index) const
BasePluginInterface * load(const QString &pluginName, const QString &path) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
GuiPluginEntry::GuiExtensionState guiExtensionState(const QModelIndex &index) const
GuiPluginTable(GuiPluginManager *parent=nullptr)
int addExternalPlugin(const QString &path)
void setGuiExtensionState(const QString &pluginName, GuiPluginEntry::GuiExtensionState state)
GuiPluginEntry * at(int irow) const
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void handleButtonPressed(const QModelIndex &buttonIndex)
void handlePluginLoaded(const QString &pluginName, const QString &path)
void handlePluginUnloaded(const QString &pluginName, const QString &path)
void showCliOptions(QString pluginName, QString cliOptions)
void toggleEnableGuiContribution(QString pluginName)
void loadFeature(FacExtensionInterface::Feature ft, const QString &extension=QString())
bool hasCliExtension(const QModelIndex &index) const
QStringList neededBy(const QString &pluginName)
void removeEntry(int irow)
GuiPluginView(QWidget *parent=nullptr)
GuiPluginTable * mGuiPluginTable
Definition: plugin_relay.h:68
void pluginUnloaded(const QString &name, const QString &path)
void pluginLoaded(const QString &name, const QString &path)
std::string get_options_string() const
QString toFileDialog(bool addHalFormat) const
#define log_debug(channel,...)
Definition: log.h:74
#define log_info(channel,...)
Definition: log.h:70
#define log_warning(channel,...)
Definition: log.h:76
QIcon getStyledSvgIcon(const QString &from_to_colors_enabled, const QString &svg_path, QString from_to_colors_disabled=QString())
Definition: graphics.cpp:60
bool has_valid_file_extension(std::filesystem::path file_name)
bool unload(const std::string &plugin_name)
bool load(const std::string &plugin_name, const std::filesystem::path &file_path=std::filesystem::path())
std::set< std::string > get_plugin_names()
std::filesystem::path get_plugin_path(std::string plugin_name)
BasePluginInterface * get_plugin_instance(const std::string &plugin_name, bool initialize=true, bool silent=false)
std::vector< PluginFeature > get_plugin_features(std::string name)
std::vector< std::filesystem::path > get_plugin_directories()
Definition: utils.cpp:195
std::filesystem::path get_user_config_directory()
Definition: utils.cpp:172
Definition: defines.h:45
PluginRelay * gPluginRelay
Definition: plugin_gui.cpp:83
void clicked(bool checked)
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const
void setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
void setEnabled(bool)
void triggered(bool checked)
QStyle * style()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
bool isValid() const const
bool isValid() const const
QString toString(Qt::DateFormat format) const const
virtual int exec()
MouseButtonPress
QEvent::Type type() const const
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QString baseName() const const
QString fileName() const const
void setSectionResizeMode(QHeaderView::ResizeMode mode)
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
void setAlignment(Qt::Alignment)
void setIndent(int)
void setPixmap(const QPixmap &)
void addWidget(QWidget *w)
bool setAlignment(QWidget *w, Qt::Alignment alignment)
void append(const T &value)
void clear()
bool isEmpty() const const
QMap::iterator begin()
void clear()
QMap::const_iterator constBegin() const const
QMap::const_iterator constEnd() const const
bool contains(const Key &key) const const
QMap::iterator end()
QMap::iterator erase(QMap::iterator pos)
QMap::iterator insert(const Key &key, const T &value)
const Key key(const T &value, const Key &defaultKey) const const
QList< Key > keys() const const
int remove(const Key &key)
int size() const const
const T value(const Key &key, const T &defaultValue) const const
QAction * addAction(const QString &text)
QAction * addMenu(QMenu *menu)
QAction * addSeparator()
virtual int exec() override
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
int column() const const
bool isValid() const const
int row() const const
QPoint pos() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
virtual bool event(QEvent *e)
void fillRect(const QRectF &rectangle, const QBrush &brush)
int left() const const
int top() const const
int width() const const
QRegularExpressionMatch match(const QString &subject, int offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
QString captured(int nth) const const
bool hasMatch() const const
int beginReadArray(const QString &prefix)
void beginWriteArray(const QString &prefix, int size)
void clear()
void endArray()
void setArrayIndex(int i)
void setValue(const QString &key, const QVariant &value)
void sync()
QVariant value(const QString &key, const QVariant &defaultValue) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void clear()
QString fromStdString(const std::string &str)
bool isEmpty() const const
QString & prepend(QChar ch)
QString & remove(int position, int n)
std::string toStdString() const const
QString join(const QString &separator) const const
virtual void drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
virtual void polish(QWidget *widget)
virtual void unpolish(QWidget *widget)
AlignLeft
DisplayRole
Orientation
QHeaderView * horizontalHeader() const const
virtual void setModel(QAbstractItemModel *model) override
QHeaderView * verticalHeader() const const
bool toBool() const const
QDateTime toDateTime() const const
int toInt(bool *ok) const const
QString toString() const const
QStringList toStringList() const const
T value() const const
void setEnabled(bool)
QLayout * layout() const const
void setFixedHeight(int h)
void setFixedSize(const QSize &s)
void setSizePolicy(QSizePolicy)
QStyle * style() const const
void setStyleSheet(const QString &styleSheet)