HAL  v4.5.0-133-g64838ea8d
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
graph_graphics_view.cpp
Go to the documentation of this file.
2 
23 #include "gui/gui_globals.h"
24 #include "gui/gui_utils/netlist.h"
42 #include "hal_core/netlist/gate.h"
45 #include "hal_core/netlist/net.h"
48 
49 #include <QAction>
50 #include <QApplication>
51 #include <QColorDialog>
52 #include <QDrag>
53 #include <QInputDialog>
54 #include <QLabel>
55 #include <QLineEdit>
56 #include <QList>
57 #include <QMenu>
58 #include <QMessageBox>
59 #include <QMimeData>
60 #include <QPixmap>
61 #include <QScrollBar>
62 #include <QStyleOptionGraphicsItem>
63 #include <QWheelEvent>
64 #include <QWidgetAction>
65 #include <QDebug>
66 #include <algorithm>
67 #include <qmath.h>
68 
69 
70 namespace hal
71 {
72  const QString GraphGraphicsView::sAssignToGrouping("Assign to grouping ");
73 
75  : QGraphicsView(parent), mGraphWidget(parent),
76  mMinimapEnabled(false), mGridEnabled(true), mGridClustersEnabled(true),
77  mGridType(GraphicsScene::GridType::Dots),
78  mDragController(new DragController(mGraphWidget,this)),
79  mDragModifier(Qt::KeyboardModifier::AltModifier),
80  mPanModifier(Qt::KeyboardModifier::ShiftModifier),
81  mZoomModifier(Qt::NoModifier),
82  mZoomFactorBase(1.0015)
83  {
84  connect(gSelectionRelay, &SelectionRelay::subfocusChanged, this, &GraphGraphicsView::conditionalUpdate);
85  connect(this, &GraphGraphicsView::customContextMenuRequested, this, &GraphGraphicsView::showContextMenu);
86 
90 
91  setAcceptDrops(true);
92  setMouseTracking(true);
93  }
94 
95  void GraphGraphicsView::conditionalUpdate()
96  {
97  if (QStyleOptionGraphicsItem::levelOfDetailFromTransform(transform()) >= graph_widget_constants::sGateMinLod)
98  update();
99  }
100 
102  {
104  if (!ctx) return;
109 
110  // delete context/view if nothing left to show
111  QSet<u32> remainingMods = ctx->modules() - gSelectionRelay->selectedModules();
112  QSet<u32> remainingGats = ctx->gates() - gSelectionRelay->selectedGates();
113  if (remainingMods.isEmpty() && remainingGats.isEmpty())
114  {
115  UserActionCompound* compound = new UserActionCompound;
116  compound->setUseCreatedObject();
117  act->setObjectLock(true);
118  compound->addAction(act);
119  compound->addAction(new ActionDeleteObject);
120  compound->exec();
121  }
122  else
123  act->exec();
124  }
125 
126  void GraphGraphicsView::handleIsolationViewAction()
127  {
128  auto selected_modules = gSelectionRelay->selectedModules();
129  auto selected_gates = gSelectionRelay->selectedGates();
130 
131  // When there is only one module selected, check if there is a context connected to that module
132  // If yes, select the context
133  // If no, create a new context and connect the module to the context
134  if (selected_modules.size() == 1 && selected_gates.empty())
135  {
136  u32 module_id = *selected_modules.begin();
137  auto module_context = gGraphContextManager->getContextByExclusiveModuleId(module_id);
138  if (module_context)
139  {
142  }
143  else
144  {
145  UserActionCompound* act = new UserActionCompound;
146  act->setUseCreatedObject();
147  QString name = QString::fromStdString(gNetlist->get_module_by_id(module_id)->get_name()) + " (ID: " + QString::number(module_id) + ")";
148  act->addAction(new ActionCreateObject(UserActionObjectType::ContextView, name));
149  act->addAction(new ActionAddItemsToObject(selected_modules, selected_gates));
150  act->exec();
151  GraphContext* context = gGraphContextManager->getContextById(act->object().id());
152  context->setDirty(false);
153  context->setExclusiveModuleId(module_id);
154  }
155  return;
156  }
157 
158  QString name = gGraphContextManager->nextViewName("Isolated View");
159  UserActionCompound* act = new UserActionCompound;
160  act->setUseCreatedObject();
161  act->addAction(new ActionCreateObject(UserActionObjectType::ContextView, name));
162  act->addAction(new ActionAddItemsToObject(selected_modules, selected_gates));
163  act->exec();
164  GraphContext* context = gGraphContextManager->getContextById(act->object().id());
165  context->setDirty(false);
166  }
167 
168  void GraphGraphicsView::adjustMinScale()
169  {
170  if (!scene())
171  return;
172  mMinScale = std::min(viewport()->width() / scene()->width(), viewport()->height() / scene()->height());
173  }
174 
175  void GraphGraphicsView::handleAddCommentAction()
176  {
177  auto action = dynamic_cast<QAction*>(sender());
178  if(!action) return;
179 
180  auto node = action->data().value<Node>();
181  CommentDialog commentDialog("New Comment");
182  if(commentDialog.exec() == QDialog::Accepted)
183  gCommentManager->addComment(new CommentEntry(node, commentDialog.getText(), commentDialog.getHeader()));
184  commentDialog.close();
185  }
186 
188  {
190 
191  // USE CONSISTENT METHOD NAMES
193  GraphicsScene::setGridEnabled(mGridEnabled);
194  GraphicsScene::setGridClustersEnabled(mGridClustersEnabled);
195  GraphicsScene::setGridType(mGridType);
196 
199 
204 
206  }
207 
209  {
210  if (event->button() != Qt::LeftButton)
211  return;
212 
213  QGraphicsItem* item = itemAt(event->pos());
214  if (!item) return;
215 
216  CommentSpeechBubble* csb = dynamic_cast<CommentSpeechBubble*>(item);
217  if (csb)
218  {
219  csb->mouseDoubleClickEvent(nullptr);
220  return;
221  }
222 
223  GraphicsItem* git = dynamic_cast<GraphicsItem*>(itemAt(event->pos()));
224 
225  if (!git || git->itemType() != ItemType::Module)
226  return;
227 
228  Q_EMIT moduleDoubleClicked(git->id());
229  }
230 
232  {
233  Q_UNUSED(rect)
234 
235 #ifdef GUI_DEBUG_GRID
236  if(mGraphWidget->getContext()->scene()->debugGridEnabled())
237  debugDrawLayouterGridpos(painter);
238 #endif
239 
240  if (!mMinimapEnabled)
241  return;
242 
243  QRectF map(viewport()->width() - 210, viewport()->height() - 130, 200, 120);
244  painter->resetTransform();
245  painter->fillRect(map, QColor(0, 0, 0, 170));
246  }
247 
248 #ifdef GUI_DEBUG_GRID
249  void GraphGraphicsView::debugDrawLayouterGridpos(QPainter* painter)
250  {
251  painter->resetTransform();
252  painter->setPen(QPen(Qt::magenta));
253  QString pos_str = QString("(%1, %2)").arg(m_debug_gridpos.x()).arg(m_debug_gridpos.y());
254  painter->drawText(QPoint(25, 25), pos_str);
255  }
256 #endif
257 
259  {
260  // it the clicked item is a speechbubble, simply return so that it does
261  // not change / clear the current selection, double-click however works fine
262  if(dynamic_cast<CommentSpeechBubble*>(itemAt(event->pos())))
263  return;
264 
265  if ((event->modifiers() == mPanModifier && event->button() == Qt::LeftButton) ||
267  {
268  mMovePosition = event->pos();
269  }
270  else if (event->button() == Qt::LeftButton)
271  {
272  GraphicsNode* item = dynamic_cast<GraphicsNode*>(itemAt(event->pos()));
273  if (item && itemDraggable(item))
274  {
275  mDragController->set(item, event->pos());
276  }
277  else
278  {
279  mDragController->clear();
280  }
281 
282  // we still need the normal mouse logic for single clicks
283  mousePressEventNotItemDrag(event);
284  }
285  else
286  mousePressEventNotItemDrag(event);
287  }
288 
289  void GraphGraphicsView::mousePressEventNotItemDrag(QMouseEvent *event)
290  {
292  GraphicsScene* sc = dynamic_cast<GraphicsScene*>(scene());
293  if (sc) sc->setMousePressed(true);
294  }
295 
296 
298  {
299  GraphicsScene* sc = dynamic_cast<GraphicsScene*>(scene());
300  if (sc) sc->setMousePressed(false);
302  }
303 
305  {
306  if (!scene())
307  return;
308 
309  QPointF delta = mTargetViewportPos - event->pos();
310 
311  if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5)
312  {
313  mTargetViewportPos = event->pos();
314  mTargetScenePos = mapToScene(event->pos());
315  }
316 
317  if ((event->buttons().testFlag(Qt::LeftButton) && event->modifiers() == mPanModifier) ||
319  {
321  QScrollBar* vBar = verticalScrollBar();
322  QPoint delta_move = event->pos() - mMovePosition;
323  mMovePosition = event->pos();
324  hBar->setValue(hBar->value() + (isRightToLeft() ? delta_move.x() : -delta_move.x()));
325  vBar->setValue(vBar->value() - delta_move.y());
326  }
327  else if (event->buttons().testFlag(Qt::LeftButton))
328  {
329  if (mDragController->hasDragged(event->pos()))
330  {
331  QDrag* drag = new QDrag(this);
332  QMimeData* mimeData = new QMimeData;
333 
334  // the drop side recognises the drag by its source and action, the mime data only has to exist; a text
335  // would be drawn next to the cursor by the system, so a private type without content is used instead
336  mimeData->setData("application/x-hal-graph-node", QByteArray());
337  drag->setMimeData(mimeData);
338 
339  // the scene draws the node being moved itself, the system should draw nothing
340  QPixmap nothing(1, 1);
341  nothing.fill(Qt::transparent);
342  drag->setPixmap(nothing);
343 
344  // enable DragMoveEvents until mouse released
345  drag->exec(Qt::MoveAction);
346  }
347  }
348 #ifdef GUI_DEBUG_GRID
349  debugShowLayouterGridpos(event->pos());
350 #endif
351 
353  }
354 
356  {
357  if (event->source() == this && event->proposedAction() == Qt::MoveAction)
358  {
359  mDragController->enterDrag(event->keyboardModifiers() == mDragModifier);
360  event->acceptProposedAction();
361  }
362  else
363  {
364  //causes a bug where dockbar-buttons just disappear instead of snapping back when dragged into the view
365 // QGraphicsView::dragEnterEvent(event);
366  }
367  }
368 
370  {
371  Q_UNUSED(event)
372  mDragController->clear();
373  }
374 
375  void GraphGraphicsView::dragPan(float dpx, float dpy)
376  {
377  if (dpx != 0)
378  {
380  int hValue = hBar->value() + 10*dpx;
381  if (hValue < hBar->minimum()) hBar->setMinimum(hValue);
382  if (hValue > hBar->maximum()) hBar->setMaximum(hValue);
383  hBar->setValue(hValue);
384  }
385  if (dpy != 0)
386  {
387  QScrollBar* vBar = verticalScrollBar();
388  int vValue = vBar->value() + 10*dpy;
389  if (vValue < vBar->minimum()) vBar->setMinimum(vValue);
390  if (vValue > vBar->maximum()) vBar->setMaximum(vValue);
391  vBar->setValue(vValue);
392  }
393  }
394 
396  {
397  if (event->source() == this && event->proposedAction() == Qt::MoveAction)
398  {
399  QPair<QPoint,QPointF> snap = closestLayouterPos(mapToScene(event->pos()));
400 
401  mDragController->move(event->pos(),event->keyboardModifiers() == mDragModifier,snap.first);
402 
403  QPoint p = event->pos() - viewport()->geometry().topLeft();
404  float rx = 100. * p.x() / viewport()->geometry().width();
405  float ry = 100. * p.y() / viewport()->geometry().height();
406 // qDebug() << "move it" << event->pos() << viewport()->geometry() << rx << ry;
407  float dpx = 0;
408  float dpy = 0;
409  if (rx < 10) dpx = -10+rx;
410  if (rx > 90) dpx = rx-90;
411  if (ry < 10) dpy = -10+ry;
412  if (ry > 90) dpy = ry-90;
413  if (dpx !=0 || dpy != 0)
414  dragPan(dpx, dpy);
415  }
416  }
417 
419  {
420  if (event->source() == this && event->proposedAction() == Qt::MoveAction)
421  {
422  event->acceptProposedAction();
423  if (mDragController->isDropAllowed())
424  {
425  GridPlacement* plc = mDragController->finalGridPlacement();
426  GraphContext* context = mGraphWidget->getContext();
427  GraphLayouter* layouter = context->getLayouter();
428  assert(layouter->done()); // ensure grid stable
429  ActionMoveNode* act = new ActionMoveNode(context->id(),plc);
430  if (act->exec())
431  context->setDirty(true);
432  delete plc;
433  }
434  }
435  else
436  {
438  }
439  mDragController->clear();
440  }
441 
443  {
444  if (QApplication::keyboardModifiers() == mZoomModifier)
445  {
446  if (event->angleDelta().y() != 0)
447  {
448  qreal angle = event->angleDelta().y();
449  qreal factor = qPow(mZoomFactorBase, angle);
450  gentleZoom(factor);
451  }
452  }
453  }
454 
456  {
457  switch (event->key())
458  {
459  case Qt::Key_Space: {
460  //qDebug() << "Space pressed";
461  }
462  break;
463  }
464 
465  event->ignore();
466  }
467 
469  {
470  switch (event->key())
471  {
472  case Qt::Key_Space: {
473  //qDebug() << "Space released";
474  }
475  break;
476  }
477 
478  event->ignore();
479  }
480 
482  {
484  adjustMinScale();
485  }
486 
487  void GraphGraphicsView::showContextMenu(const QPoint& pos)
488  {
489  GraphicsScene* s = static_cast<GraphicsScene*>(scene());
490 
491  if (!s)
492  return;
493 
494  QMenu context_menu(this);
495  QAction* action;
496 
497  QGraphicsItem* item = itemAt(pos);
498  bool isGate = false;
499  bool isModule = false;
500  bool isNet = false;
501 
502  // otherwise crashes, speechbubbles do not need a context menu (for now)
503  // add ItemType::SpeechBubble so that it can be handled better?
504  if(dynamic_cast<CommentSpeechBubble*>(item)) return;
505 
506  bool isMultiGates = gSelectionRelay->selectedGates().size() > 1 &&
508  if (item)
509  {
510  mItem = static_cast<GraphicsItem*>(item);
511  isGate = mItem->itemType() == ItemType::Gate;
512  isModule = mItem->itemType() == ItemType::Module;
513  isNet = mItem->itemType() == ItemType::Net;
514 
515  if (isGate)
516  {
517  if (!gSelectionRelay->containsGate(mItem->id()))
518  {
520  gSelectionRelay->addGate(mItem->id());
523  }
524  }
525  else if (isModule)
526  {
527  if (!gSelectionRelay->containsModule(mItem->id()))
528  {
530  gSelectionRelay->addModule(mItem->id());
533  }
534  }
535  else if (isNet)
536  {
537  if (!gSelectionRelay->containsNet((mItem->id())))
538  {
540  gSelectionRelay->addNet(mItem->id());
543  }
544  }
545 
546  if (isGate || isModule)
547  {
548  QMenu* preSucMenu = context_menu.addMenu("Successor/Predecessor …");
549  recursionLevelMenu(preSucMenu->addMenu("Add successors to view …"), true, &GraphGraphicsView::handleAddSuccessorToView);
550  if (isMultiGates)
551  recursionLevelMenu(preSucMenu->addMenu("Add common successors to view …"),true, &GraphGraphicsView::handleAddCommonSuccessorToView);
552  if (isGate)
553  {
554  action = preSucMenu->addAction("Add path to successor gate to view …");
555  action->setData(SuccessorGate);
556  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleShortestPathToView);
557  action = preSucMenu->addAction("Add path to successor module to view …");
558  action->setData(SuccessorModule);
559  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleShortestPathToView);
560  }
561  recursionLevelMenu(preSucMenu->addMenu("Highlight successors …"), true, &GraphGraphicsView::handleHighlightSuccessor, true);
562  recursionLevelMenu(preSucMenu->addMenu("Highlight successors by distance …"), true, &GraphGraphicsView::handleSuccessorDistance);
563  if (isGate)
564  {
565  action = preSucMenu->addAction("Highlight path to successor gate …");
566  action->setData(SuccessorGate);
567  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleQueryShortestPathGate);
568  action = preSucMenu->addAction("Highlight path to successor module …");
569  action->setData(SuccessorModule);
570  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleQueryShortestPathModule);
571  }
572 
573  preSucMenu->addSeparator();
574  recursionLevelMenu(preSucMenu->addMenu("Add predecessors to view …"), false, &GraphGraphicsView::handleAddPredecessorToView);
575  if (isMultiGates)
576  recursionLevelMenu(preSucMenu->addMenu("Add common predecessors to view …"),false, &GraphGraphicsView::handleAddCommonPredecessorToView);
577  if (isGate)
578  {
579  action = preSucMenu->addAction("Add path to predecessor gate to view …");
580  action->setData(PredecessorGate);
581  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleShortestPathToView);
582  action = preSucMenu->addAction("Add path to predecessor module to view …");
583  action->setData(PredecessorModule);
584  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleShortestPathToView);
585  }
586  recursionLevelMenu(preSucMenu->addMenu("Highlight predecessors …"), false, &GraphGraphicsView::handleHighlightPredecessor, true);
587  recursionLevelMenu(preSucMenu->addMenu("Highlight predecessors by distance …"), false, &GraphGraphicsView::handlePredecessorDistance);
588  if (isGate)
589  {
590  action = preSucMenu->addAction("Highlight path to predecessor gate …");
591  action->setData(PredecessorGate);
592  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleQueryShortestPathGate);
593  action = preSucMenu->addAction("Highlight path to predecessor module …");
594  action->setData(PredecessorModule);
595  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleQueryShortestPathModule);
596  }
597  }
598 
599  action = context_menu.addAction("Remove selected items from view");
601 
603  {
604  action = context_menu.addAction("Cancel pick-item mode");
605  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleCancelPickMode);
606  }
607 
608  if(isModule)
609  ModuleContextMenu::addModuleSubmenu(&context_menu, mItem->id());
610  else if(isGate)
611  ModuleContextMenu::addGateSubmenu(&context_menu, mItem->id());
612  else if(isNet)
613  ModuleContextMenu::addNetSubmenu(&context_menu, mItem->id());
614 
615  // Appended to single selected item menu
616  if(isGate || isModule)
617  {
618  action = context_menu.addAction(" Fold parent module");
619  QObject::connect(action, &QAction::triggered, this, &GraphGraphicsView::handleFoldParentSingle);
620  }
621  if(isModule)
622  {
623  action = context_menu.addAction(" Unfold module");
624  QObject::connect(action, &QAction::triggered, this, &GraphGraphicsView::handleUnfoldSingleAction);
625  }
626 
628  {
630 
631  // Appended to multiple selected items menu
632  action = context_menu.addAction(" Fold all parent modules");
633  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleFoldParentAll);
634  action = context_menu.addAction(" Isolate all in new view");
635  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleIsolationViewAction);
636  action = context_menu.addAction(" Unfold all selected modules");
637  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleUnfoldAllAction);
638 
639  action = context_menu.addAction(" Add comment");
640  QVariant data;
641  data.setValue(Node(mItem->id(), isGate ? Node::NodeType::Gate : Node::NodeType::Module));
642  action->setData(data);
643  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleAddCommentAction);
644  }
645  }
646  else
647  {
648  context_menu.addAction("This view:")->setEnabled(false);
649 
650  action = context_menu.addAction(" Add module to view");
651  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleAddModuleToView);
652 
653  int selectable_modules_count = 0;
654  QSet<u32> not_selectable_modules = getNotSelectableModules();
655 
656  for (Module* m : gNetlist->get_modules())
657  if (!not_selectable_modules.contains(m->get_id()))
658  selectable_modules_count++;
659 
660  if (selectable_modules_count == 0)
661  action->setDisabled(true);
662 
663  action = context_menu.addAction(" Add gate to view");
664  connect(action, &QAction::triggered, this, &GraphGraphicsView::handleAddGateToView);
665  if (getSelectableGates().empty())
666  action->setDisabled(true);
667  }
668 
669  // if (!item || isNet)
670  // {
671  // QAction* antialiasing_action = context_menu.addAction("Antialiasing");
672  // QAction* cosmetic_action = context_menu.addAction("Cosmetic Nets");
673  // QMenu* grid_menu = context_menu.addMenu("Grid");
674  // QMenu* type_menu = grid_menu->addMenu("Type");
675  // QMenu* cluster_menu = grid_menu->addMenu("Clustering");
676  // QAction* lines_action = type_menu->addAction("Lines");
677  // QAction* dots_action = type_menu->addAction("Dots");
678  // QAction* none_action = type_menu->addAction("None");
679  // connect(action, &QAction::triggered, this, SLOT);
680  // }
681 
686  context_menu.exec(mapToGlobal(pos));
687  update();
688  }
689 
690  void GraphGraphicsView::handlePluginContextContributionTriggered()
691  {
692  QAction* act = static_cast<QAction*>(sender());
693  Q_ASSERT(act);
694  ContextMenuContribution* cmc = static_cast<ContextMenuContribution*>(act->data().value<void*>());
695  Q_ASSERT(cmc);
696  Q_ASSERT(cmc->mContributer);
697  cmc->mContributer->execute_function(cmc->mTagname,gNetlist,
703  }
704 
705  void GraphGraphicsView::toggleAntialiasing()
706  {
708  }
709 
710  bool GraphGraphicsView::itemDraggable(GraphicsItem* item)
711  {
712  ItemType type = item->itemType();
713  return type == ItemType::Gate || type == ItemType::Module;
714  }
715 
716  void GraphGraphicsView::gentleZoom(const qreal factor)
717  {
718  scale(factor, factor);
719  centerOn(mTargetScenePos);
720  QPointF delta_viewport_pos = mTargetViewportPos - QPointF(viewport()->width() / 2.0, viewport()->height() / 2.0);
721  QPointF viewport_center = mapFromScene(mTargetScenePos) - delta_viewport_pos;
722  centerOn(mapToScene(viewport_center.toPoint()));
723  }
724 
725  void GraphGraphicsView::viewportCenterZoom(const qreal factor)
726  {
727  QPointF target_pos = mapToScene(viewport()->rect().center());
728  scale(factor, factor);
729  centerOn(target_pos.toPoint());
730  }
731 
732  void GraphGraphicsView::handleCancelPickMode()
733  {
735  }
736 
737  namespace ShortestPath
738  {
739  const Net* net(const Gate* g0, const Gate* g1)
740  {
741  for (const Net* n0 : g0->get_fan_out_nets())
742  for (const Net* n1 : g1->get_fan_in_nets())
743  if (n0 == n1)
744  return n0;
745  return nullptr;
746  }
747 
748  const Module* module(const Gate* g, const NodeBoxes& boxes)
749  {
750  const Module* parent = g->get_module();
751  while (parent)
752  {
753  Node nd(parent->get_id(),Node::Module);
754  if (boxes.boxForNode(nd)) return parent;
755  parent = parent->get_parent_module();
756  }
757  return nullptr;
758  }
759 
760  }
761 
762 
763  void GraphGraphicsView::handleAddModuleToView()
764  {
765  GraphContext* context = mGraphWidget->getContext();
766 
767 // QSet<u32> not_selectable_modules;
768 // QSet<u32> modules_in_context = context->modules();
769 // QSet<u32> gates_in_context = context->gates();
770 
771 // for (Module* module : gNetlist->get_modules())
772 // {
773 // bool module_in_context = false;
774 // for (Module* submodule: module->get_submodules(nullptr, true))
775 // {
776 // if (modules_in_context.contains(submodule->get_id()))
777 // {
778 // module_in_context = true;
779 // break;
780 // }
781 // }
782 // for (Gate* subgate : module->get_gates(nullptr, true))
783 // {
784 // if (gates_in_context.contains(subgate->get_id()))
785 // {
786 // module_in_context = true;
787 // break;
788 // }
789 // }
790 // if (module_in_context)
791 // {
792 // not_selectable_modules.insert(module->get_id());
793 // }
794 // }
795 
796 // not_selectable_modules += modules_in_context;
797 
798 // QSet<u32> direct_par_modules;
799 // for (u32 id : modules_in_context)
800 // {
801 // Module* cur_module = gNetlist->get_module_by_id(id);
802 // for (Module* module : cur_module->get_submodules(nullptr, true))
803 // {
804 // not_selectable_modules.insert(module->get_id());
805 // }
806 
807 // if (!cur_module->is_top_module())
808 // {
809 // direct_par_modules.insert(cur_module->get_parent_module()->get_id());
810 // }
811 // }
812 
813 // if (!gates_in_context.empty())
814 // {
815 // for (u32 id : gates_in_context)
816 // {
817 // direct_par_modules.insert(gNetlist->get_gate_by_id(id)->get_module()->get_id());
818 // }
819 // }
820 
821 // for (u32 id : direct_par_modules)
822 // {
823 // not_selectable_modules.insert(id);
824 
825 // Module* tmp_module = gNetlist->get_module_by_id(id);
826 // while (!tmp_module->is_top_module())
827 // {
828 // Module* par_module = tmp_module->get_parent_module();
829 // tmp_module = par_module;
830 // not_selectable_modules.insert(par_module->get_id());
831 // }
832 // }
833 
834  ModuleDialog module_dialog(getNotSelectableModules(),"Add module to view", true, nullptr, this);
835  if (module_dialog.exec() == QDialog::Accepted)
836  {
837  QSet<u32> module_to_add;
838  module_to_add.insert(module_dialog.selectedId());
839  ActionAddItemsToObject* act = new ActionAddItemsToObject(module_to_add, {});
840  act->setObject(UserActionObject(context->id(), UserActionObjectType::ContextView));
841  act->exec();
842  }
843  }
844 
845 
846  QSet<u32> GraphGraphicsView::getNotSelectableModules()
847  {
848  GraphContext* context = mGraphWidget->getContext();
849 
850  QSet<u32> not_selectable_modules;
851  QSet<u32> modules_in_context = context->modules();
852  QSet<u32> gates_in_context = context->gates();
853 
854  for (Module* module : gNetlist->get_modules())
855  {
856  bool module_in_context = false;
857  for (Module* submodule: module->get_submodules(nullptr, true))
858  {
859  if (modules_in_context.contains(submodule->get_id()))
860  {
861  module_in_context = true;
862  break;
863  }
864  }
865  for (Gate* subgate : module->get_gates(nullptr, true))
866  {
867  if (gates_in_context.contains(subgate->get_id()))
868  {
869  module_in_context = true;
870  break;
871  }
872  }
873  if (module_in_context)
874  {
875  not_selectable_modules.insert(module->get_id());
876  }
877  }
878 
879  not_selectable_modules += modules_in_context;
880 
881  QSet<u32> direct_par_modules;
882  for (u32 id : modules_in_context)
883  {
884  Module* cur_module = gNetlist->get_module_by_id(id);
885  for (Module* module : cur_module->get_submodules(nullptr, true))
886  {
887  not_selectable_modules.insert(module->get_id());
888  }
889 
890  if (!cur_module->is_top_module())
891  {
892  direct_par_modules.insert(cur_module->get_parent_module()->get_id());
893  }
894  }
895 
896  if (!gates_in_context.empty())
897  {
898  for (u32 id : gates_in_context)
899  {
900  direct_par_modules.insert(gNetlist->get_gate_by_id(id)->get_module()->get_id());
901  }
902  }
903 
904  for (u32 id : direct_par_modules)
905  {
906  not_selectable_modules.insert(id);
907 
908  Module* tmp_module = gNetlist->get_module_by_id(id);
909  while (!tmp_module->is_top_module())
910  {
911  Module* par_module = tmp_module->get_parent_module();
912  tmp_module = par_module;
913  not_selectable_modules.insert(par_module->get_id());
914  }
915  }
916 
917  return not_selectable_modules;
918  }
919 
920  QSet<u32> GraphGraphicsView::getSelectableGates()
921  {
922  GraphContext* context = mGraphWidget->getContext();
923 
924  QSet<u32> not_selectable_gates = context->gates();
925  QSet<u32> modules_in_context = context->modules();
926 
927  for (u32 module_id : modules_in_context)
928  {
929  for (Gate* gate : gNetlist->get_module_by_id(module_id)->get_gates(nullptr, true))
930  {
931  not_selectable_gates.insert(gate->get_id());
932  }
933  }
934 
935  QSet<u32> selectable_gates;
936  for (Gate* gate : gNetlist->get_gates())
937  {
938  if (!not_selectable_gates.contains(gate->get_id()))
939  {
940  selectable_gates.insert(gate->get_id());
941  }
942  }
943 
944  return selectable_gates;
945  }
946 
947  void GraphGraphicsView::handleAddGateToView()
948  {
949  QSet<u32> selectable_gates = getSelectableGates();
950 
951  GraphContext* context = mGraphWidget->getContext();
952 
953  GateDialog gate_dialog(selectable_gates, "Add gate to view", nullptr, this);
954  if (gate_dialog.exec() == QDialog::Accepted)
955  {
956  QSet<u32> gate_to_add;
957  gate_to_add.insert(gate_dialog.selectedId());
958  ActionAddItemsToObject* act = new ActionAddItemsToObject({}, gate_to_add);
959  act->setObject(UserActionObject(context->id(), UserActionObjectType::ContextView));
960  act->exec();
961  }
962  }
963 
964 
965  void GraphGraphicsView::handleAddSuccessorToView()
966  {
967  QAction* send = static_cast<QAction*>(sender());
968  Q_ASSERT(send);
969  int level = send->data().toInt();
970  addSuccessorToView(level, true);
971  }
972 
973  void GraphGraphicsView::handleAddPredecessorToView()
974  {
975  QAction* send = static_cast<QAction*>(sender());
976  Q_ASSERT(send);
977  int level = send->data().toInt();
978  addSuccessorToView(level, false);
979  }
980 
981  void GraphGraphicsView::handleAddCommonSuccessorToView()
982  {
983  QAction* send = static_cast<QAction*>(sender());
984  Q_ASSERT(send);
985  int level = send->data().toInt();
986  addCommonSuccessorToView(level, true);
987  }
988 
989  void GraphGraphicsView::handleAddCommonPredecessorToView()
990  {
991  QAction* send = static_cast<QAction*>(sender());
992  Q_ASSERT(send);
993  int level = send->data().toInt();
994  addCommonSuccessorToView(level, false);
995  }
996 
997  void GraphGraphicsView::addSuccessorToView(int maxLevel, bool succ)
998  {
999 
1000  QSet<u32> gatsNew;
1001  QSet<const Gate*> gatsHandled;
1002 
1003  QList<const Gate*> startList;
1004  Node startNode;
1005 
1006  switch (mItem->itemType())
1007  {
1008  case ItemType::Gate:
1009  startNode = Node(mItem->id(),Node::Gate);
1010  startList.append(gNetlist->get_gate_by_id(mItem->id()));
1011  break;
1012  case ItemType::Module:
1013  startNode = Node(mItem->id(),Node::Module);
1014  for (const Gate* g : gNetlist->get_module_by_id(mItem->id())->get_gates(nullptr,true))
1015  startList.append(g);
1016  break;
1017  default:
1018  return;
1019  }
1020 
1021  Q_ASSERT(startList.size());
1022 
1023  for (const Gate* g : startList)
1024  gatsHandled.insert(g);
1025 
1026  const NodeBoxes& boxes = mGraphWidget->getContext()->getLayouter()->boxes();
1027  const NodeBox* box = boxes.boxForNode(startNode);
1028  Q_ASSERT(box);
1029  int xOrigin = box->x();
1030  int yOrigin = box->y();
1031  int xDir = succ ? 1 : -1;
1032 
1033  PlacementHint plc(PlacementHint::Standard);
1034 
1035  for (int loop = 0; !maxLevel || loop<maxLevel; loop++)
1036  {
1037  int y = 0;
1038  QList<const Gate*> foundList;
1039 
1040  for (const Gate* gOrigin : startList)
1041  {
1042  for (const Gate* g : netlist_utils::get_next_gates(gOrigin, succ, 1))
1043  {
1044  if (gatsHandled.contains(g)) continue;
1045  gatsHandled.insert(g);
1046  if (boxes.boxForGate(g)) continue; // by in view
1047  foundList.append(g);
1048  }
1049  }
1050 
1051  if (foundList.isEmpty()) break;
1052  for (const Gate* g: foundList)
1053  {
1054  gatsNew.insert(g->get_id());
1055  QPoint point(xOrigin + (loop+1) * xDir, yOrigin + y);
1056  y = y > 0 ? -y : -y+1;
1057  Node nd(g->get_id(),Node::Gate);
1058  if (!boxes.boxForPoint(point)) // not occupied yet
1059  {
1060  if (plc.mode() == PlacementHint::Standard)
1061  plc = PlacementHint(PlacementHint::GridPosition);
1062  plc.addGridPosition(nd,point);
1063  }
1064  }
1065  startList = foundList;
1066  }
1067 
1068  ActionAddItemsToObject* act = new ActionAddItemsToObject({}, gatsNew);
1069  act->setObject(UserActionObject(mGraphWidget->getContext()->id(),UserActionObjectType::ContextView));
1070  act->setPlacementHint(plc);
1071  act->exec();
1072  }
1073 
1074 
1075  void GraphGraphicsView::addCommonSuccessorToView(int maxLevel, bool succ)
1076  {
1077  CommonSuccessorPredecessor csp(gSelectionRelay->selectedGatesList(),succ,maxLevel);
1078  const NodeBoxes& boxes = mGraphWidget->getContext()->getLayouter()->boxes();
1079 
1080  QSet<u32> gatsNew;
1081  for (const Gate* g : csp.result())
1082  {
1083  if (boxes.boxForGate(g)) continue;
1084  gatsNew.insert(g->get_id());
1085  }
1086  ActionAddItemsToObject* act = new ActionAddItemsToObject({}, gatsNew);
1087  act->setObject(UserActionObject(mGraphWidget->getContext()->id(),UserActionObjectType::ContextView));
1088  act->exec();
1089  }
1090 
1091  void GraphGraphicsView::handleHighlightSuccessor()
1092  {
1093  QAction* send = static_cast<QAction*>(sender());
1094  Q_ASSERT(send);
1095  int level = send->data().toInt();
1097  }
1098 
1099  void GraphGraphicsView::handleHighlightPredecessor()
1100  {
1101  QAction* send = static_cast<QAction*>(sender());
1102  Q_ASSERT(send);
1103  int level = send->data().toInt();
1105  }
1106 
1107  void GraphGraphicsView::handleSuccessorDistance()
1108  {
1109  QAction* send = static_cast<QAction*>(sender());
1110  Q_ASSERT(send);
1111  int level = send->data().toInt();
1113  }
1114 
1115  void GraphGraphicsView::handlePredecessorDistance()
1116  {
1117  QAction* send = static_cast<QAction*>(sender());
1118  Q_ASSERT(send);
1119  int level = send->data().toInt();
1121  }
1122 
1123  void GraphGraphicsView::handleShortestPathToView()
1124  {
1125  QAction* send = static_cast<QAction*>(sender());
1126  Q_ASSERT(send);
1127 
1128  int param = send->data().toInt();
1129  bool succ = (param == SuccessorModule || param == SuccessorGate);
1130  bool isMod = (param == SuccessorModule || param == PredecessorModule);
1131  std::vector<Gate*> spath;
1132  Gate* startGate = gNetlist->get_gate_by_id(mItem->id());
1133  Q_ASSERT(startGate);
1134 
1135  if (isMod)
1136  {
1137  QSet<u32> excludeModules;
1138  const Module* parMod = startGate->get_module();
1139  while (parMod)
1140  {
1141  excludeModules.insert(parMod->get_id());
1142  parMod = parMod->get_parent_module();
1143  }
1144 
1145  ModuleDialog md(excludeModules, "Shortest path to module", true, nullptr, this);
1146 
1147  if (md.exec() != QDialog::Accepted) return;
1148 
1149  Module* endModule = gNetlist->get_module_by_id(md.selectedId());
1150  Q_ASSERT(endModule);
1151 
1152  spath = netlist_utils::get_shortest_path(startGate,endModule,succ);
1153  }
1154  else
1155  {
1156  QSet<u32> selectableGates;
1157 
1158  for (Gate* g : netlist_utils::get_next_gates(startGate,succ))
1159  {
1160  selectableGates.insert(g->get_id());
1161  }
1162 
1163 // GraphGraphicsViewNeighborSelector* ggvns = new GraphGraphicsViewNeighborSelector(mItem->id(), succ, this);
1164  GateDialog gd(selectableGates, QString("Shortest path %1 gate").arg(succ?"to":"from"), nullptr, this);
1165 
1166  if (gd.exec() != QDialog::Accepted) return;
1167 
1168  Gate* endGate = gNetlist->get_gate_by_id(gd.selectedId());
1169  Q_ASSERT(endGate);
1170 
1171  if (succ)
1172  spath = netlist_utils::get_shortest_path(startGate,endGate);
1173  else
1174  {
1175  spath = netlist_utils::get_shortest_path(endGate,startGate);
1176  }
1177  }
1178  if (spath.empty()) return;
1179  if (!succ) std::reverse(spath.begin(), spath.end());
1180 
1181  const NodeBoxes& boxes = mGraphWidget->getContext()->getLayouter()->boxes();
1182  const NodeBox* originBox = boxes.boxForGate(startGate);
1183  Q_ASSERT(originBox);
1184  QPoint point(originBox->x(),originBox->y());
1185  QPoint deltaX(succ ? 1 : -1, 0);
1186  PlacementHint plc(PlacementHint::Standard);
1187 
1188  QSet<u32> gats;
1189 
1190  for (Gate* g : spath)
1191  {
1192  if (boxes.boxForGate(g)) continue; // already in view
1193  do {
1194  point += deltaX;
1195  } while (boxes.boxForPoint(point)); // find grid point not occupied
1196  gats.insert(g->get_id());
1197  Node nd(g->get_id(),Node::Gate);
1198  if (plc.mode() == PlacementHint::Standard)
1199  plc = PlacementHint(PlacementHint::GridPosition);
1200  plc.addGridPosition(nd,point);
1201  }
1202 
1203  ActionAddItemsToObject* act = new ActionAddItemsToObject({},gats);
1204  act->setObject(UserActionObject(mGraphWidget->getContext()->id(),UserActionObjectType::ContextView));
1205  act->setPlacementHint(plc);
1206  act->exec();
1207  }
1208 
1209  void GraphGraphicsView::handleQueryShortestPathModule()
1210  {
1211  QAction* send = static_cast<QAction*>(sender());
1212  Q_ASSERT(send);
1213  bool succ = send->data().toInt() == SuccessorGate;
1214 
1215  QSet<u32> excludeModules;
1216  Gate* g = gNetlist->get_gate_by_id(mItem->id());
1217  Q_ASSERT(g);
1218  Module* par = g->get_module();
1219  while(par)
1220  {
1221  excludeModules.insert(par->get_id());
1222  par = par->get_parent_module();
1223  }
1224  ModuleDialog md(excludeModules, "Shortest path to module", true, nullptr, this);
1225 
1226  if (md.exec() != QDialog::Accepted) return;
1227 
1228  handleShortestPathToGrouping(mItem->id(),Node(md.selectedId(),Node::Module),succ);
1229  }
1230 
1231  void GraphGraphicsView::handleQueryShortestPathGate()
1232  {
1233  QAction* send = static_cast<QAction*>(sender());
1234  Q_ASSERT(send);
1235  bool succ = send->data().toInt() == SuccessorGate;
1236 
1237  QSet<u32> selectableGates;
1238  for (Gate* g : netlist_utils::get_next_gates(gNetlist->get_gate_by_id(mItem->id()),succ))
1239  selectableGates.insert(g->get_id());
1240 
1241  GraphGraphicsViewNeighborSelector* ggvns = new GraphGraphicsViewNeighborSelector(mItem->id(), succ, this);
1242  GateDialog gd(selectableGates,QString("Shortest path %1 gate").arg(succ?"to":"from"),ggvns,this);
1243 
1244  if (gd.exec() != QDialog::Accepted) return;
1245 
1246  u32 targetId = gd.selectedId();
1247  if (!targetId) return;
1248 
1249  if (succ)
1250  handleShortestPathToGrouping(mItem->id(),Node(targetId,Node::Gate),true);
1251  else
1252  handleShortestPathToGrouping(targetId,Node(mItem->id(),Node::Gate),true);
1253  }
1254 
1255  void GraphGraphicsView::handleShortestPathToGrouping(u32 idFrom, Node nodeTo, bool forwardDirection)
1256  {
1257  std::vector<Gate*> spath;
1258 
1259  Gate* g0 = gNetlist->get_gate_by_id(idFrom);
1260  Q_ASSERT(g0);
1261  Q_ASSERT(!nodeTo.isNull());
1262 
1263  QString target;
1264 
1265  if(nodeTo.isGate())
1266  {
1267  Gate* g1 = gNetlist->get_gate_by_id(nodeTo.id());
1268  Q_ASSERT(g1);
1269  spath = forwardDirection ?
1272  target = QString("gate '%1'[%2]").arg(QString::fromStdString(g1->get_name()), g1->get_id());
1273  }
1274  else
1275  {
1276  Module* m = gNetlist->get_module_by_id(nodeTo.id());
1277  Q_ASSERT(m);
1278  spath = netlist_utils::get_shortest_path(g0,m,forwardDirection);
1279  target = QString("module '%1'[%2]").arg(QString::fromStdString(m->get_name()), m->get_id());
1280  }
1281 
1282 
1283  QSet<u32> mods;
1284  QSet<u32> gats;
1285  QSet<u32> nets;
1286 
1287  Gate* previousGate = nullptr;
1288  for (Gate* g : spath)
1289  {
1290  const Module* pm = ShortestPath::module(g,mGraphWidget->getContext()->getLayouter()->boxes());
1291  if (pm) mods.insert(pm->get_id());
1292  gats.insert(g->get_id());
1293  if (previousGate)
1294  {
1295  const Net* n = ShortestPath::net(previousGate,g);
1296  if (n) nets.insert(n->get_id());
1297  }
1298  previousGate = g;
1299  }
1300 
1302  act->setUseCreatedObject();
1304  QString("Path from '%1'[%2] to %3")
1305  .arg(QString::fromStdString(g0->get_name())).arg(g0->get_id())
1306  .arg(target)));
1307  act->addAction(new ActionAddItemsToObject(mods,gats,nets));
1308  act->addAction(new ActionSetSelectionFocus());
1309  act->exec();
1310  }
1311 
1312  void GraphGraphicsView::handleSelectOutputs()
1313  {
1314  auto context = mGraphWidget->getContext();
1315  QAction* sender_action = dynamic_cast<QAction*>(sender());
1316  if (sender_action)
1317  {
1318  QSet<u32> gates;
1319  for (auto sel_id : gSelectionRelay->selectedGatesList())
1320  {
1321  auto gate = gNetlist->get_gate_by_id(sel_id);
1322  for (auto net : gate->get_fan_out_nets())
1323  {
1324  for (const auto& suc : net->get_destinations())
1325  {
1326  bool found = false;
1327  for (const auto& id : context->modules())
1328  {
1329  auto m = gNetlist->get_module_by_id(id);
1330  if (m->contains_gate(suc->get_gate(), true))
1331  {
1332  found = true;
1333  break;
1334  }
1335  }
1336  if (!found)
1337  {
1338  gates.insert(suc->get_gate()->get_id());
1339  }
1340  }
1341  }
1342  }
1343  for (auto sel_id : gSelectionRelay->selectedModulesList())
1344  {
1345  auto module = gNetlist->get_module_by_id(sel_id);
1346  for (auto net : module->get_output_nets())
1347  {
1348  for (const auto& suc : net->get_destinations())
1349  {
1350  bool found = false;
1351  for (const auto& id : context->modules())
1352  {
1353  auto m = gNetlist->get_module_by_id(id);
1354  if (m->contains_gate(suc->get_gate(), true))
1355  {
1356  found = true;
1357  break;
1358  }
1359  }
1360  if (!found)
1361  {
1362  gates.insert(suc->get_gate()->get_id());
1363  }
1364  }
1365  }
1366  }
1367 
1368  gates = context->getLayouter()->boxes().filterNotInView(gates);
1369  ActionAddItemsToObject* act = new ActionAddItemsToObject({},gates);
1370  act->setObject(UserActionObject(context->id(),UserActionObjectType::ContextView));
1372  {
1376  act->setPlacementHint(PlacementHint(PlacementHint::PreferRight,origin));
1377  }
1378  act->exec();
1379  }
1380  }
1381  void GraphGraphicsView::handleSelectInputs()
1382  {
1383  auto context = mGraphWidget->getContext();
1384  QAction* sender_action = dynamic_cast<QAction*>(sender());
1385  if (sender_action)
1386  {
1387  QSet<u32> gates;
1388  for (auto sel_id : gSelectionRelay->selectedGatesList())
1389  {
1390  auto gate = gNetlist->get_gate_by_id(sel_id);
1391  for (auto net : gate->get_fan_in_nets())
1392  {
1393  if (!net->get_sources().empty() && net->get_sources().at(0)->get_gate() != nullptr)
1394  {
1395  bool found = false;
1396  for (const auto& id : context->modules())
1397  {
1398  auto m = gNetlist->get_module_by_id(id);
1399  if (m->contains_gate(net->get_sources().at(0)->get_gate(), true))
1400  {
1401  found = true;
1402  break;
1403  }
1404  }
1405  if (!found)
1406  {
1407  gates.insert(net->get_sources().at(0)->get_gate()->get_id());
1408  }
1409  }
1410  }
1411  }
1412  for (auto sel_id : gSelectionRelay->selectedModulesList())
1413  {
1414  auto module = gNetlist->get_module_by_id(sel_id);
1415  for (auto net : module->get_input_nets())
1416  {
1417  if (!net->get_sources().empty() && net->get_sources().at(0)->get_gate() != nullptr)
1418  {
1419  bool found = false;
1420  for (const auto& id : context->modules())
1421  {
1422  auto m = gNetlist->get_module_by_id(id);
1423  if (m->contains_gate(net->get_sources().at(0)->get_gate(), true))
1424  {
1425  found = true;
1426  break;
1427  }
1428  }
1429  if (!found)
1430  {
1431  gates.insert(net->get_sources().at(0)->get_gate()->get_id());
1432  }
1433  }
1434  }
1435  }
1436 
1437  gates = context->getLayouter()->boxes().filterNotInView(gates);
1438  ActionAddItemsToObject* act = new ActionAddItemsToObject({},gates);
1439  act->setObject(UserActionObject(context->id(),UserActionObjectType::ContextView));
1441  {
1445  act->setPlacementHint(PlacementHint(PlacementHint::PreferLeft,origin));
1446  }
1447  act->exec();
1448  }
1449  }
1450 
1451  void GraphGraphicsView::selectedNodeToItem()
1452  {
1453  if (gSelectionRelay->numberSelectedItems() != 1) return;
1454  NodeBox* box = nullptr;
1456  {
1457  u32 gatId = gSelectionRelay->selectedGatesList().at(0);
1458  box = mGraphWidget->getContext()->getLayouter()->boxes().boxForNode(Node(gatId,Node::Gate));
1459  }
1460  else if (gSelectionRelay->numberSelectedModules() == 1)
1461  {
1462  u32 modId = gSelectionRelay->selectedModulesList().at(0);
1463  box = mGraphWidget->getContext()->getLayouter()->boxes().boxForNode(Node(modId,Node::Module));
1464  }
1465  if (!box) return;
1466  mItem = box->item();
1467  }
1468 
1470  {
1471  selectedNodeToItem();
1472  if (!mItem) return;
1473  handleFoldParentSingle();
1474  }
1475 
1477  {
1478  selectedNodeToItem();
1479  if (!mItem || mItem->itemType() != ItemType::Module) return;
1480  handleUnfoldSingleAction();
1481  }
1482 
1483  void GraphGraphicsView::handleFoldParentSingle()
1484  {
1485  const Module* parentModule = nullptr;
1486  Node childNode;
1487  u32 id = mItem->id();
1488  GraphContext* context = mGraphWidget->getContext();
1489  switch (mItem->itemType())
1490  {
1491  case ItemType::Module:
1492  parentModule = gNetlist->get_module_by_id(id)->get_parent_module();
1493  childNode = Node(id, Node::Module);
1494  break;
1495  case ItemType::Gate:
1496  parentModule = gNetlist->get_gate_by_id(id)->get_module();
1497  childNode = Node(id, Node::Gate);
1498  break;
1499  default:
1500  return;
1501  }
1502 
1503  if (!parentModule || childNode.type()==Node::None) return;
1504  NodeBox* box = context->getLayouter()->boxes().boxForNode(childNode);
1505  if (!box) return;
1506 
1507  PlacementHint plc(PlacementHint::GridPosition);
1508  plc.addGridPosition(Node(parentModule->get_id(),Node::Module),box->gridPosition());
1509  ActionFoldModule* act = new ActionFoldModule(parentModule->get_id());
1510  act->setContextId(context->id());
1511  act->setPlacementHint(plc);
1512  act->exec();
1513  }
1514 
1515  void GraphGraphicsView::handleUnfoldSingleAction()
1516  {
1517  GraphContext* context = mGraphWidget->getContext();
1518  Module* m = gNetlist->get_module_by_id(mItem->id());
1519  if (m->get_gates().empty() && m->get_submodules().empty())
1520  {
1521  QMessageBox msg;
1522  msg.setText("This module is empty.\nYou can't unfold it.");
1523  msg.setWindowTitle("Error");
1524  msg.exec();
1525  return;
1526  // We would otherwise unfold the empty module into nothing, so the user
1527  // would have nowhere to click to get their module back
1528  }
1529  ActionUnfoldModule* act = new ActionUnfoldModule(mItem->id());
1530  act->setContextId(context->id());
1531  act->exec();
1532  }
1533 
1534  void GraphGraphicsView::handleFoldParentAll()
1535  {
1536  GraphContext* context = mGraphWidget->getContext();
1537 
1538  QSet<const Module*> modSet;
1539 
1540  for (Node& nd : gSelectionRelay->selectedNodesList())
1541  {
1542  if (!context->getLayouter()->boxes().boxForNode(nd)) continue; // not in view
1543 
1544  switch (nd.type())
1545  {
1546  case Node::Gate:
1547  modSet.insert(gNetlist->get_gate_by_id(nd.id())->get_module());
1548  break;
1549  case Node::Module:
1550  modSet.insert(gNetlist->get_module_by_id(nd.id())->get_parent_module());
1551  default:
1552  continue;
1553  }
1554  }
1555 
1557  for (const Module* m : modSet)
1558  if (m) modDepth.insertMulti(m->get_submodule_depth(),m);
1559 
1560  if (!modDepth.empty())
1561  {
1562  auto it = modDepth.constEnd();
1563  for (;;)
1564  {
1565  --it;
1566  const Module* m = it.value();
1567  ActionFoldModule* act = new ActionFoldModule(m->get_id());
1568  act->setContextId(context->id());
1569  act->exec();
1570  if (it == modDepth.constBegin())
1571  break;
1572  }
1573  }
1574  }
1575 
1576  void GraphGraphicsView::handleUnfoldAllAction()
1577  {
1578  auto context = mGraphWidget->getContext();
1579 
1580  context->beginChange();
1581  UserActionCompound* act = new UserActionCompound;
1583  {
1584  ActionUnfoldModule* ufo = new ActionUnfoldModule(id);
1585  ufo->setContextId(context->id());
1586  act->addAction(ufo);
1587  }
1588  act->exec();
1589  context->endChange();
1590  }
1591 
1592 #ifdef GUI_DEBUG_GRID
1593  void GraphGraphicsView::debugShowLayouterGridpos(const QPoint& mouse_pos)
1594  {
1595  auto context = mGraphWidget->getContext();
1596  if (!context)
1597  return;
1598 
1599  const GraphLayouter* layouter = context->getLayouter();
1600  if (!(layouter->done()))
1601  return;
1602 
1603  QPointF scene_mouse_pos = mapToScene(mouse_pos);
1604  QPoint layouter_pos = closestLayouterPos(scene_mouse_pos).first;
1605  m_debug_gridpos = layouter_pos;
1606  }
1607 #endif
1608 
1609  QPair<QPoint,QPointF> GraphGraphicsView::closestLayouterPos(const QPointF& scene_pos) const
1610  {
1611  auto context = mGraphWidget->getContext();
1612  assert(context);
1613 
1614  const GraphLayouter* layouter = context->getLayouter();
1615  assert(layouter->done()); // ensure grid stable
1616 
1617  int default_width = layouter->defaultGridWidth();
1618  int default_height = layouter->defaultGridHeight();
1619  int min_x = layouter->minXIndex();
1620  int min_y = layouter->minYIndex();
1621  QVector<qreal> x_vals = layouter->xValues();
1622  QVector<qreal> y_vals = layouter->yValues();
1623  LayouterPoint x_point = closestLayouterPoint(scene_pos.x(), default_width, min_x, x_vals);
1624  LayouterPoint y_point = closestLayouterPoint(scene_pos.y(), default_height, min_y, y_vals);
1625  return qMakePair(QPoint(x_point.mIndex, y_point.mIndex), QPointF(x_point.mPos, y_point.mPos));
1626  }
1627 
1628  GraphGraphicsView::LayouterPoint GraphGraphicsView::closestLayouterPoint(qreal scene_pos, int default_spacing, int min_index, QVector<qreal> sections) const
1629  {
1630  int index = min_index;
1631  qreal pos = 0;
1632  if (sections.first() > scene_pos)
1633  {
1634  int distance = sections.first() - scene_pos;
1635  int nSections = distance / default_spacing; // this rounds down
1636  index -= (nSections + 1);
1637  pos = sections.first() + index * default_spacing;
1638  }
1639  else if (sections.last() <= scene_pos)
1640  {
1641  int distance = scene_pos - sections.last();
1642  int nSections = distance / default_spacing; // this rounds down
1643  index += (sections.size() + nSections -1);
1644  pos = sections.last() + nSections * default_spacing;
1645  }
1646  else
1647  {
1648  // search for first value in sections larger than or equal to scene_pos
1649  auto it = sections.constBegin();
1650  auto jt = it+1;
1651  while (jt != sections.constEnd())
1652  {
1653  if (scene_pos < *jt) break; // found value in inteval [it..[jt
1654  ++it;
1655  ++jt;
1656  ++index;
1657  }
1658  pos = *it;
1659  }
1660  return LayouterPoint{index, pos};
1661  }
1662 
1663 
1665  {
1666  return mGridType;
1667  }
1668 
1670  {
1671  mGridType = gridType;
1672  }
1673 
1675  {
1676  return mDragModifier;
1677  }
1678 
1680  {
1681  mDragModifier = dragModifier;
1682  }
1683 
1685  {
1686  return mPanModifier;
1687  }
1688 
1690  {
1691  mPanModifier = panModifier;
1692  }
1693 
1695  {
1696  if (!gats.empty())
1697  {
1698  u32 pickedGate = *gats.constBegin();
1699  GraphGraphicsView* ggv = dynamic_cast<GraphGraphicsView*>(parent());
1700  if (ggv)
1701  {
1702  if (mPickSuccessor)
1703  ggv->handleShortestPathToGrouping(mOrigin,Node(pickedGate,Node::Gate),true);
1704  else
1705  ggv->handleShortestPathToGrouping(pickedGate,Node(mOrigin,Node::Gate),true);
1706  }
1707  }
1708  this->deleteLater();
1709  }
1710 } // namespace hal
Adds an item to a module or grouping.
Removes an item from a Module or Grouping.
Set the selection and focus.
void addComment(CommentEntry *entry)
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
GraphTabWidget * getGraphTabWidget()
Get hal's graph tab widget.
GroupingManagerWidget * getGroupingManagerWidget()
ContextManagerWidget * getContextManagerWidget()
void selectViewContext(GraphContext *context)
GridPlacement * finalGridPlacement() const
void move(const QPoint &eventPos, bool wantSwap, const QPoint &gridPos)
bool hasDragged(const QPoint &eventPos)
void set(GraphicsNode *drgItem, const QPoint &eventPos)
bool isDropAllowed() const
void enterDrag(bool wantSwap)
Definition: gate.h:58
const std::vector< Net * > & get_fan_in_nets() const
Definition: gate.cpp:591
const std::string & get_name() const
Definition: gate.cpp:105
Module * get_module() const
Definition: gate.cpp:174
const std::vector< Net * > & get_fan_out_nets() const
Definition: gate.cpp:735
u32 get_id() const
Definition: gate.cpp:95
Logical container for modules, gates, and nets.
Definition: graph_context.h:55
GraphicsScene * scene()
void setDirty(bool dty)
const QSet< u32 > & gates() const
const QSet< u32 > & modules() const
GraphLayouter * getLayouter() const
QString nextViewName(const QString &prefix) const
GraphContext * getContextById(u32 id) const
GraphContext * getContextByExclusiveModuleId(u32 module_id) const
static SettingsItemCheckbox * sSettingPanOnMiddleButton
A view to display the rendered graph (needs a GraphicsScene).
void keyPressEvent(QKeyEvent *event) override
void dragLeaveEvent(QDragLeaveEvent *event) override
void setGridType(GraphicsScene::GridType gridType)
void dropEvent(QDropEvent *event) override
void wheelEvent(QWheelEvent *event) override
void setPanModifier(Qt::KeyboardModifier panModifier)
Qt::KeyboardModifier panModifier()
void keyReleaseEvent(QKeyEvent *event) override
void viewportCenterZoom(const qreal factor)
void handleShortestPathToGrouping(u32 idFrom, Node nodeTo, bool forwardDirection)
void setDragModifier(Qt::KeyboardModifier dragModifier)
GraphGraphicsView(GraphWidget *parent)
void paintEvent(QPaintEvent *event) override
void moduleDoubleClicked(u32 id)
void mouseMoveEvent(QMouseEvent *event) override
Qt::KeyboardModifier dragModifier()
void resizeEvent(QResizeEvent *event) override
GraphicsScene::GridType gridType()
void dragEnterEvent(QDragEnterEvent *event) override
void mouseDoubleClickEvent(QMouseEvent *event) override
void mouseReleaseEvent(QMouseEvent *event) override
void mousePressEvent(QMouseEvent *event) override
void drawForeground(QPainter *painter, const QRectF &rect) override
void gentleZoom(const qreal factor)
void dragMoveEvent(QDragMoveEvent *event) override
void handleGatesPicked(const QSet< u32 > &gats) override
Base class for all specific layouters.
const NodeBoxes & boxes() const
void emitTerminatePicker() const
bool isSelectMode() const
Wraps a GraphContext and a GraphGraphicsView.
Definition: graph_widget.h:55
GraphContext * getContext() const
Superclass for all graphic items used ins the GraphicsScene. It contains information about the underl...
Definition: graphics_item.h:44
static void setLod(const qreal lod)
ItemType itemType() const
Abstract base class for nodes (e.g. gates, modules)
Definition: graphics_node.h:42
Container for a GraphGraphicsView containing gates, nets, and modules.
static void setLod(const qreal &lod)
void setMousePressed(bool isPressed)
static void setGridEnabled(const bool &value)
static void setGridType(const GridType &gridType)
static void setGridClustersEnabled(const bool &value)
void newGroupingSuccOrPred(int maxDepth, bool succ, const GraphicsItem *item)
void newGroupingByDistance(int maxDepth, bool succ, const GraphicsItem *item)
static void addPluginSubmenus(QMenu *contextMenu, Netlist *netlist, const std::vector< u32 > &modules, const std::vector< u32 > &gates, const std::vector< u32 > &nets)
static void addModuleSubmenu(QMenu *contextMenu, u32 id)
static void addMultipleElementsSubmenu(QMenu *contextMenu, const QSet< u32 > &modules=QSet< u32 >(), const QSet< u32 > &gates=QSet< u32 >(), const QSet< u32 > &nets=QSet< u32 >())
static void addGateSubmenu(QMenu *contextMenu, u32 id)
static void addNetSubmenu(QMenu *contextMenu, u32 id)
Module * get_parent_module() const
Definition: module.cpp:126
const std::vector< Gate * > & get_gates() const
Definition: module.cpp:400
std::string get_name() const
Definition: module.cpp:88
const std::unordered_set< Net * > & get_input_nets() const
Definition: module.cpp:549
std::vector< Module * > get_submodules(const std::function< bool(Module *)> &filter=nullptr, bool recursive=false) const
Definition: module.cpp:268
const std::unordered_set< Net * > & get_output_nets() const
Definition: module.cpp:554
u32 get_id() const
Definition: module.cpp:83
Definition: net.h:58
u32 get_id() const
Definition: net.cpp:88
const std::vector< Gate * > & get_gates() const
Definition: netlist.cpp:206
Gate * get_gate_by_id(const u32 gate_id) const
Definition: netlist.cpp:195
const std::vector< Module * > & get_modules() const
Definition: netlist.cpp:626
Module * get_module_by_id(u32 module_id) const
Definition: netlist.cpp:615
The NodeBoxes class owns all NodeBox'es from hal view.
Definition: node_box.h:150
NodeBox * boxForNode(const Node &n) const
boxForNode find NodeBox by node
Definition: node_box.h:200
NodeBox * boxForGate(const Gate *g) const
boxForGate find NodeBox by Gate pointer.
Definition: node_box.h:184
static void setLod(const qreal lod)
The Node class object represents a module or a gate.
Definition: gui_def.h:61
NodeType type() const
type getter for type information
Definition: gui_def.h:71
bool isGate() const
isGate test whether node is a gate
Definition: gui_def.h:89
@ Module
Definition: gui_def.h:63
@ Gate
Definition: gui_def.h:63
@ None
Definition: gui_def.h:63
bool isNull() const
isNull test for null-Node object typically returned from functions
Definition: gui_def.h:83
u32 id() const
id getter for ID information
Definition: gui_def.h:77
PythonThread * pythonThread() const
int numberSelectedGates() const
int numberSelectedItems() const
bool containsGate(u32 id) const
void setFocus(ItemType ftype, u32 fid, Subfocus sfoc=Subfocus::None, u32 sfinx=0)
const QSet< u32 > & selectedNets() const
void relaySelectionChanged(void *sender)
QList< Node > selectedNodesList() const
QList< u32 > selectedModulesList() const
const QSet< u32 > & selectedGates() const
const QSet< u32 > & selectedModules() const
std::vector< u32 > selectedGatesVector() const
int numberSelectedModules() const
bool containsModule(u32 id) const
std::vector< u32 > selectedNetsVector() const
bool containsNet(u32 id) const
QList< u32 > selectedGatesList() const
std::vector< u32 > selectedModulesVector() const
void subfocusChanged(void *sender)
int numberSelectedNodes() const
virtual QVariant value() const override
void addAction(UserAction *act)
virtual void setObject(const UserActionObject &obj)
Definition: user_action.cpp:32
void setObjectLock(bool lock)
Definition: user_action.h:166
The UserActionObject class represents a single object used in UserAction.
uint32_t u32
Definition: defines.h:41
ItemType
The ItemType enum provides the enum type to classify graphic items into Modules, Gates or Nets....
Definition: gui_def.h:45
const Module * module(const Gate *g, const NodeBoxes &boxes)
const Net * net(const Gate *g0, const Gate *g1)
std::vector< Gate * > get_next_gates(const Gate *gate, bool get_successors, int depth=0, const std::function< bool(const Gate *)> &filter=nullptr)
std::vector< Gate * > get_shortest_path(Gate *start_gate, Gate *end_gate, bool search_both_directions=false)
Definition: defines.h:45
PythonContext * gPythonContext
Definition: plugin_gui.cpp:89
ContentManager * gContentManager
Definition: plugin_gui.cpp:79
GraphContextManager * gGraphContextManager
Definition: plugin_gui.cpp:86
SelectionRelay * gSelectionRelay
Definition: plugin_gui.cpp:84
CommentManager * gCommentManager
Definition: plugin_gui.cpp:88
Netlist * gNetlist
Definition: gui_globals.h:69
PinType type
Net * net
std::string name
std::string origin
QScrollBar * horizontalScrollBar() const const
QScrollBar * verticalScrollBar() const const
QWidget * viewport() const const
void setMinimum(int)
void setValue(int)
QVariant data() const const
void setData(const QVariant &userData)
void setDisabled(bool b)
void triggered(bool checked)
Qt::DropAction exec(Qt::DropActions supportedActions)
void setMimeData(QMimeData *data)
void setPixmap(const QPixmap &pixmap)
void centerOn(const QPointF &pos)
virtual void dropEvent(QDropEvent *event) override
virtual bool event(QEvent *event) override
QGraphicsItem * itemAt(const QPoint &pos) const const
QPoint mapFromScene(const QPointF &point) const const
QPointF mapToScene(const QPoint &point) const const
virtual void mouseMoveEvent(QMouseEvent *event) override
virtual void mousePressEvent(QMouseEvent *event) override
virtual void mouseReleaseEvent(QMouseEvent *event) override
void setOptimizationFlags(QGraphicsView::OptimizationFlags flags)
virtual void paintEvent(QPaintEvent *event) override
virtual void resizeEvent(QResizeEvent *event) override
void scale(qreal sx, qreal sy)
QGraphicsScene * scene() const const
void setRenderHint(QPainter::RenderHint hint, bool enabled)
QTransform transform() const const
void setViewportUpdateMode(QGraphicsView::ViewportUpdateMode mode)
Qt::KeyboardModifiers keyboardModifiers()
void append(const T &value)
const T & at(int i) const const
bool isEmpty() const const
int size() const const
QMap::iterator insertMulti(const Key &key, const T &value)
QMap::const_iterator constBegin() const const
QMap::const_iterator constEnd() const const
bool empty() const const
QAction * addAction(const QString &text)
QAction * addMenu(QMenu *menu)
QAction * addSeparator()
virtual int exec() override
void setWindowTitle(const QString &title)
void setText(const QString &text)
void setData(const QString &mimeType, const QByteArray &data)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
QObject * parent() const const
QObject * sender() const const
void drawText(const QPointF &position, const QString &text)
void fillRect(const QRectF &rectangle, const QBrush &brush)
void resetTransform()
void setPen(const QColor &color)
void fill(const QColor &color)
int x() const const
int y() const const
QPoint toPoint() const const
qreal x() const const
qreal y() const const
QSet::iterator begin()
QSet::const_iterator constBegin() const const
bool contains(const T &value) const const
bool empty() const const
QSet::iterator insert(const T &value)
bool isEmpty() const const
int size() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromStdString(const std::string &str)
QString number(int n, int base)
qreal levelOfDetailFromTransform(const QTransform &worldTransform)
CustomContextMenu
MoveAction
Key_Space
KeyboardModifier
LeftButton
bool toBool() const const
int toInt(bool *ok) const const
T value() const const
QVector::const_iterator constBegin() const const
QVector::const_iterator constEnd() const const
T & first()
T & last()
int size() const const
void setAcceptDrops(bool on)
void setContextMenuPolicy(Qt::ContextMenuPolicy policy)
void customContextMenuRequested(const QPoint &pos)
QPoint mapToGlobal(const QPoint &pos) const const
void setMouseTracking(bool enable)
void update()