HAL
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 
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));
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  // plugin updated or not known yet, try dummy load to retrieve information
450  bpif = load(pluginName, info.absoluteFilePath());
451  if (!bpif)
452  {
453  log_warning("gui", "GuiPluginManager: loading of '{}' failed, is it really a HAL plugin?", pluginName.toStdString());
454  gpe->mLoadState = GuiPluginEntry::NotAPlugin;
455  continue;
456  }
457 
458  if (bpif->get_name().empty() || bpif->get_version().empty())
459  {
460  log_warning("gui", "GuiPluginManager: '{}' has empty name or version, plugin ignored.", pluginName.toStdString());
461  gpe->mLoadState = GuiPluginEntry::NotAPlugin;
462  }
463  else
464  {
465  log_info("gui", "GuiPluginManager: '{}' loaded to retrieve information about plugin features.", pluginName.toStdString());
466  gpe->updateFromLoaded(bpif, false, info.lastModified()); // load success
467  }
468  }
469  }
470 
471  // update entry if plugin loaded in meantime as dependency
472  for (std::string loadedPluginName: plugin_manager::get_plugin_names())
473  {
474  GuiPluginEntry* gpe = pluginEntries.value(QString::fromStdString(loadedPluginName));
475  if (!gpe || gpe->isLoaded()) continue;
476  BasePluginInterface* bpif = plugin_manager::get_plugin_instance(loadedPluginName,false,true);
477  if (!bpif) continue;
478  QFileInfo info(gpe->mFilePath);
479  gpe->updateFromLoaded(bpif,false,info.exists()?info.lastModified():QDateTime());
480  }
481 
482  mEntries.clear();
483  mAvoid.clear();
484  mLookup.clear();
485  auto it = pluginEntries.begin();
486  while (it != pluginEntries.end())
487  {
488  bool removeEntry = false;
489  GuiPluginEntry* gpe = it.value();
490  if (gpe->isFileFound())
491  {
492  if (gpe->isPlugin())
493  {
494  mLookup.insert(gpe->mName,mEntries.size());
495  mEntries.append(gpe);
496  }
497  else
498  {
499  mAvoid.append(gpe);
500  }
501  }
502  else if (gpe->mExternalPath.isEmpty() || !gpe->requestLoad() )
503  {
504  // plugin does no longer exist or no longer required
505  removeEntry = true;
506  }
507  else
508  {
509  // loaded plugin as binary from user provided path
510  QFileInfo info(gpe->mExternalPath);
511  if (info.exists() && info.isReadable())
512  {
513  mLookup.insert(gpe->mName,mEntries.size());
514  mEntries.append(gpe);
515  BasePluginInterface* bpif = load(gpe->mName, info.absoluteFilePath());
516  if (!bpif)
517  {
518  log_warning("gui", "GuiPluginManager: loading of requested plugin '{}' failed.", gpe->mName.toStdString());
519  removeEntry = true;
520  }
521  else
522  {
523  gpe->updateFromLoaded(bpif, true, info.lastModified());
524  log_info("gui", "GuiPluginManager: '{}' loaded external plugin upon user request.", gpe->mName.toStdString());
525  }
526  }
527  else
528  {
529  removeEntry = true;
530  }
531  }
532  if (removeEntry)
533  it = pluginEntries.erase(it);
534  else
535  ++it;
536  }
537  persist();
538  }
539 
541  {
542  QFileInfo finfo(path);
543  GuiPluginEntry* gpe = new GuiPluginEntry(finfo);
544  int n = mEntries.size();
546  mLookup.insert(gpe->mName,n);
547  mEntries.append(gpe);
548  endInsertRows();
549  BasePluginInterface* bpif = load(finfo.baseName(),path);
550  if (bpif)
551  {
552  gpe->updateFromLoaded(bpif,true);
553  gpe->mExternalPath = path;
555  persist();
556  }
557  else
558  {
560  gpe = mEntries.takeLast();
561  mLookup.remove(gpe->mName);
562  endRemoveRows();
563  delete gpe;
564  n = -1; // return value : no row was inserted
565  }
566  return n;
567  }
568 
569 
570  void GuiPluginTable::handlePluginLoaded(const QString& pluginName, const QString&)
571  {
572  if (mWaitForRefresh) return;
573  changeState(pluginName,GuiPluginEntry::AutoLoad);
574  }
575 
576  void GuiPluginTable::handlePluginUnloaded(const QString& pluginName, const QString&)
577  {
578  if (mWaitForRefresh) return;
579  changeState(pluginName,GuiPluginEntry::NotLoaded);
580  }
581 
582  void GuiPluginTable::changeState(const QString& pluginName, GuiPluginEntry::LoadState state)
583  {
584  int irow = mLookup.value(pluginName,-1);
585  if (irow < 0 || irow >= mEntries.size()) return;
586  GuiPluginEntry* gpe = mEntries.at(irow);
587  gpe->mLoadState = state;
588  if (state >= GuiPluginEntry::AutoLoad)
589  {
592  if (bpif)
593  {
594  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
595  {
596  GuiExtensionInterface* geif = dynamic_cast<GuiExtensionInterface*>(aeif);
597  if (geif)
598  {
599  newGuiExtensionState = gpe->enforceGuiExtensionState(geif);
600  break;
601  }
602  }
603  }
604  gpe->mGuiExtensionState = newGuiExtensionState;
605  }
606  Q_EMIT dataChanged(index(irow,0),index(irow,10));
607  }
608 
610  {
611  SupportedFileFormats retval(ft);
612  for (GuiPluginEntry* gpe : mEntries)
613  {
614  if (gpe->mFeature != ft) continue;
615  for (QString arg : gpe->mFeatureArguments)
616  {
617  retval.insert(arg,gpe->mDescription);
618  }
619  }
620  return retval;
621  }
622 
624  {
625  QMap<QString,QString> pluginMap = *this; // might want to add hal format temporarily
626  if (addHalFormat)
627  pluginMap.insert(".hal", "HAL progress files ");
628 
629  QString reStr = "(.*)";
631  reStr = "Default (.*) Parser";
633  reStr = "Default (.*) Writer";
634  QRegExp re(reStr, Qt::CaseInsensitive);
635 
636  QString retval;
637 
638  if (pluginMap.size() > 1)
639  {
640  for (const QString& ext: pluginMap.keys())
641  {
642  if (!retval.isEmpty()) retval += " ";
643  retval += "*" + ext;
644  }
645  retval+= ")";
646  retval.prepend("All supported files (");
647  }
648  QMap<QString,QString> formatMap;
649  for (auto it = pluginMap.constBegin(); it != pluginMap.constEnd(); ++it)
650  {
651  QString label = it.value();
652  QString fileFmt = (re.indexIn(label) < 0)
653  ? label.remove(QChar(':'))
654  : re.cap(1) + QString(" files ");
655  if (formatMap.contains(fileFmt))
656  formatMap[fileFmt] += " *" + it.key();
657  else
658  formatMap[fileFmt] = "*" + it.key();
659  }
660  for (auto it = formatMap.constBegin(); it != formatMap.constEnd(); ++it)
661  {
662  if (!retval.isEmpty()) retval += ";;";
663  retval += it.key() + "(" + it.value() + ")";
664  }
665  return retval;
666  }
667 
669  {
670  for (GuiPluginEntry* gpe : mEntries)
671  {
672  if (gpe->isLoaded() || gpe->mFeature != ft) continue;
673  if (!extension.isEmpty())
674  {
675  bool extensionFound = false;
676  for (QString arg : gpe->mFeatureArguments)
677  {
678  if (arg==extension)
679  {
680  extensionFound = true;
681  break;
682  }
683  }
684  if (!extensionFound) continue;
685  }
686  BasePluginInterface* bpif = load(gpe->mName, gpe->mFilePath);
687  if (bpif) gpe->updateFromLoaded(bpif,false);
688  }
689  }
690 
691  BasePluginInterface* GuiPluginTable::load(const QString& pluginName, const QString& path) const
692  {
693  std::string pnam = pluginName.toStdString();
694  if (!plugin_manager::load(pnam,path.toStdString()))
695  {
696  log_warning("gui", "Error loading plugin '{}' from location '{}'",
697  pnam,path.toStdString());
698  return nullptr;
699  }
701  }
702 
704  {
705  mSettings->clear();
706  mSettings->beginWriteArray("plugins");
707  int inx = 0;
708 
709  for (GuiPluginEntry* gpe : mEntries)
710  {
711  mSettings->setArrayIndex(inx++);
712  gpe->persist(mSettings);
713  }
714  for (GuiPluginEntry* gpe : mAvoid)
715  {
716  mSettings->setArrayIndex(inx++);
717  gpe->persist(mSettings);
718  }
719  mSettings->endArray();
720  mSettings->sync();
721  }
722 
724  {
726  for (const std::string& pluginName : plugin_manager::get_plugin_names())
727  {
729  if (!bpif) continue;
730  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
731  {
732  GuiExtensionInterface* geif = dynamic_cast<GuiExtensionInterface*>(aeif);
733  if (geif) retval.insert(QString::fromStdString(pluginName),geif);
734  }
735  }
736  return retval;
737  }
738 
739  void GuiPluginTable::clearMemory()
740  {
741 
742  for (GuiPluginEntry* entry : mEntries)
743  delete entry;
744 
745  mEntries.clear();
746  mAvoid.clear();
747  mLookup.clear();
748  mSettings->deleteLater();
749  mSettings = nullptr;
750  }
751 
753  {
754  return mEntries.size();
755  }
756 
758  {
759  return 11;
760  }
761 
762  QVariant GuiPluginTable::headerData(int section, Qt::Orientation orientation, int role) const
763  {
764  if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
765  {
766  switch(section)
767  {
768  case 0: return "Name";
769  case 1: return "Description";
770  case 2: return "Filename";
771  case 3: return "Timestamp";
772  case 4: return "Dependencies";
773  case 5: return "Category";
774  case 6: return "Extensions";
775  case 7: return "GUI";
776  case 8: return "CLI";
777  case 9: return "State";
778  case 10: return QString();
779  default: return QVariant();
780  }
781  }
782  return QAbstractTableModel::headerData(section,orientation,role);
783  }
784 
786  {
787  return mEntries.at(irow);
788  }
789 
791  {
792  if (mWaitForRefresh) return;
793 
794  int irow = buttonIndex.row();
795  if (irow >= mEntries.size()) return;
796  GuiPluginEntry* gpe = mEntries.at(irow);
797  switch (buttonIndex.column())
798  {
799  case 7:
800  if (gpe->mName == "hal_gui") return;
801  if (gpe->isLoaded())
803  return;
804  case 8:
805  if (!gpe->mCliOptions.isEmpty())
807  return;
808  }
809  if (gpe->mName == "hal_gui") return;
810 
811  bool success = false;
812  if (gpe->isLoaded())
813  {
814  QStringList need = neededBy(gpe->mName);
815  if (need.isEmpty())
816  {
817  success = plugin_manager::unload(gpe->mName.toStdString());
818  if (success)
820  else
821  QMessageBox::warning(qApp->activeWindow(),"Unload failed",
822  QString("Unload of plugin %1\nrefused by plugin_manager")
823  .arg(gpe->mName));
824  }
825  else
826  {
827  QMessageBox::warning(qApp->activeWindow(),"Unload failed",
828  QString("Cannot unload plugin %1\nwhich is required by %2")
829  .arg(gpe->mName).arg(need.join(',')));
830  }
831  }
832  else
833  {
835  {
836  success = true;
838  }
839  else
840  QMessageBox::warning(qApp->activeWindow(),"Load failed",
841  QString("Could not load plugin %1\nplease check plugin_manager log")
842  .arg(gpe->mName));
843  }
844  if (success)
845  {
846  Q_EMIT dataChanged(index(irow,0),index(irow,columnCount()-1));
847  persist();
848  }
849  }
850 
851  QVariant GuiPluginTable::data(const QModelIndex &index, int role) const
852  {
853  switch (role)
854  {
855  case Qt::ForegroundRole:
856  if (mEntries.at(index.row())->isLoaded())
857  return mPluginMgr->hilightTextColor();
858  return mPluginMgr->defaultTextColor();
859  case Qt::BackgroundRole:
860  if (mEntries.at(index.row())->isLoaded())
861  return mPluginMgr->hilightBackgroundColor();
862  return QBrush();
863  case Qt::DisplayRole:
864  break; // handle below
865  default:
866  return QVariant();
867  }
868 
869  QVariant v = mEntries.at(index.row())->data(index.column());
870  if (index.column() >= 7 && index.column() <= 8)
871  return (v.toBool() ? "X" : " ");
872  return v;
873  }
874 
875  bool GuiPluginTable::isHalGui(const QModelIndex &index) const
876  {
877  if (index.row() >= mEntries.size()) return false;
878  return mEntries.at(index.row())->mName == "hal_gui";
879  }
880 
882  {
883  if (index.row() >= mEntries.size()) return GuiPluginEntry::Unknown;
884  return mEntries.at(index.row())->mGuiExtensionState;
885  }
886 
888  {
889  int irow = mLookup.value(pluginName,-1);
890  if (irow < 0 || irow >= mEntries.size()) return;
891  mEntries.at(irow)->mGuiExtensionState = state;
892  persist();
893  }
894 
896  {
897  if (index.row() >= mEntries.size()) return false;
898  return !mEntries.at(index.row())->mCliOptions.isEmpty();
899  }
900 
901  bool GuiPluginTable::isLoaded(const QModelIndex &index) const
902  {
903  if (index.row() >= mEntries.size()) return false;
904  return mEntries.at(index.row())->isLoaded();
905  }
906 
908  {
909  QStringList retval;
910  for (GuiPluginEntry* gpe : mEntries)
911  {
912  if (gpe->mName == pluginName) continue;
913  for (QString dep : gpe->mDependencies)
914  if (dep == pluginName)
915  {
916  // dependency found, check if loaded
917  if (gpe->isLoaded())
918  retval.append(gpe->mName);
919  }
920  }
921  return retval;
922  }
923 
924  //------------------------------------------------------------
925 
927  : mLoadState(NotAPlugin), mName(info.baseName()),
928  mFilePath(info.absoluteFilePath()),
929  mFileModified(info.lastModified()),
930  mFeature(FacExtensionInterface::FacUnknown),
931  mUserInterface(false),
932  mGuiExtensionState(Unknown),
933  mFileFound(true)
934  {;}
935 
937  {
938  switch (icol)
939  {
940  case 0: return mName;
941  case 1: return mDescription;
942  case 2: return QFileInfo(mFilePath).fileName();
943  case 3: return mFileModified.toString("dd.MM.yy hh:mm");
944  case 4: return mDependencies.join(' ');
945  case 5:
946  {
947  switch (mFeature)
948  {
949  case FacExtensionInterface::FacNetlistParser: return "Netlist parser";
950  case FacExtensionInterface::FacNetlistWriter: return "Netlist writer";
951  case FacExtensionInterface::FacGatelibParser: return "Gate library parser";
952  case FacExtensionInterface::FacGatelibWriter: return "Gate library writer";
953  default: break;
954  }
955  if (mUserInterface) return "HAL user interface";
956  return "Other HAL plugin";
957  }
958  case 6: return mFeatureArguments.join(' ');
959  case 7: return mGuiExtensionState;
960  case 8: return mCliOptions;
961  case 9: if (!isLoaded()) return "-";
962  else if (mExternalPath.isEmpty()) return "LOADED";
963  else return "EXTERN";
964  case 10: return (int) mLoadState;
965  }
966  return QVariant();
967  }
968 
970  {
971  if (!geif) return NotAnExtension;
972 
973  switch (mGuiExtensionState) // enforce user setting
974  {
975  case Disabled:
976  geif->set_contribution_enabled(false);
977  break;
978  case Enabled:
979  geif->set_contribution_enabled(true);
980  break;
981  default:
982  break;
983  }
984  return (geif->is_contribution_enabled() ? Enabled : Disabled);
985  }
986 
987 
988  void GuiPluginEntry::persist(QSettings* settings) const
989  {
990  settings->setValue("state", (int) mLoadState);
991  settings->setValue("name", mName);
992  settings->setValue("version", mVersion);
993  settings->setValue("description", mDescription);
994  settings->setValue("file_modified", mFileModified);
995  settings->setValue("dependencies", mDependencies);
996  settings->setValue("feature_code", (int) mFeature);
997  settings->setValue("feature_args", mFeatureArguments);
998  settings->setValue("user_interface", mUserInterface);
999  settings->setValue("gui_extension_state", (int) mGuiExtensionState);
1000  settings->setValue("cli_options", mCliOptions);
1001  settings->setValue("ext_path", mExternalPath);
1002  }
1003 
1005  : mFileFound(false)
1006  {
1007  mLoadState = (LoadState) settings->value("state").toInt();
1008  mName = settings->value("name").toString();
1009  mVersion = settings->value("version").toString();
1010  mDescription = settings->value("description").toString();
1011  mFileModified = settings->value("file_modified").toDateTime();
1012  mDependencies = settings->value("dependencies").toStringList();
1013  mFeature = (FacExtensionInterface::Feature) settings->value("feature_code").toInt();
1014  mFeatureArguments = settings->value("feature_args").toStringList();
1015  mUserInterface = settings->value("user_interface").toBool();
1016  mGuiExtensionState = (GuiExtensionState) settings->value("gui_extension_state").toInt();
1017  mCliOptions = settings->value("cli_options").toString();
1018  mExternalPath = settings->value("ext_path").toString();
1020  }
1021 
1022  void GuiPluginEntry::updateFromLoaded(const BasePluginInterface *bpif, bool isUser, const QDateTime& modified)
1023  {
1024  mLoadState = isUser ? UserLoad : AutoLoad;
1025  QString desc = QString::fromStdString(bpif->get_name());
1026  if (desc != mName) mDescription = desc;
1027  if (!bpif->get_description().empty()) mDescription = QString::fromStdString(bpif->get_description());
1029  mDependencies.clear();
1030  for (std::string dep : bpif->get_dependencies())
1034  {
1035  mDescription = QString::fromStdString(feature.description);
1036  mFeature = feature.feature;
1038  for (std::string arg : feature.args)
1040  }
1041  mCliOptions.clear();
1042 
1043  GuiExtensionState newGuiExtensionState = Unknown; // not initialized
1044  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
1045  {
1046  CliExtensionInterface* ceif = nullptr;
1047  GuiExtensionInterface* geif = nullptr;
1048  if (geif = dynamic_cast<GuiExtensionInterface*>(aeif))
1049  {
1050  mGuiExtensionState = newGuiExtensionState = enforceGuiExtensionState(geif);
1051  }
1052  else if ((ceif=dynamic_cast<CliExtensionInterface*>(aeif)))
1054  }
1055  if (!newGuiExtensionState) mGuiExtensionState = NotAnExtension; // no GUI extension
1056 
1057  mUserInterface = (dynamic_cast<const UIPluginInterface*>(bpif) != nullptr);
1058  if (modified.isValid()) mFileModified = modified;
1059  }
1060 
1061  //------------------------------------------------
1063  : QItemDelegate(parent)
1064  {
1065  // int bgBright[3] = {26, 46, 80};
1066  for (int i=0; i<3; i++)
1067  {
1068  mTemplateButton[i] = new QPushButton;
1069 // mTemplateButton[i]->setStyleSheet(QString("background-color : rgb(%1,%1,%1)").arg(bgBright[i]));
1070  }
1071 
1072  }
1073 
1075  {
1076  mIconLoad = gui_utility::getStyledSvgIcon(gpm->loadIconStyle(),gpm->loadIconPath());
1077  mIconUnload = gui_utility::getStyledSvgIcon(gpm->unloadIconStyle(), gpm->unloadIconPath());
1078  mIconCliOptions = gui_utility::getStyledSvgIcon(gpm->cliIconStyle(), gpm->cliIconPath());
1079  mIconEnableGuiContribution = gui_utility::getStyledSvgIcon(gpm->guiIconEnabledStyle(), gpm->guiIconEnabledPath());
1080  mIconDisableGuiContribution = gui_utility::getStyledSvgIcon(gpm->guiIconDisabledStyle(), gpm->guiIconDisabledPath());
1081  }
1082 
1084  {
1085  for (int i=0; i<3; i++)
1086  delete mTemplateButton[i];
1087  }
1088 
1089  void GuiPluginDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
1090  {
1091  const int w = 28;
1092  const GuiPluginTable* plt = dynamic_cast<const GuiPluginTable*>(index.model());
1093  if (!plt) return;
1094  QStyleOptionButton button;
1095  bool drawIcon = true;
1097  switch (index.column())
1098  {
1099  case 7:
1100  button.iconSize = QSize(w-4,w-4);
1101  iconState = plt->guiExtensionState(index);
1102 
1103  if (!iconState)
1104  {
1105  QString pluginName = plt->at(index.row())->name();
1106  GuiExtensionInterface* geif = GuiPluginManager::getGuiExtensions().value(pluginName);
1107  if (!plt->isLoaded(index))
1108  iconState = GuiPluginEntry::Unknown;
1109  else if (!geif)
1110  iconState = GuiPluginEntry::NotAnExtension;
1111  else if (geif->is_contribution_enabled())
1112  iconState = GuiPluginEntry::Enabled;
1113  else
1114  iconState = GuiPluginEntry::Disabled;
1115  }
1116 
1117  switch (iconState)
1118  {
1120  {
1121  drawIcon = false;
1122  break;
1123  }
1125  drawIcon = false;
1126  break;
1128  button.icon = mIconDisableGuiContribution;
1129  break;
1131  button.icon = mIconEnableGuiContribution;
1132  break;
1133  }
1134  break;
1135  case 8:
1136  button.iconSize = QSize(w-4,w-4);
1137  if (plt->hasCliExtension(index))
1138  button.icon = mIconCliOptions;
1139  else
1140  drawIcon = false;
1141  break;
1142  case 10:
1143  if (plt->isHalGui(index))
1144  drawIcon = false;
1145  else
1146  {
1147  if (plt->isLoaded(index))
1148  button.icon = mIconUnload;
1149  else
1150  button.icon = mIconLoad;
1151  button.iconSize = QSize(w-12,w-12);
1152  }
1153  break;
1154  default:
1155  drawIcon = false;
1156  break;
1157  }
1158 
1159  State stat = Normal;
1160  QColor bgColor = plt->data(index,Qt::BackgroundRole).value<QColor>();
1161  if (mMouseIndex.isValid() && mMouseIndex == index)
1162  {
1163  stat = Pressed;
1164  bgColor = Qt::red;
1165  }
1166 
1167  QRect r = option.rect;
1168  int x = r.left() + r.width() - w;
1169  int y = r.top();
1170  int h = w;
1171  button.rect = QRect(x,y,w,h);
1173 
1174  if (bgColor.isValid())
1175  painter->fillRect(r,QBrush(bgColor));
1176  if (drawIcon)
1177  QApplication::style()->drawControl( QStyle::CE_PushButton, &button, painter, mTemplateButton[stat]);
1178  }
1179 
1181  {
1182  return QSize(30,30);
1183  }
1184 
1186  {
1187  QMouseEvent* mev = dynamic_cast<QMouseEvent*>(event);
1188  if (!mev) return false;
1189  switch (mev->type())
1190  {
1192  mMousePos = mev->pos();
1193  mMouseIndex = index;
1194  return true;
1196  if ((mMousePos - mev->pos()).manhattanLength()<4)
1197  {
1198  Q_EMIT buttonPressed(index);
1199  }
1200  mMousePos = QPoint();
1201  mMouseIndex = QModelIndex();
1202  return true;
1203  default:
1204  break;
1205  }
1206  return false;
1207  }
1208 }
Copyright The Montserrat Project in Original or Modified may be sold by itself Original or Modified Versions of the Font Software may be redistributed and or sold with any provided that each copy contains the above copyright notice and this license These can be included either as stand alone text human readable headers or in the appropriate machine readable metadata fields within text or binary files as long as those fields can be easily viewed by the user No Modified Version of the Font Software may use the Reserved Font endorse or advertise any Modified except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software
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)
GuiPluginManager(QWidget *parent=nullptr)
void setHilightTextColor(QColor &c)
void setUnloadIconPath(const QString &s)
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)
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)
void setGuiIconDisabledStyle(const QString &s)
void setUnloadIconStyle(const QString &s)
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)
std::set< std::string > get_plugin_names()
BasePluginInterface * get_plugin_instance(const std::string &plugin_name, bool initialize, bool silent)
std::filesystem::path get_plugin_path(std::string plugin_name)
std::vector< PluginFeature > get_plugin_features(std::string name)
bool load(const std::string &plugin_name, const std::filesystem::path &file_path_or_empty)
std::filesystem::path get_user_config_directory()
Definition: utils.cpp:171
std::vector< std::filesystem::path > get_plugin_directories()
Definition: utils.cpp:194
PluginRelay * gPluginRelay
Definition: plugin_gui.cpp:82
n
Definition: test.py:6
option(PL_GUI "PL_GUI" ON) if(PL_GUI OR BUILD_ALL_PLUGINS) cmake_minimum_required(VERSION 3.1.0) if(APPLE AND CMAKE_HOST_APPLE AND NOT Qt5_DIR) set(Qt5_DIR "/usr/local/opt/qt@5/lib/cmake") endif(APPLE AND CMAKE_HOST_APPLE AND NOT Qt5_DIR) find_package(Qt5 COMPONENTS Core REQUIRED) find_package(Qt5 COMPONENTS Widgets REQUIRED) if(Qt5Widgets_FOUND) message(VERBOSE "Qt5Widgets_INCLUDE_DIRS
Definition: CMakeLists.txt:1
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
const QAbstractItemModel * model() 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
QString cap(int nth) const const
int indexIn(const QString &str, int offset, QRegExp::CaretMode caretMode) 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
CaseInsensitive
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)