HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
module_pins_tree.cpp
Go to the documentation of this file.
2 
3 #include "gui/gui_globals.h"
12 
13 #include <QApplication>
14 #include <QClipboard>
15 #include <QHeaderView>
16 #include <QList>
17 #include <QMenu>
18 #include <QMessageBox>
19 #include <QQueue>
20 #include <QSet>
21 
22 namespace hal
23 {
24  ModulePinsTree::ModulePinsTree(QWidget* parent) : QTreeView(parent), mPortModel(new ModulePinsTreeModel(this)), mModuleID(-1)
25  {
28  // all rows are plain single line text, so the view must not measure each of them separately
30  //setSelectionMode(QAbstractItemView::NoSelection);
32  //setSelectionMode(QAbstractItemView::SingleSelection);
36  setModel(mPortModel);
37 
38  setDragDropMode(DragDropMode::DragDrop);
39  setDragEnabled(true);
40  setAcceptDrops(true);
41  viewport()->setAcceptDrops(true);
43 
44  //connections
46  connect(mPortModel, &ModulePinsTreeModel::numberOfPortsChanged, this, &ModulePinsTree::handleNumberOfPortsChanged);
49  }
50 
52  {
53  mExpandedGroupIds.clear();
54  mSelectedGroupIds.clear();
55  mSelectedPinIds.clear();
56 
57  for (int irow = 0; irow < mPortModel->rowCount(); irow++)
58  {
59  QModelIndex inx = mPortModel->index(irow, 0);
60  if (!isExpanded(inx))
61  continue;
62  if (ModulePinsTreeItem* grp = dynamic_cast<ModulePinsTreeItem*>(mPortModel->getItemFromIndex(inx)))
63  mExpandedGroupIds.insert(grp->id());
64  }
65 
66  for (const QModelIndex& inx : selectionModel()->selectedRows())
67  {
68  ModulePinsTreeItem* item = dynamic_cast<ModulePinsTreeItem*>(mPortModel->getItemFromIndex(inx));
69  if (!item)
70  continue;
71  if (item->itemType() == ModulePinsTreeItem::Group)
72  mSelectedGroupIds.insert(item->id());
73  else if (item->itemType() == ModulePinsTreeItem::Pin)
74  mSelectedPinIds.insert(item->id());
75  }
76  }
77 
79  {
80  if (mExpandedGroupIds.isEmpty() && mSelectedGroupIds.isEmpty() && mSelectedPinIds.isEmpty())
81  return;
82 
83  QItemSelection restored;
84 
85  for (int irow = 0; irow < mPortModel->rowCount(); irow++)
86  {
87  QModelIndex groupInx = mPortModel->index(irow, 0);
88  ModulePinsTreeItem* group = dynamic_cast<ModulePinsTreeItem*>(mPortModel->getItemFromIndex(groupInx));
89  if (!group)
90  continue;
91 
92  // a bulk change might have removed pins and pin groups, whatever is gone just stays unrestored
93  if (mExpandedGroupIds.contains(group->id()))
94  setExpanded(groupInx, true);
95  if (mSelectedGroupIds.contains(group->id()))
96  restored.select(groupInx, groupInx);
97 
98  if (mSelectedPinIds.isEmpty())
99  continue;
100 
101  for (int prow = 0; prow < mPortModel->rowCount(groupInx); prow++)
102  {
103  QModelIndex pinInx = mPortModel->index(prow, 0, groupInx);
104  ModulePinsTreeItem* pin = dynamic_cast<ModulePinsTreeItem*>(mPortModel->getItemFromIndex(pinInx));
105  if (pin && mSelectedPinIds.contains(pin->id()))
106  restored.select(pinInx, pinInx);
107  }
108  }
109 
110  if (!restored.isEmpty())
112 
113  mExpandedGroupIds.clear();
114  mSelectedGroupIds.clear();
115  mSelectedPinIds.clear();
116  }
117 
119  {
120  Module* m = gNetlist->get_module_by_id(moduleID);
121  if (!m)
122  return;
123 
124  mPortModel->clear();
125  mPortModel->setModule(m);
126  mModuleID = moduleID;
127  }
128 
130  {
131  if (!m)
132  return;
133 
134  setModule(m->get_id());
135  }
136 
138  {
139  mPortModel->clear();
140  mModuleID = -1;
141  }
142 
144  {
145  return mModuleID;
146  }
147 
149  {
150  QModelIndex clickedIndex = indexAt(pos);
151  if (!clickedIndex.isValid())
152  return;
153 
154  //all relevant information
155  ModulePinsTreeItem* clickedItem = static_cast<ModulePinsTreeItem*>(mPortModel->getItemFromIndex(clickedIndex));
156  Net* n = mPortModel->getNetFromItem(clickedItem);
158  u32 modId = mPortModel->getRepresentedModuleId();
159  auto mod = gNetlist->get_module_by_id(modId);
160  QList<BaseTreeItem*> selectedPins;
161  std::pair<bool, int> sameGroup;
162  bool onlyPins;
163  std::tie(selectedPins, sameGroup, onlyPins) = getSelectedPins();
164  int itemId = clickedItem->id();
165  QMenu menu;
166 
167  //shared plaintext entries: NAME, DIRECTION, TYPE (shared with pins and groups)
168  menu.addAction("Name to clipboard", [clickedItem]() { QApplication::clipboard()->setText(clickedItem->getData(ModulePinsTreeModel::sNameColumn).toString()); });
169  menu.addAction("Direction to clipboard", [clickedItem]() { QApplication::clipboard()->setText(clickedItem->getData(ModulePinsTreeModel::sDirectionColumn).toString()); });
170  menu.addAction("Type to clipboard", [clickedItem]() { QApplication::clipboard()->setText(clickedItem->getData(ModulePinsTreeModel::sTypeColumn).toString()); });
171 
172  menu.addSection("Misc");
173 
174  //shared context menu entry to add to existing groups
175  bool addToExistingActionPossible = true;
176 // for (auto pingroup : mod->get_pin_groups())
177 // {
178 // if (pingroup->size() > 1) //at least one pingroup should have at least 2 items
179 // {
180 // addToExistingActionPossible = true;
181 // break;
182 // }
183 // }
184  if (addToExistingActionPossible)
185  {
186  menu.addAction("Add selection to existing pin group", [selectedPins, mod]() {
187  PingroupSelectorDialog psd("Pingroup selector", "Select pingroup", mod, false);
188  if (psd.exec() == QDialog::Accepted)
189  {
191  auto* pinGroup = mod->get_pin_group_by_id(psd.getSelectedGroupId());
192  if (pinGroup == nullptr)
193  return;
194  for (auto item : selectedPins)
195  {
196  auto* pin = mod->get_pin_by_id(static_cast<ModulePinsTreeItem*>(item)->id());
197  if (pin == nullptr)
198  return;
199  pins.append(pin->get_id());
200  }
201  ActionPingroup* act = ActionPingroup::addPinsToExistingGroup(mod,pinGroup->get_id(),pins);
202  if (act) act->exec();
203  }
204  });
205  }
206 
207  if (clickedItem->itemType() == ModulePinsTreeItem::Group) //group specific context, own helper function? (returns at the end)
208  {
209  menu.addAction("Change name", [itemId, mod, name]() {
210  PinGroup<ModulePin>* pg = mod->get_pin_group_by_id(itemId);
211  if (pg == nullptr) return;
212 
213  InputDialog ipd("Change pin group name", "New group name", name);
214  if (ipd.exec() == QDialog::Accepted)
215  {
216  if (ipd.textValue().isEmpty())
217  return;
218 
221  act->exec();
222  }
223  });
224 
225  menu.addAction("Change type", [itemId, mod, name]() {
226  PinGroup<ModulePin>* pg = mod->get_pin_group_by_id(itemId);
227  if (pg == nullptr) return;
228 
229  QStringList types;
230  for (auto const& [k, v] : EnumStrings<PinType>::data)
231  types << QString::fromStdString(v);
232 
233  ComboboxDialog cbd("Change type", "Select type for pin group " + name, types);
234 
235  if (cbd.exec() == QDialog::Accepted)
236  {
237  PinType ptype = enum_from_string<PinType>(cbd.textValue().toStdString(),PinType::none);
238  ActionPingroup* act = ActionPingroup::changePinGroupType(mod,itemId,(int)ptype);
239  if (act) act->exec();
240  }
241  });
242  menu.addAction("Toggle ascending/descending", [itemId, mod](){
244  if (act) act->exec();
245  });
246  menu.addAction("Autmatically rename pins", [itemId, mod](){
248  if (act) act->exec();
249  });
250  menu.addAction("Delete pin group", [itemId, mod]() {
251  auto* pinGroup = mod->get_pin_group_by_id(itemId);
252  if (pinGroup != nullptr)
253  {
255  if (act) act->exec();
256  }
257  });
258 
259  if (selectionModel()->selectedRows().size() > 1)
260  appendMultiSelectionEntries(menu, modId);
261 
262  menu.addSection("Python");
263  menu.addAction(QIcon(":/icons/python"), "Get pin group", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinGroup(modId, itemId)); });
264  menu.move(mapToGlobal(pos));
265  menu.exec();
266  return;
267  }
268 
269  //menu.addSection("Misc");
270  if (n) //should never be nullptr, but you never know
271  {
272  menu.addAction("Change name", [mod, name, itemId]() {
273  InputDialog ipd("Change pin name", "New pin name", name);
274  if (ipd.exec() == QDialog::Accepted)
275  {
276  if (ipd.textValue().isEmpty())
277  return;
278  auto* pin = mod->get_pin_by_id(itemId);
279  if (pin != nullptr)
280  {
281  ActionPingroup* act = new ActionPingroup(PinActionType::PinRename,pin->get_id(),ipd.textValue());
283  act->exec();
284  }
285  }
286  });
287 
288  menu.addAction("Change type", [mod, name, itemId]() {
289  auto* pin = mod->get_pin_by_id(itemId);
290  if (pin == nullptr)
291  return;
292 
293  QStringList types;
294  for (auto const& [k, v] : EnumStrings<PinType>::data)
295  types << QString::fromStdString(v);
296 
297  ComboboxDialog cbd("Pin Types", "Select pin type", types);
298 
299  if (cbd.exec() == QDialog::Accepted)
300  {
301  PinType ptype = enum_from_string<PinType>(cbd.textValue().toStdString(),PinType::none);
302 
303  ActionPingroup* act = new ActionPingroup(PinActionType::PinTypeChange,pin->get_id(),"",(int)ptype);
305  act->exec();
306  }
307  });
308  menu.addAction("Set focus to pin", [this, mod, itemId]() {
309  auto* pin = mod->get_pin_by_id(itemId);
310  if (pin && (pin->get_direction() == PinDirection::input || pin->get_direction() == PinDirection::output)) // TODO : what about inout?
311  {
312  auto pins = pin->get_direction() == PinDirection::input ? mod->get_input_pins() : mod->get_output_pins();
313  int cnt = 0;
314  for (auto* testPin : pins)
315  {
316  if (testPin == pin)
317  {
318  SelectionRelay::Subfocus sfoc = pin->get_direction() == PinDirection::input
320  gSelectionRelay->setFocus(SelectionRelay::ItemType::Module, mod->get_id(), sfoc, cnt);
322  break;
323  }
324  ++cnt;
325  }
326  }
327  });
328  menu.addAction("Add net to current selection", [this, n]() {
331  });
332  menu.addAction("Set net as current selection", [this, n]() {
336  });
337  }
338  //can be both single(simple right-click, no real selection) and multi-selection
339  if (sameGroup.first && mod->get_pin_group_by_id(sameGroup.second)->size() > 1)
340  {
341  menu.addAction("Remove selection from group", [selectedPins, mod /*, sameGroup*/]() {
343  for (auto item : selectedPins)
344  pins.append(static_cast<ModulePinsTreeItem*>(item)->id());
345 
347  if (act) act->exec();
348  });
349  }
350 
351  //multi-selection (part of misc)
352  if (selectionModel()->selectedRows().size() > 1)
353  appendMultiSelectionEntries(menu, modId);
354 
355  menu.addSection("Python");
356  if(clickedItem->itemType()==ModulePinsTreeItem::Pin)
357  menu.addAction(QIcon(":/icons/python"), "Get pin", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinById(modId, itemId)); });
358  else
359  menu.addAction(QIcon(":/icons/python"), "Get group", [modId, itemId]() { QApplication::clipboard()->setText(PyCodeProvider::pyCodeModulePinGroup(modId, itemId)); });
360 
361  menu.move(mapToGlobal(pos));
362  menu.exec();
363  }
364 
365  void ModulePinsTree::handleNumberOfPortsChanged(int newNumberPorts)
366  {
367  Q_EMIT updateText(QString("Pins (%1)").arg(newNumberPorts));
368  }
369 
370  void ModulePinsTree::appendMultiSelectionEntries(QMenu& menu, int modId)
371  {
372  QList<BaseTreeItem*> selectedPins;
373  std::pair<bool, int> sameGroup;
374  bool onlyPins;
375  std::tie(selectedPins, sameGroup, onlyPins) = getSelectedPins();
376  if (selectedPins.size() > 1)
377  {
378  menu.addAction("Add objects to new pin group", [selectedPins, modId]() {
379  InputDialog ipd("Pingroup name", "New pingroup name", "ExampleName");
380  if (ipd.exec() == QDialog::Accepted && !ipd.textValue().isEmpty())
381  {
383  Module* mod = gNetlist->get_module_by_id(modId);
384  for (auto item : selectedPins)
385  {
386  auto* pin = mod->get_pin_by_id(static_cast<ModulePinsTreeItem*>(item)->id());
387  if (pin == nullptr)
388  return;
389  pins.append(pin->get_id());
390  }
391 
392  ActionPingroup* act = ActionPingroup::addPinsToNewGroup(mod,ipd.textValue(),pins);
393  if (act) act->exec();
394  }
395  });
396  }
397  }
398 
399  std::tuple<QList<BaseTreeItem*>, std::pair<bool, int>, bool> ModulePinsTree::getSelectedPins()
400  {
401  QList<BaseTreeItem*> selectedPins; //ordered
402  QSet<BaseTreeItem*> alreadyProcessedPins; //only for performance purposes
403  bool sameGroup = true;
404  bool onlyPins = true;
405  int groupId = -1;
406  for (auto index : selectionModel()->selectedRows())
407  {
408  ModulePinsTreeItem* item = static_cast<ModulePinsTreeItem*>(mPortModel->getItemFromIndex(index));
409  if (item->itemType() == ModulePinsTreeItem::Pin)
410  {
411  if (!alreadyProcessedPins.contains(item))
412  {
413  selectedPins.append(item);
414  alreadyProcessedPins.insert(item);
415  }
416  }
417  else if (item->itemType() == ModulePinsTreeItem::Group)
418  {
419  onlyPins = false;
420  for (auto pinItem : item->getChildren())
421  {
422  if (!alreadyProcessedPins.contains(pinItem))
423  {
424  selectedPins.append(pinItem);
425  alreadyProcessedPins.insert(pinItem);
426  }
427  }
428  }
429  }
430  //Check if all pins are from the same group (if no pin is selected it is from the same group)
431  if (!selectedPins.isEmpty())
432  {
433  auto mod = gNetlist->get_module_by_id(mModuleID);
434  auto* firstPin = mod->get_pin_by_id(static_cast<ModulePinsTreeItem*>(selectedPins.front())->id());
435  groupId = firstPin->get_group().first->get_id();
436  for (auto pinTreeItem : selectedPins)
437  {
438  auto pin = mod->get_pin_by_id(static_cast<ModulePinsTreeItem*>(pinTreeItem)->id());
439  if (groupId != (int)pin->get_group().first->get_id())
440  {
441  sameGroup = false;
442  break;
443  }
444  }
445  groupId = sameGroup ? groupId : -1;
446  }
447  return std::make_tuple(selectedPins, std::make_pair(sameGroup, groupId), onlyPins);
448  }
449 
450 } // namespace hal
Pingroup user actions.
static ActionPingroup * deletePinGroup(const Module *m, u32 grpId)
static ActionPingroup * addPinsToExistingGroup(const Module *m, u32 grpId, QList< u32 > pinIds, int pinRow=-1)
static ActionPingroup * automaticallyRenamePins(const Module *m, u32 grpId)
static ActionPingroup * toggleAscendingGroup(const Module *m, u32 grpId)
static ActionPingroup * addPinsToNewGroup(const Module *m, const QString &name, QList< u32 > pinIds, int grpRow=-1)
static ActionPingroup * removePinsFromGroup(const Module *m, QList< u32 > pinIds)
bool exec() override
static ActionPingroup * changePinGroupType(const Module *m, u32 grpId, int ptype)
const std::pair< PinGroup< T > *, i32 > & get_group() const
Definition: base_pin.h:160
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
BaseTreeItem * getItemFromIndex(QModelIndex index) const
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
QString textValue() const
Generic input dialog for user input.
Definition: input_dialog.h:51
QString textValue() const
ModulePin * get_pin_by_id(const u32 id) const
Definition: module.cpp:985
u32 get_id() const
Definition: module.cpp:82
void updateText(const QString &newHeadline)
void setModule(u32 moduleID)
void handleContextMenuRequested(const QPoint &pos)
ModulePinsTree(QWidget *parent=nullptr)
QVariant getData(int column) const override
A model to represent the ports of a module.
Net * getNetFromItem(ModulePinsTreeItem *item)
void numberOfPortsChanged(const int newNumber)
Definition: net.h:58
u32 get_id() const
Definition: net.cpp:88
Module * get_module_by_id(u32 module_id) const
Definition: netlist.cpp:613
static QString pyCodeModulePinGroup(u32 moduleId, u32 groupId)
static QString pyCodeModulePinById(u32 moduleId, u32 pinId)
void setFocus(ItemType ftype, u32 fid, Subfocus sfoc=Subfocus::None, u32 sfinx=0)
void relaySelectionChanged(void *sender)
virtual void setObject(const UserActionObject &obj)
Definition: user_action.cpp:32
The UserActionObject class represents a single object used in UserAction.
uint32_t u32
Definition: defines.h:41
Definition: defines.h:45
SelectionRelay * gSelectionRelay
Definition: plugin_gui.cpp:83
Netlist * gNetlist
Definition: gui_globals.h:69
PinType
Definition: pin_type.h:36
std::vector< PinInformation > pins
std::string name
void setDragDropMode(QAbstractItemView::DragDropMode behavior)
void setDragEnabled(bool enable)
void setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior)
void setSelectionMode(QAbstractItemView::SelectionMode mode)
QItemSelectionModel * selectionModel() const const
void setDropIndicatorShown(bool enable)
QWidget * viewport() const const
void setText(const QString &text, QClipboard::Mode mode)
virtual int exec()
QClipboard * clipboard()
void setStretchLastSection(bool stretch)
void select(const QModelIndex &topLeft, const QModelIndex &bottomRight)
virtual void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
QModelIndexList selectedRows(int column) const const
void append(const T &value)
T & front()
bool isEmpty() const const
int size() const const
QAction * addAction(const QString &text)
QAction * addSection(const QString &text)
QAction * exec()
bool isValid() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void clear()
bool contains(const T &value) const const
QSet::iterator insert(const T &value)
bool isEmpty() const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
std::string toStdString() const const
CustomContextMenu
QHeaderView * header() const const
virtual QModelIndex indexAt(const QPoint &point) const const override
bool isExpanded(const QModelIndex &index) const const
void setExpanded(const QModelIndex &index, bool expanded)
virtual void setModel(QAbstractItemModel *model) override
void setUniformRowHeights(bool uniform)
QString toString() const const
void setAcceptDrops(bool on)
void setContextMenuPolicy(Qt::ContextMenuPolicy policy)
void customContextMenuRequested(const QPoint &pos)
void setFocusPolicy(Qt::FocusPolicy policy)
QPoint mapToGlobal(const QPoint &pos) const const
void move(int x, int y)
void setSizePolicy(QSizePolicy)