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  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";
661  QRegExp re(reStr, Qt::CaseInsensitive);
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  QString fileFmt = (re.indexIn(label) < 0)
680  ? label.remove(QChar(':'))
681  : re.cap(1) + QString(" files ");
682  if (formatMap.contains(fileFmt))
683  formatMap[fileFmt] += " *" + it.key();
684  else
685  formatMap[fileFmt] = "*" + it.key();
686  }
687  for (auto it = formatMap.constBegin(); it != formatMap.constEnd(); ++it)
688  {
689  if (!retval.isEmpty()) retval += ";;";
690  retval += it.key() + "(" + it.value() + ")";
691  }
692  return retval;
693  }
694 
696  {
697  for (GuiPluginEntry* gpe : mEntries)
698  {
699  if (gpe->isLoaded() || gpe->mFeature != ft) continue;
700  if (!extension.isEmpty())
701  {
702  bool extensionFound = false;
703  for (QString arg : gpe->mFeatureArguments)
704  {
705  if (arg==extension)
706  {
707  extensionFound = true;
708  break;
709  }
710  }
711  if (!extensionFound) continue;
712  }
713  BasePluginInterface* bpif = load(gpe->mName, gpe->mFilePath);
714  if (bpif) gpe->updateFromLoaded(bpif,false);
715  }
716  }
717 
718  BasePluginInterface* GuiPluginTable::load(const QString& pluginName, const QString& path) const
719  {
720  std::string pnam = pluginName.toStdString();
721  if (!plugin_manager::load(pnam,path.toStdString()))
722  {
723  log_warning("gui", "Error loading plugin '{}' from location '{}'",
724  pnam,path.toStdString());
725  return nullptr;
726  }
728  }
729 
731  {
732  mSettings->clear();
733  mSettings->beginWriteArray("plugins");
734  int inx = 0;
735 
736  for (GuiPluginEntry* gpe : mEntries)
737  {
738  mSettings->setArrayIndex(inx++);
739  gpe->persist(mSettings);
740  }
741  for (GuiPluginEntry* gpe : mAvoid)
742  {
743  mSettings->setArrayIndex(inx++);
744  gpe->persist(mSettings);
745  }
746  mSettings->endArray();
747  mSettings->sync();
748  }
749 
751  {
753  for (const std::string& pluginName : plugin_manager::get_plugin_names())
754  {
756  if (!bpif) continue;
757  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
758  {
759  GuiExtensionInterface* geif = dynamic_cast<GuiExtensionInterface*>(aeif);
760  if (geif) retval.insert(QString::fromStdString(pluginName),geif);
761  }
762  }
763  return retval;
764  }
765 
766  void GuiPluginTable::clearMemory()
767  {
768 
769  for (GuiPluginEntry* entry : mEntries)
770  delete entry;
771 
772  mEntries.clear();
773  mAvoid.clear();
774  mLookup.clear();
775  mSettings->deleteLater();
776  mSettings = nullptr;
777  }
778 
780  {
781  return mEntries.size();
782  }
783 
785  {
786  return 11;
787  }
788 
789  QVariant GuiPluginTable::headerData(int section, Qt::Orientation orientation, int role) const
790  {
791  if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
792  {
793  switch(section)
794  {
795  case 0: return "Name";
796  case 1: return "Description";
797  case 2: return "Filename";
798  case 3: return "Timestamp";
799  case 4: return "Dependencies";
800  case 5: return "Category";
801  case 6: return "Extensions";
802  case 7: return "GUI";
803  case 8: return "CLI";
804  case 9: return "State";
805  case 10: return QString();
806  default: return QVariant();
807  }
808  }
809  return QAbstractTableModel::headerData(section,orientation,role);
810  }
811 
813  {
814  return mEntries.at(irow);
815  }
816 
818  {
819  if (mWaitForRefresh) return;
820 
821  int irow = buttonIndex.row();
822  if (irow >= mEntries.size()) return;
823  GuiPluginEntry* gpe = mEntries.at(irow);
824  switch (buttonIndex.column())
825  {
826  case 7:
827  if (gpe->mName == "hal_gui") return;
828  if (gpe->isLoaded())
830  return;
831  case 8:
832  if (!gpe->mCliOptions.isEmpty())
834  return;
835  }
836  if (gpe->mName == "hal_gui") return;
837 
838  bool success = false;
839  if (gpe->isLoaded())
840  {
841  QStringList need = neededBy(gpe->mName);
842  if (need.isEmpty())
843  {
844  success = plugin_manager::unload(gpe->mName.toStdString());
845  if (success)
847  else
848  QMessageBox::warning(qApp->activeWindow(),"Unload failed",
849  QString("Unload of plugin %1\nrefused by plugin_manager")
850  .arg(gpe->mName));
851  }
852  else
853  {
854  QMessageBox::warning(qApp->activeWindow(),"Unload failed",
855  QString("Cannot unload plugin %1\nwhich is required by %2")
856  .arg(gpe->mName).arg(need.join(',')));
857  }
858  }
859  else
860  {
862  {
863  success = true;
865  }
866  else
867  QMessageBox::warning(qApp->activeWindow(),"Load failed",
868  QString("Could not load plugin %1\nplease check plugin_manager log")
869  .arg(gpe->mName));
870  }
871  if (success)
872  {
873  Q_EMIT dataChanged(index(irow,0),index(irow,columnCount()-1));
874  persist();
875  }
876  }
877 
878  QVariant GuiPluginTable::data(const QModelIndex &index, int role) const
879  {
880  switch (role)
881  {
882  case Qt::ForegroundRole:
883  if (mEntries.at(index.row())->isLoaded())
884  return mPluginMgr->hilightTextColor();
885  return mPluginMgr->defaultTextColor();
886  case Qt::BackgroundRole:
887  if (mEntries.at(index.row())->isLoaded())
888  return mPluginMgr->hilightBackgroundColor();
889  return QBrush();
890  case Qt::DisplayRole:
891  break; // handle below
892  default:
893  return QVariant();
894  }
895 
896  QVariant v = mEntries.at(index.row())->data(index.column());
897  if (index.column() >= 7 && index.column() <= 8)
898  return (v.toBool() ? "X" : " ");
899  return v;
900  }
901 
902  bool GuiPluginTable::isHalGui(const QModelIndex &index) const
903  {
904  if (index.row() >= mEntries.size()) return false;
905  return mEntries.at(index.row())->mName == "hal_gui";
906  }
907 
909  {
910  if (index.row() >= mEntries.size()) return GuiPluginEntry::Unknown;
911  return mEntries.at(index.row())->mGuiExtensionState;
912  }
913 
915  {
916  int irow = mLookup.value(pluginName,-1);
917  if (irow < 0 || irow >= mEntries.size()) return;
918  mEntries.at(irow)->mGuiExtensionState = state;
919  persist();
920  }
921 
923  {
924  if (index.row() >= mEntries.size()) return false;
925  return !mEntries.at(index.row())->mCliOptions.isEmpty();
926  }
927 
928  bool GuiPluginTable::isLoaded(const QModelIndex &index) const
929  {
930  if (index.row() >= mEntries.size()) return false;
931  return mEntries.at(index.row())->isLoaded();
932  }
933 
935  {
936  QStringList retval;
937  for (GuiPluginEntry* gpe : mEntries)
938  {
939  if (gpe->mName == pluginName) continue;
940  for (QString dep : gpe->mDependencies)
941  if (dep == pluginName)
942  {
943  // dependency found, check if loaded
944  if (gpe->isLoaded())
945  retval.append(gpe->mName);
946  }
947  }
948  return retval;
949  }
950 
951  //------------------------------------------------------------
952 
954  : mLoadState(NotAPlugin), mName(info.baseName()),
955  mFilePath(info.absoluteFilePath()),
956  mFileModified(info.lastModified()),
957  mFeature(FacExtensionInterface::FacUnknown),
958  mUserInterface(false),
959  mGuiExtensionState(Unknown),
960  mFileFound(true)
961  {;}
962 
964  {
965  switch (icol)
966  {
967  case 0: return mName;
968  case 1: return mDescription;
969  case 2: return QFileInfo(mFilePath).fileName();
970  case 3: return mFileModified.toString("dd.MM.yy hh:mm");
971  case 4: return mDependencies.join(' ');
972  case 5:
973  {
974  switch (mFeature)
975  {
976  case FacExtensionInterface::FacNetlistParser: return "Netlist parser";
977  case FacExtensionInterface::FacNetlistWriter: return "Netlist writer";
978  case FacExtensionInterface::FacGatelibParser: return "Gate library parser";
979  case FacExtensionInterface::FacGatelibWriter: return "Gate library writer";
980  default: break;
981  }
982  if (mUserInterface) return "HAL user interface";
983  return "Other HAL plugin";
984  }
985  case 6: return mFeatureArguments.join(' ');
986  case 7: return mGuiExtensionState;
987  case 8: return mCliOptions;
988  case 9: if (!isLoaded()) return "-";
989  else if (mExternalPath.isEmpty()) return "LOADED";
990  else return "EXTERN";
991  case 10: return (int) mLoadState;
992  }
993  return QVariant();
994  }
995 
997  {
998  if (!geif) return NotAnExtension;
999 
1000  switch (mGuiExtensionState) // enforce user setting
1001  {
1002  case Disabled:
1003  geif->set_contribution_enabled(false);
1004  break;
1005  case Enabled:
1006  geif->set_contribution_enabled(true);
1007  break;
1008  default:
1009  break;
1010  }
1011  return (geif->is_contribution_enabled() ? Enabled : Disabled);
1012  }
1013 
1014 
1015  void GuiPluginEntry::persist(QSettings* settings) const
1016  {
1017  settings->setValue("state", (int) mLoadState);
1018  settings->setValue("name", mName);
1019  settings->setValue("version", mVersion);
1020  settings->setValue("description", mDescription);
1021  settings->setValue("file_modified", mFileModified);
1022  settings->setValue("dependencies", mDependencies);
1023  settings->setValue("feature_code", (int) mFeature);
1024  settings->setValue("feature_args", mFeatureArguments);
1025  settings->setValue("user_interface", mUserInterface);
1026  settings->setValue("gui_extension_state", (int) mGuiExtensionState);
1027  settings->setValue("cli_options", mCliOptions);
1028  settings->setValue("ext_path", mExternalPath);
1029  }
1030 
1032  : mFileFound(false)
1033  {
1034  mLoadState = (LoadState) settings->value("state").toInt();
1035  mName = settings->value("name").toString();
1036  mVersion = settings->value("version").toString();
1037  mDescription = settings->value("description").toString();
1038  mFileModified = settings->value("file_modified").toDateTime();
1039  mDependencies = settings->value("dependencies").toStringList();
1040  mFeature = (FacExtensionInterface::Feature) settings->value("feature_code").toInt();
1041  mFeatureArguments = settings->value("feature_args").toStringList();
1042  mUserInterface = settings->value("user_interface").toBool();
1043  mGuiExtensionState = (GuiExtensionState) settings->value("gui_extension_state").toInt();
1044  mCliOptions = settings->value("cli_options").toString();
1045  mExternalPath = settings->value("ext_path").toString();
1047  }
1048 
1049  void GuiPluginEntry::updateFromLoaded(const BasePluginInterface *bpif, bool isUser, const QDateTime& modified)
1050  {
1051  mLoadState = isUser ? UserLoad : AutoLoad;
1052  QString desc = QString::fromStdString(bpif->get_name());
1053  if (desc != mName) mDescription = desc;
1054  if (!bpif->get_description().empty()) mDescription = QString::fromStdString(bpif->get_description());
1056  mDependencies.clear();
1057  for (std::string dep : bpif->get_dependencies())
1061  {
1062  mDescription = QString::fromStdString(feature.description);
1063  mFeature = feature.feature;
1065  for (std::string arg : feature.args)
1067  }
1068  mCliOptions.clear();
1069 
1070  GuiExtensionState newGuiExtensionState = Unknown; // not initialized
1071  for (AbstractExtensionInterface* aeif : bpif->get_extensions())
1072  {
1073  CliExtensionInterface* ceif = nullptr;
1074  GuiExtensionInterface* geif = nullptr;
1075  if (geif = dynamic_cast<GuiExtensionInterface*>(aeif))
1076  {
1077  mGuiExtensionState = newGuiExtensionState = enforceGuiExtensionState(geif);
1078  }
1079  else if ((ceif=dynamic_cast<CliExtensionInterface*>(aeif)))
1081  }
1082  if (!newGuiExtensionState) mGuiExtensionState = NotAnExtension; // no GUI extension
1083 
1084  mUserInterface = (dynamic_cast<const UIPluginInterface*>(bpif) != nullptr);
1085  if (modified.isValid()) mFileModified = modified;
1086  }
1087 
1088  //------------------------------------------------
1090  : QItemDelegate(parent)
1091  {
1092  // int bgBright[3] = {26, 46, 80};
1093  for (int i=0; i<3; i++)
1094  {
1095  mTemplateButton[i] = new QPushButton;
1096 // mTemplateButton[i]->setStyleSheet(QString("background-color : rgb(%1,%1,%1)").arg(bgBright[i]));
1097  }
1098 
1099  }
1100 
1102  {
1103  mIconLoad = gui_utility::getStyledSvgIcon(gpm->loadIconStyle(),gpm->loadIconPath());
1104  mIconUnload = gui_utility::getStyledSvgIcon(gpm->unloadIconStyle(), gpm->unloadIconPath());
1105  mIconCliOptions = gui_utility::getStyledSvgIcon(gpm->cliIconStyle(), gpm->cliIconPath());
1106  mIconEnableGuiContribution = gui_utility::getStyledSvgIcon(gpm->guiIconEnabledStyle(), gpm->guiIconEnabledPath());
1107  mIconDisableGuiContribution = gui_utility::getStyledSvgIcon(gpm->guiIconDisabledStyle(), gpm->guiIconDisabledPath());
1108  }
1109 
1111  {
1112  for (int i=0; i<3; i++)
1113  delete mTemplateButton[i];
1114  }
1115 
1116  void GuiPluginDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
1117  {
1118  const int w = 28;
1119  const GuiPluginTable* plt = dynamic_cast<const GuiPluginTable*>(index.model());
1120  if (!plt) return;
1121  QStyleOptionButton button;
1122  bool drawIcon = true;
1124  switch (index.column())
1125  {
1126  case 7:
1127  button.iconSize = QSize(w-4,w-4);
1128  iconState = plt->guiExtensionState(index);
1129 
1130  if (!iconState)
1131  {
1132  QString pluginName = plt->at(index.row())->name();
1133  GuiExtensionInterface* geif = GuiPluginManager::getGuiExtensions().value(pluginName);
1134  if (!plt->isLoaded(index))
1135  iconState = GuiPluginEntry::Unknown;
1136  else if (!geif)
1137  iconState = GuiPluginEntry::NotAnExtension;
1138  else if (geif->is_contribution_enabled())
1139  iconState = GuiPluginEntry::Enabled;
1140  else
1141  iconState = GuiPluginEntry::Disabled;
1142  }
1143 
1144  switch (iconState)
1145  {
1147  {
1148  drawIcon = false;
1149  break;
1150  }
1152  drawIcon = false;
1153  break;
1155  button.icon = mIconDisableGuiContribution;
1156  break;
1158  button.icon = mIconEnableGuiContribution;
1159  break;
1160  }
1161  break;
1162  case 8:
1163  button.iconSize = QSize(w-4,w-4);
1164  if (plt->hasCliExtension(index))
1165  button.icon = mIconCliOptions;
1166  else
1167  drawIcon = false;
1168  break;
1169  case 10:
1170  if (plt->isHalGui(index))
1171  drawIcon = false;
1172  else
1173  {
1174  if (plt->isLoaded(index))
1175  button.icon = mIconUnload;
1176  else
1177  button.icon = mIconLoad;
1178  button.iconSize = QSize(w-12,w-12);
1179  }
1180  break;
1181  default:
1182  drawIcon = false;
1183  break;
1184  }
1185 
1186  State stat = Normal;
1187  QColor bgColor = plt->data(index,Qt::BackgroundRole).value<QColor>();
1188  if (mMouseIndex.isValid() && mMouseIndex == index)
1189  {
1190  stat = Pressed;
1191  bgColor = Qt::red;
1192  }
1193 
1194  QRect r = option.rect;
1195  int x = r.left() + r.width() - w;
1196  int y = r.top();
1197  int h = w;
1198  button.rect = QRect(x,y,w,h);
1200 
1201  if (bgColor.isValid())
1202  painter->fillRect(r,QBrush(bgColor));
1203  if (drawIcon)
1204  QApplication::style()->drawControl( QStyle::CE_PushButton, &button, painter, mTemplateButton[stat]);
1205  }
1206 
1208  {
1209  return QSize(30,30);
1210  }
1211 
1213  {
1214  QMouseEvent* mev = dynamic_cast<QMouseEvent*>(event);
1215  if (!mev) return false;
1216  switch (mev->type())
1217  {
1219  mMousePos = mev->pos();
1220  mMouseIndex = index;
1221  return true;
1223  if ((mMousePos - mev->pos()).manhattanLength()<4)
1224  {
1225  Q_EMIT buttonPressed(index);
1226  }
1227  mMousePos = QPoint();
1228  mMouseIndex = QModelIndex();
1229  return true;
1230  default:
1231  break;
1232  }
1233  return false;
1234  }
1235 }
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:172
std::vector< std::filesystem::path > get_plugin_directories()
Definition: utils.cpp:195
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.5.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)