HAL
graph_widget.cpp
Go to the documentation of this file.
2 
10 //#include "gui/graph_widget/graph_layout_spinner_widget.h"
17 #include "gui/gui_def.h"
18 #include "gui/gui_globals.h"
19 #include "gui/gui_utils/netlist.h"
25 #include "gui/toolbar/toolbar.h"
29 #include "hal_core/netlist/gate.h"
31 #include "hal_core/netlist/net.h"
35 
36 #include <QApplication>
37 #include <QDebug>
38 #include <QGraphicsRectItem>
39 #include <QInputDialog>
40 #include <QKeyEvent>
41 #include <QMessageBox>
42 #include <QThread>
43 #include <QTimer>
44 #include <QToolButton>
45 #include <QVBoxLayout>
46 #include <QVariantAnimation>
47 
48 namespace hal
49 {
50  SettingsItemSpinbox* GraphWidget::sSettingAnimationDuration =
51  new SettingsItemSpinbox("Animation Duration [1/10s]",
52  "graph_view/animation_duration",
53  10,
54  "Appearance:Graph View",
55  "Duration of 'fly to gate' animation when viewport is zooming in on new target. Unit is 1/10 second, thus 20=2sec, 10=1sec. Value of zero turns animation off.");
56 
57  GraphWidget* GraphWidget::sInstance = nullptr;
58 
60  : ContentWidget("Graph", parent), mView(new GraphGraphicsView(this)), mContext(context), mOverlay(new WidgetOverlay(this)),
61  mNavigationWidgetV3(new GraphNavigationWidget(false)), mProgressBar(nullptr),
62  mSpinnerWidget(new SpinnerWidget(this)), mCommentWidget(nullptr), mCurrentExpansion(0)
63  {
64  connect(mNavigationWidgetV3, &GraphNavigationWidget::navigationRequested, this, &GraphWidget::handleNavigationJumpRequested);
65  connect(mNavigationWidgetV3, &GraphNavigationWidget::closeRequested, mOverlay, &WidgetOverlay::hide);
66  connect(mNavigationWidgetV3, &GraphNavigationWidget::closeRequested, this, &GraphWidget::resetFocus);
67 
68  connect(mOverlay, &WidgetOverlay::clicked, this, &GraphWidget::hideOverlay);
69 
70  connect(mView, &GraphGraphicsView::moduleDoubleClicked, this, &GraphWidget::handleModuleDoubleClicked);
71 
72  mOverlay->hide();
73  mOverlay->setWidget(mNavigationWidgetV3);
74  mSpinnerWidget->hide();
75  mContentLayout->addWidget(mView);
76 
81 
82  mContext->setParentWidget(this);
83 
84  if (!mContext->sceneUpdateInProgress())
85  {
86  mView->setScene(mContext->scene());
87  mView->centerOn(0, 0);
88  }
89  sInstance = this;
91  geif->register_progress_indicator(&GraphWidget::pluginProgressIndicator);
92  }
93 
95  {
96  sInstance = nullptr;
97  }
98 
100  {
101  return mContext;
102  }
103 
105  {
106  mView->setScene(mContext->scene());
107 
108  connect(mOverlay, &WidgetOverlay::clicked, this, &GraphWidget::hideOverlay);
109 
110  hideOverlay();
111  mSpinnerWidget->hide();
112  mOverlay->setWidget(mNavigationWidgetV3);
113 
114  if (hasFocus())
115  mView->setFocus();
116  else if (mStoreViewport.mValid)
117  {
118  mView->fitInView(restoreViewport(), Qt::KeepAspectRatio);
119  }
120  }
121 
122  void GraphWidget::hideOverlay()
123  {
124  mOverlay->hide();
125  if (mProgressBar)
126  {
127  mProgressBar->deleteLater();
128  mProgressBar = nullptr;
129  mOverlay->clearWidget();
130  }
131  if (mCommentWidget)
132  {
133  mCommentWidget->deleteLater();
134  mCommentWidget = nullptr;
135  mOverlay->clearWidget();
136  }
137  }
138 
139  void GraphWidget::showBusy(int percent, const QString &text)
140  {
141  if (!mProgressBar)
142  {
143  mProgressBar = new BusyIndicator;
144  mProgressBar->setMinimumSize(256,288);
145  mOverlay->setWidget(mProgressBar);
147  mOverlay->show();
148  }
149  if (!text.isEmpty())
150  mProgressBar->setText(text);
151  mProgressBar->setValue(percent);
152  if (QThread::currentThread() == qApp->thread())
153  {
154  qApp->processEvents();
155  }
156  }
157 
159  {
160  mCommentWidget = new CommentWidget(this);
161  mCommentWidget->nodeChanged(nd);
162  mOverlay->setWidget(mCommentWidget);
163  mOverlay->show();
164  }
165 
166  void GraphWidget::showProgress(int percent, const QString& text)
167  {
168  if (!mProgressBar)
169  {
170  mProgressBar = new ProgressBar(mContext->getLayouter()->canRollback() ? mContext : nullptr);
171  mProgressBar->setMinimumSize(width() / 2, height() / 4);
172  mOverlay->setWidget(mProgressBar);
174  mOverlay->show();
175  }
176 
177  if (!text.isEmpty())
178  mProgressBar->setText(text);
179  mProgressBar->setValue(percent);
180  qApp->processEvents();
181  }
182 
184  {
185  mView->setScene(nullptr);
186 
187  disconnect(mOverlay, &WidgetOverlay::clicked, this, &GraphWidget::hideOverlay);
188 
189  mOverlay->setWidget(mSpinnerWidget);
190 
191  if (mOverlay->isHidden())
192  mOverlay->show();
193  }
194 
196  {
197  mView->setScene(nullptr);
198  mContext = nullptr;
199  }
200 
202  {
203  if (!mContext->getLayouter()->done())
204  {
205  mStoreViewport.mValid = false;
206  return;
207  }
208  QRect vg = mView->viewport()->geometry();
209  QPoint viewportCenter = (vg.topLeft() + vg.bottomRight()) / 2;
210  mStoreViewport.mValid = true;
211  mStoreViewport.mRect = mView->mapToScene(mView->viewport()->geometry()).boundingRect();
212  mStoreViewport.mGrid = mView->closestLayouterPos(mView->mapToScene(QPoint(viewportCenter)));
213  // qDebug() << "store" << viewportCenter << mStoreViewport.mGrid.first << mStoreViewport.mGrid.second << mStoreViewport.mRect;
214  }
215 
216  QRectF GraphWidget::restoreViewport(bool reset)
217  {
218  if (!mStoreViewport.mValid)
219  return QRectF();
220  if (reset)
221  mStoreViewport.mValid = false;
222 
223  QPointF centerPos(mContext->getLayouter()->gridXposition(mStoreViewport.mGrid.first.x()), mContext->getLayouter()->gridYposition(mStoreViewport.mGrid.first.y()));
224  QPointF topLeft = mStoreViewport.mRect.topLeft() - mStoreViewport.mGrid.second + centerPos;
225  return QRectF(topLeft, mStoreViewport.mRect.size());
226  }
227 
229  {
230  if (!mContext)
231  return;
232 
233  if (mContext->sceneUpdateInProgress())
234  return;
235 
236  switch (event->key())
237  {
238  case Qt::Key_Left: {
239  handleNavigationLeftRequest();
240  break;
241  }
242  case Qt::Key_Right: {
243  handleNavigationRightRequest();
244  break;
245  }
246  case Qt::Key_Up: {
247  handleNavigationUpRequest();
248  break;
249  }
250  case Qt::Key_Down: {
251  handleNavigationDownRequest();
252  break;
253  }
254  case Qt::Key_Z: {
255  if (event->modifiers() & Qt::ControlModifier) // modifiers are set as bitmasks
256  {
257  }
258  break;
259  }
260  case Qt::Key_Escape: {
262  break;
263  }
264  default:
265  break;
266  }
267  }
268 
269  void GraphWidget::substituteByVisibleModules(const QSet<u32>& gates,
270  const QSet<u32>& modules,
271  QSet<u32>& target_gates,
272  QSet<u32>& target_modules,
273  QSet<u32>& remove_gates,
274  QSet<u32>& remove_modules) const
275  {
276  // EXPAND SELECTION AND CONTEXT UP THE HIERARCHY TREE
277 
278  for (auto& mid : modules)
279  {
280  auto m = gNetlist->get_module_by_id(mid);
281  QSet<u32> common = gui_utility::parentModules(m) & mContext->modules();
282  if (common.empty())
283  {
284  // we can select the module
285  target_modules.insert(mid);
286  }
287  else
288  {
289  // we must select the respective parent module instead
290  // (this "common" set only has one element)
291  assert(common.size() == 1);
292  target_modules += common;
293  }
294  }
295 
296  for (auto& gid : gates)
297  {
298  auto g = gNetlist->get_gate_by_id(gid);
299  QSet<u32> common = gui_utility::parentModules(g) & mContext->modules();
300  if (common.empty())
301  {
302  target_gates.insert(gid);
303  }
304  else
305  {
306  // At this stage, "common" could contain multiple elements because
307  // we might have inserted a parent module where its child module is
308  // already mVisible. This is cleaned up later.
309  target_modules += common;
310  }
311  }
312 
313  // PRUNE SELECTION AND CONTEXT DOWN THE HIERARCHY TREE
314 
315  // discard (and if required schedule for removal) all modules whose
316  // parent modules we'll be showing
317  QSet<u32> new_module_set = mContext->modules() + target_modules;
318  for (auto& mid : mContext->modules())
319  {
320  auto m = gNetlist->get_module_by_id(mid);
321  if (gui_utility::parentModules(m).intersects(new_module_set))
322  {
323  remove_modules.insert(mid);
324  }
325  }
326  auto it = target_modules.constBegin();
327  while (it != target_modules.constEnd())
328  {
329  auto m = gNetlist->get_module_by_id(*it);
330  if (gui_utility::parentModules(m).intersects(new_module_set))
331  {
332  it = target_modules.erase(it);
333  }
334  else
335  {
336  ++it;
337  }
338  }
339 
340  // discard (and if required schedule for removal) all gates whose
341  // parent modules we'll be showing
342  new_module_set = (mContext->modules() - remove_modules) + target_modules;
343  for (auto& gid : mContext->gates())
344  {
345  auto g = gNetlist->get_gate_by_id(gid);
346  if (gui_utility::parentModules(g).intersects(new_module_set))
347  {
348  remove_gates.insert(gid);
349  }
350  }
351  it = target_gates.constBegin();
352  while (it != target_gates.constEnd())
353  {
354  auto g = gNetlist->get_gate_by_id(*it);
355  if (gui_utility::parentModules(g).intersects(new_module_set))
356  {
357  it = target_gates.erase(it);
358  }
359  else
360  {
361  ++it;
362  }
363  }
364  // qDebug() << "-----------";
365  // qDebug() << "requested gates" << gates;
366  // qDebug() << "requested modules" << modules;
367  // qDebug() << "target gates" << target_gates;
368  // qDebug() << "target modules" << target_modules;
369  // qDebug() << "remove gates" << remove_gates;
370  // qDebug() << "remove modules" << remove_modules;
371  }
372 
373  void GraphWidget::handleNavigationJumpRequested(const Node& origin, const u32 via_net, const QSet<u32>& to_gates, const QSet<u32>& to_modules)
374  {
375  // bool bail_animation = false;
376 
377  setFocus();
378  storeViewport();
379 
380  // ASSERT INPUTS ARE VALID
381  auto n = gNetlist->get_net_by_id(via_net);
382  if (!n || (to_gates.empty() && to_modules.empty()))
383  {
384  // prevent stuck navigation widget
385  mOverlay->hide();
386  mView->setFocus();
387  return;
388  }
389 
390  // Substitute all gates by their modules if we're showing them.
391  // This avoids ripping gates out of their already mVisible modules.
392  QSet<u32> final_modules, remove_modules;
393  QSet<u32> final_gates, remove_gates;
394  substituteByVisibleModules(to_gates, to_modules, final_gates, final_modules, remove_gates, remove_modules);
395 
396  // find out which gates and modules we still need to add to the context
397  // (this makes the cone view work)
398  QSet<u32> nonvisible_gates = final_gates - mContext->gates();
399  QSet<u32> nonvisible_modules = final_modules - mContext->modules();
400 
401  // if we don't have all gates and modules, we need to add them
402  if (!nonvisible_gates.empty() || !nonvisible_modules.empty())
403  {
404  // hint the layouter at the direction we're navigating in
405  // (so the cone view nicely extends to the right or left)
406  // either they're all inputs or all outputs, so just check the first one
407 
408  std::vector<Net*> in_nets;
409  if (to_gates.empty())
410  {
412  }
413  else
414  {
415  in_nets = utils::to_vector(gNetlist->get_gate_by_id(*to_gates.begin())->get_fan_in_nets());
416  }
417  bool netIsInput = std::find(in_nets.begin(), in_nets.end(), n) != in_nets.cend();
419 
420  // add all new gates and modules
421  if (!remove_modules.isEmpty() || !remove_gates.isEmpty())
422  {
423  ActionRemoveItemsFromObject* act = new ActionRemoveItemsFromObject(remove_modules, remove_gates);
424  act->setObject(UserActionObject(mContext->id(), UserActionObjectType::ContextView));
425  act->exec();
426  }
427  if (!nonvisible_modules.isEmpty() || !nonvisible_gates.isEmpty())
428  {
429  ActionAddItemsToObject* act = new ActionAddItemsToObject(nonvisible_modules, nonvisible_gates);
430  act->setPlacementHint(PlacementHint(placementMode, origin));
431  act->setObject(UserActionObject(mContext->id(), UserActionObjectType::ContextView));
432  act->exec();
433  }
434 
435  // FIXME find out how to do this properly
436  // If we have added any gates, the scene may have resized. In that case, the animation can be erratic,
437  // so we set a mFlag here that we can't run the animation. See end of this method for more details.
438  // bail_animation = true;
439  }
440  else
441  {
442  // if we don't need to add anything, we're done here
443 
444  mOverlay->hide();
445  //if (hasFocus())
446  mView->setFocus();
447  }
448 
449  // SELECT IN RELAY
451 
452  // TODO implement subselections on modules, then add a case for when the
453  // selection is only one module (instead of one gate)
454 
456  u32 sfinx = 0;
457 
458  if (final_gates.size() == 1 && final_modules.empty())
459  {
460  // subfocus only possible when just one gate selected
461  u32 gid = *final_gates.begin();
462  auto g = gNetlist->get_gate_by_id(gid);
463 
464  u32 cnt = 0;
465  // TODO simplify (we do actually know if we're navigating left or right)
466  for (const auto& pin : g->get_type()->get_input_pins())
467  {
468  if (g->get_fan_in_net(pin) == n) // input net
469  {
471  sfinx = cnt;
472  break;
473  }
474  cnt++;
475  }
476  if (sfoc == SelectionRelay::Subfocus::None)
477  {
478  cnt = 0;
479  for (const auto& pin : g->get_type()->get_output_pins())
480  {
481  if (g->get_fan_out_net(pin) == n) // input net
482  {
484  sfinx = cnt;
485  break;
486  }
487  cnt++;
488  }
489  }
491  }
492  else if (final_modules.size() == 1 && final_gates.empty())
493  {
494  // subfocus only possible when just one module selected
495  u32 mid = *final_modules.begin();
496  auto m = gNetlist->get_module_by_id(mid);
497  Q_ASSERT(m);
498 
499  // TODO simplify (we do actually know if we're navigating left or right)
500  Node needle(mid, Node::Module);
501  const NodeBox* nbox = mContext->getLayouter()->boxes().boxForNode(needle);
502  Q_ASSERT(nbox);
503  const GraphicsNode* gnode = static_cast<const GraphicsNode*>(nbox->item());
504  Q_ASSERT(gnode);
505 
506  int inx = gnode->inputByNet(n->get_id());
507  if (inx < 0)
508  {
509  inx = gnode->outputByNet(n->get_id());
510  if (inx >= 0)
511  {
513  sfinx = inx;
514  }
515  }
516  else
517  {
519  sfinx = inx;
520  }
522  }
523 
524  gSelectionRelay->setSelectedModules(final_modules);
525  gSelectionRelay->setSelectedGates(final_gates);
527 
528  // FIXME If the scene has been resized during this method, the animation triggered by
529  // ensure_gates_visible is broken. Thus, if that is the case, we bail out here and not
530  // trigger the animation.
531  // if (bail_animation)
532  // return;
533 
534  // JUMP TO THE GATES AND MODULES
535  ensureItemsVisible(final_gates, final_modules);
536  }
537 
538  void GraphWidget::handleModuleDoubleClicked(const u32 id)
539  {
540  // CONNECT DIRECTLY TO HANDLE ???
541  // MAYBE ADDITIONAL CODE NECESSARY HERE...
542  handleEnterModuleRequested(id);
543  }
544 
545  // ADD SOUND OR ERROR MESSAGE TO FAILED NAVIGATION ATTEMPTS
546  void GraphWidget::handleNavigationLeftRequest()
547  {
548  if (!hasFocusedItem(SelectionRelay::Subfocus::Left)) return;
550  mOverlay->setWidget(mNavigationWidgetV3);
551  switch (gSelectionRelay->focusType())
552  {
554  return;
555  }
558 
559  if (!g)
560  return;
561 
563  {
564  const GatePin* pin = g->get_type()->get_input_pins().at(gSelectionRelay->subfocusIndex());
565  Net* n = g->get_fan_in_net(pin);
566 
567  if (!n)
568  return;
569 
570  if (n->get_num_of_sources() == 0)
571  {
573  gSelectionRelay->addNet(n->get_id());
576  }
577  else if (n->get_num_of_sources() == 1)
578  {
579  handleNavigationJumpRequested(Node(g->get_id(), Node::Gate), n->get_id(), {n->get_sources().at(0)->get_gate()->get_id()}, {});
580  }
581  else
582  {
583  mNavigationWidgetV3->setup(navigateLeft);
584  mNavigationWidgetV3->setFocus();
585  mOverlay->show();
586  }
587  }
588  else if (g->get_type()->get_input_pins().size())
589  {
593  }
594 
595  return;
596  }
599 
600  if (!n)
601  return;
602 
603  if (n->get_num_of_sources() == 0)
604  return;
605 
606  if (n->get_num_of_sources() == 1)
607  {
608  handleNavigationJumpRequested(mContext->getNetDestination(n), n->get_id(), {n->get_sources()[0]->get_gate()->get_id()}, {});
609  }
610  else
611  {
612  mNavigationWidgetV3->setup(navigateLeft);
613  mNavigationWidgetV3->setFocus();
614  mOverlay->show();
615  }
616 
617  return;
618  }
621 
622  if (!m)
623  return;
624 
626  {
627  Node needle(m->get_id(), Node::Module);
628  const NodeBox* nbox = mContext->getLayouter()->boxes().boxForNode(needle);
629  Q_ASSERT(nbox);
630  const GraphicsNode* gnode = static_cast<const GraphicsNode*>(nbox->item());
631  Q_ASSERT(gnode);
632  Net* n = gNetlist->get_net_by_id(gnode->inputNets().at(gSelectionRelay->subfocusIndex()));
633  Q_ASSERT(n);
634 
635  if (n->get_num_of_sources() == 0)
636  {
638  gSelectionRelay->addNet(n->get_id());
641  }
642  else if (n->get_num_of_sources() == 1)
643  {
644  handleNavigationJumpRequested(Node(m->get_id(), Node::Module), n->get_id(), {n->get_sources()[0]->get_gate()->get_id()}, {});
645  }
646  else
647  {
648  mNavigationWidgetV3->setup(navigateLeft);
649  mNavigationWidgetV3->setFocus();
650  mOverlay->show();
651  }
652  }
653  else if (m->get_input_nets().size())
654  {
657  }
658 
659  return;
660  }
661  }
662  }
663 
664  void GraphWidget::handleNavigationRightRequest()
665  {
666  if (!hasFocusedItem(SelectionRelay::Subfocus::Right)) return;
668  mOverlay->setWidget(mNavigationWidgetV3);
669  switch (gSelectionRelay->focusType())
670  {
672  return;
673  }
676 
677  if (!g)
678  return;
679 
681  {
682  auto n = g->get_fan_out_net(g->get_type()->get_output_pins()[gSelectionRelay->subfocusIndex()]);
683  if (!n)
684  return;
685 
686  if (n->get_num_of_destinations() == 0)
687  {
689  gSelectionRelay->addNet(n->get_id());
692  }
693  else if (n->get_num_of_destinations() == 1)
694  {
695  handleNavigationJumpRequested(Node(g->get_id(), Node::Gate), n->get_id(), {n->get_destinations()[0]->get_gate()->get_id()}, {});
696  }
697  else
698  {
699  mNavigationWidgetV3->setup(navigateRight);
700  mNavigationWidgetV3->setFocus();
701  mOverlay->show();
702  }
703  }
704  else if (g->get_type()->get_output_pins().size())
705  {
708  }
709 
710  return;
711  }
714 
715  if (!n)
716  return;
717 
718  if (n->get_num_of_destinations() == 0)
719  return;
720 
721  if (n->get_num_of_destinations() == 1)
722  {
723  handleNavigationJumpRequested(mContext->getNetSource(n), n->get_id(), {n->get_destinations()[0]->get_gate()->get_id()}, {});
724  }
725  else
726  {
727  mNavigationWidgetV3->setup(navigateRight);
728  mNavigationWidgetV3->setFocus();
729  mOverlay->show();
730  }
731 
732  return;
733  }
736 
737  if (!m)
738  return;
739 
741  {
742  Node needle(m->get_id(), Node::Module);
743  const NodeBox* nbox = mContext->getLayouter()->boxes().boxForNode(needle);
744  Q_ASSERT(nbox);
745  const GraphicsNode* gnode = static_cast<const GraphicsNode*>(nbox->item());
746  Q_ASSERT(gnode);
747  Net* n = gNetlist->get_net_by_id(gnode->outputNets().at(gSelectionRelay->subfocusIndex()));
748  Q_ASSERT(n);
749 
750  if (n->get_num_of_destinations() == 0)
751  {
753  gSelectionRelay->addNet(n->get_id());
756  }
757  else if (n->get_num_of_destinations() == 1)
758  {
759  handleNavigationJumpRequested(Node(m->get_id(), Node::Module), n->get_id(), {n->get_destinations()[0]->get_gate()->get_id()}, {});
760  }
761  else
762  {
763  mNavigationWidgetV3->setup(navigateRight);
764  mNavigationWidgetV3->setFocus();
765  mOverlay->show();
766  }
767  }
768  else if (m->get_output_nets().size())
769  {
772  }
773  }
774  }
775  }
776 
777  bool GraphWidget::hasFocusedItem(SelectionRelay::Subfocus navigateDirection) const
778  {
780  {
781  // focus item has not been set but since only one item is selected it is obvious what the user wants
788  }
789 
790  u32 id = gSelectionRelay->focusId();
791  switch (gSelectionRelay->focusType())
792  {
794  if (!mContext->modules().contains(id))
795  {
796  log_warning("gui", "Cannot navigate from selected origin module ID={}, folded module not found on current view.", id);
797  return false;
798  }
799  break;
801  if (!mContext->gates().contains(id))
802  {
803  log_warning("gui", "Cannot navigate from selected origin gate ID={}, gate not found on current view.", id);
804  return false;
805  }
806  break;
808  switch (navigateDirection) {
810  for (const Endpoint* ep : gNetlist->get_net_by_id(id)->get_sources())
811  if (ep->get_gate()) return true;
812  break;
814  for (const Endpoint* ep : gNetlist->get_net_by_id(id)->get_destinations())
815  if (ep->get_gate()) return true;
816  break;
817  default:
818  return true;
819  }
820  log_warning("gui", "Cannot navigate from selected origin net ID={}, net not found on current view.", id);
821  return false;
822  default:
823  log_warning("gui", "Cannot navigate, no origin selected");
824  return false;
825  }
826  return true;
827  }
828 
829  void GraphWidget::handleNavigationUpRequest()
830  {
831  if (!hasFocusedItem(SelectionRelay::Subfocus::None)) return;
832  // FIXME this is ugly
836  }
837 
838  void GraphWidget::handleNavigationDownRequest()
839  {
840  if (!hasFocusedItem(SelectionRelay::Subfocus::None)) return;
841  // FIXME this is ugly
845  }
846 
847  void GraphWidget::handleEnterModuleRequested(const u32 id)
848  {
849  auto m = gNetlist->get_module_by_id(id);
850  if (m->get_gates().empty() && m->get_submodules().empty())
851  {
852  QMessageBox msg;
853  msg.setText("This module does not contain any gates, it cannot be unfolded.");
854  msg.setWindowTitle("Error");
855  msg.exec();
856  return;
857  // We would otherwise allow creation of a context with no gates, which
858  // is bad because that context won't react to any updates since empty
859  // contexts can't infer their corresponding module from their contents
860  }
861 
862  if (mContext->gates().isEmpty() && mContext->modules() == QSet<u32>({id}))
863  {
864  ActionUnfoldModule* act = new ActionUnfoldModule(id);
865  act->setContextId(mContext->id());
866  act->exec();
867  }
868  else
870  }
871 
872  void GraphWidget::ensureItemsVisible(const QSet<u32>& gates, const QSet<u32>& modules)
873  {
874  if (mContext->sceneUpdateInProgress())
875  return;
876 
877  int min_x = INT_MAX;
878  int min_y = INT_MAX;
879  int max_x = INT_MIN;
880  int max_y = INT_MIN;
881 
882  for (auto id : gates)
883  {
884  auto rect = mContext->scene()->getGateItem(id)->sceneBoundingRect();
885 
886  min_x = std::min(min_x, static_cast<int>(rect.left()));
887  max_x = std::max(max_x, static_cast<int>(rect.right()));
888  min_y = std::min(min_y, static_cast<int>(rect.top()));
889  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
890  }
891 
892  // TODO clean up redundancy
893  for (auto id : modules)
894  {
895  auto rect = mContext->scene()->getModuleItem(id)->sceneBoundingRect();
896 
897  min_x = std::min(min_x, static_cast<int>(rect.left()));
898  max_x = std::max(max_x, static_cast<int>(rect.right()));
899  min_y = std::min(min_y, static_cast<int>(rect.top()));
900  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
901  }
902 
903  auto targetRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y).marginsAdded(QMarginsF(20, 20, 20, 20));
904 
905  focusRect(targetRect, true);
906  }
907 
909  {
910  if (mContext->sceneUpdateInProgress())
911  return;
912 
913  if (!mContext->gates().contains(gSelectionRelay->selectedGates()) || !mContext->nets().contains(gSelectionRelay->selectedNets())
914  || !mContext->modules().contains(gSelectionRelay->selectedModules()))
915  return;
916 
917  int min_x = INT_MAX;
918  int min_y = INT_MAX;
919  int max_x = INT_MIN;
920  int max_y = INT_MIN;
921 
922  for (auto id : gSelectionRelay->selectedGatesList())
923  {
924  const GraphicsGate* gg = mContext->scene()->getGateItem(id);
925  if (!gg) continue;
926  auto rect = gg->sceneBoundingRect();
927 
928  min_x = std::min(min_x, static_cast<int>(rect.left()));
929  max_x = std::max(max_x, static_cast<int>(rect.right()));
930  min_y = std::min(min_y, static_cast<int>(rect.top()));
931  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
932  }
933 
934  for (auto id : gSelectionRelay->selectedNetsList())
935  {
936  const GraphicsNet* gn = mContext->scene()->getNetItem(id);
937  if (!gn) continue;
938  auto rect = gn->sceneBoundingRect();
939 
940  min_x = std::min(min_x, static_cast<int>(rect.left()));
941  max_x = std::max(max_x, static_cast<int>(rect.right()));
942  min_y = std::min(min_y, static_cast<int>(rect.top()));
943  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
944  }
945 
946  for (auto id : gSelectionRelay->selectedModulesList())
947  {
948  const GraphicsModule* gm = mContext->scene()->getModuleItem(id);
949  if (!gm) continue;
950  auto rect = gm->sceneBoundingRect();
951 
952  min_x = std::min(min_x, static_cast<int>(rect.left()));
953  max_x = std::max(max_x, static_cast<int>(rect.right()));
954  min_y = std::min(min_y, static_cast<int>(rect.top()));
955  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
956  }
957 
958  if (min_x == INT_MAX ||
959  min_y == INT_MAX ||
960  max_x == INT_MIN ||
961  max_y == INT_MIN )
962  {
963  log_warning("gui", "Attempt to zoom in on graphics item before layout was rendered.");
964  return;
965  }
966 
967  auto targetRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y).marginsAdded(QMarginsF(20, 20, 20, 20));
968 
969  focusRect(targetRect, true);
970  }
971 
972  void GraphWidget::focusRect(QRectF targetRect, bool applyCenterFix)
973  {
974  QRectF currentRect;
975  if (mStoreViewport.mValid)
976  {
977  currentRect = restoreViewport();
978  mView->fitInView(currentRect, Qt::KeepAspectRatio);
979  /*
980  QRect vg = mView->viewport()->geometry();
981  QPoint viewportCenter = (vg.topLeft() + vg.bottomRight()) / 2;
982  QVector<QPoint> qp = mView->closestLayouterPos(mView->mapToScene(QPoint(viewportCenter)));
983  qDebug() << "focus" << viewportCenter << qp[0] << qp[1] << currentRect;
984  */
985  }
986  else
987  {
988  currentRect = mView->mapToScene(mView->viewport()->geometry()).boundingRect();
989  }
990 
991  int durationMsec = sSettingAnimationDuration->value().toInt() * 100;
992 
993  //check prevents jitter bug / resizing bug occuring due to error in 'fitToView'
994  //only happens when current and target are the same as on last usage
995  //solution -> just disable the focus alltogether if current and target are the same as last time, no need to fire animation again
996  if (!(targetRect == mLastTargetRect && currentRect == mRectAfterFocus))
997  {
998  mLastTargetRect = targetRect;
999 
1000  if (applyCenterFix)
1001  {
1002  auto centerFix = targetRect.center();
1003  targetRect.setWidth(std::max(targetRect.width(), currentRect.width()));
1004  targetRect.setHeight(std::max(targetRect.height(), currentRect.height()));
1005  targetRect.moveCenter(centerFix);
1006  }
1007 
1008  if (durationMsec)
1009  {
1010  auto anim = new QVariantAnimation();
1011  anim->setDuration(durationMsec);
1012  anim->setStartValue(currentRect);
1013  anim->setEndValue(targetRect);
1014 
1015  connect(anim, &QVariantAnimation::valueChanged, [=](const QVariant& value) { mView->fitInView(value.toRectF(), Qt::KeepAspectRatio); });
1016 
1017  connect(anim, &QVariantAnimation::finished, [this]() { mRectAfterFocus = mView->mapToScene(mView->viewport()->geometry()).boundingRect(); });
1018 
1020  }
1021  else
1022  {
1023  mView->fitInView(targetRect, Qt::KeepAspectRatio);
1024  }
1025  }
1026  }
1027 
1029  {
1030  const GraphicsGate* gate = mContext->scene()->getGateItem(gateId);
1031 
1032  if (gate)
1033  {
1034  QRectF targetRect = gate->sceneBoundingRect().marginsAdded(QMargins(50, 50, 50, 50));
1035  focusRect(targetRect, false);
1036  }
1037  }
1038 
1040  {
1041  const GraphicsNet* net = mContext->scene()->getNetItem(netId);
1042 
1043  if (net)
1044  {
1045  QRectF targetRect = net->sceneBoundingRect().marginsAdded(QMargins(50, 50, 50, 50));
1046  focusRect(targetRect, false);
1047  }
1048  }
1049 
1051  {
1052  const GraphicsModule* module = mContext->scene()->getModuleItem(moduleId);
1053 
1054  if (module)
1055  {
1056  QRectF targetRect = module->sceneBoundingRect().marginsAdded(QMargins(50, 50, 50, 50));
1057  focusRect(targetRect, false);
1058  }
1059  }
1060 
1061  void GraphWidget::resetFocus()
1062  {
1063  mView->setFocus();
1064  }
1065 
1067  {
1068  return mView;
1069  }
1070 
1071  void GraphWidget::pluginProgressIndicator(int percent, const std::string& msg)
1072  {
1073  // Qt widgets must only be touched on the GUI thread. If we're already on it,
1074  // call showBusy / handleSceneAvailable directly; otherwise post the update to
1075  // the main event queue.
1076  QString qmsg = QString::fromStdString(msg);
1077  auto apply = [percent, qmsg]() {
1078  if (!sInstance) return;
1079  if (percent == 100)
1080  sInstance->handleSceneAvailable();
1081  else
1082  sInstance->showBusy(percent, qmsg);
1083  };
1084  if (QThread::currentThread() == qApp->thread())
1085  {
1086  apply();
1087  qApp->processEvents();
1088  }
1089 
1090  else
1092  }
1093 
1094 } // namespace hal
virtual void setValue(int percent)=0
virtual void setText(const QString &txt)=0
void nodeChanged(const Node &nd)
Abstract class for Widgets within HAL's ContentArea.
QVBoxLayout * mContentLayout
const std::vector< Net * > & get_fan_in_nets() const
Definition: gate.cpp:591
Logical container for modules, gates, and nets.
Definition: graph_context.h:55
GraphicsScene * scene()
Node getNetSource(const Net *n) const
Node getNetDestination(const Net *n) const
const QSet< u32 > & gates() const
const QSet< u32 > & modules() const
void setParentWidget(GraphWidget *gw)
GraphLayouter * getLayouter() const
const QSet< u32 > & nets() const
bool sceneUpdateInProgress() const
void openModuleInView(u32 moduleId, bool unfold)
A view to display the rendered graph (needs a GraphicsScene).
void moduleDoubleClicked(u32 id)
qreal gridXposition(int ix) const
qreal gridYposition(int iy) const
bool canRollback() const
const NodeBoxes & boxes() const
void navigationRequested(const Node &origin, const u32 via_net, const QSet< u32 > &to_gates, const QSet< u32 > &to_modules)
void setup(SelectionRelay::Subfocus direction)
GraphWidget(GraphContext *context, QWidget *parent=nullptr)
void ensureSelectionVisible()
void keyPressEvent(QKeyEvent *event) override
void handleSceneUnavailable()
void showBusy(int percent, const QString &text)
GraphContext * getContext() const
void showProgress(int percent, const QString &text=QString())
static void pluginProgressIndicator(int percent, const std::string &msg)
void handleSceneAvailable()
void focusGate(u32 gateId)
void focusModule(u32 moduleId)
GraphGraphicsView * view()
void showComments(const Node &nd)
void focusNet(u32 netId)
void handleContextAboutToBeDeleted()
Abstract base class for gates.
Definition: graphics_gate.h:48
Abstract base class for modules.
The basic net class all other nets inherit from.
Definition: graphics_net.h:43
const GraphicsGate * getGateItem(const u32 id) const
const GraphicsModule * getModuleItem(const u32 id) const
const GraphicsNet * getNetItem(const u32 id) const
static QMap< QString, GuiExtensionInterface * > getGuiExtensions()
const std::unordered_set< Net * > & get_input_nets() const
Definition: module.cpp:542
std::vector< Endpoint * > get_destinations(const std::function< bool(Endpoint *ep)> &filter=nullptr) const
Definition: net.cpp:426
std::vector< Endpoint * > get_sources(const std::function< bool(Endpoint *ep)> &filter=nullptr) const
Definition: net.cpp:264
Gate * get_gate_by_id(const u32 gate_id) const
Definition: netlist.cpp:193
Module * get_module_by_id(u32 module_id) const
Definition: netlist.cpp:613
Net * get_net_by_id(u32 net_id) const
Definition: netlist.cpp:353
NodeBox * boxForNode(const Node &n) const
boxForNode find NodeBox by node
Definition: node_box.h:200
The Node class object represents a module or a gate.
Definition: gui_def.h:61
@ Module
Definition: gui_def.h:63
@ Gate
Definition: gui_def.h:63
void clicked()
PlacementModeType
The PlacementModeType enum either most compact arrangement (Standard) or to the left or right of give...
Definition: gui_def.h:217
int numberSelectedGates() const
ItemType focusType() const
int numberSelectedItems() const
void setFocus(ItemType ftype, u32 fid, Subfocus sfoc=Subfocus::None, u32 sfinx=0)
const QSet< u32 > & selectedNets() const
void relaySelectionChanged(void *sender)
int numberSelectedNets() const
void relaySubfocusChanged(void *sender)
QList< u32 > selectedModulesList() const
const QSet< u32 > & selectedGates() const
void setFocusDirect(ItemType ftype, u32 fid, Subfocus sfoc=Subfocus::None, u32 sfinx=0)
QList< u32 > selectedNetsList() const
const QSet< u32 > & selectedModules() const
Subfocus subfocus() const
std::vector< u32 > selectedGatesVector() const
int numberSelectedModules() const
u32 subfocusIndex() const
std::vector< u32 > selectedNetsVector() const
QList< u32 > selectedGatesList() const
void setSelectedGates(const QSet< u32 > &ids)
std::vector< u32 > selectedModulesVector() const
void setSelectedModules(const QSet< u32 > &ids)
virtual QVariant value() const override
A loading wheel.
Container for a QWidget that overlays another one.
void setWidget(QWidget *widget)
#define log_warning(channel,...)
Definition: log.h:76
const Module * module(const Gate *g, const NodeBoxes &boxes)
QSet< u32 > parentModules(Gate *g)
Definition: netlist.cpp:84
CORE_API std::vector< T > to_vector(const Container< T, Args... > &container)
Definition: utils.h:513
GraphContextManager * gGraphContextManager
Definition: plugin_gui.cpp:85
SelectionRelay * gSelectionRelay
Definition: plugin_gui.cpp:83
Netlist * gNetlist
Definition: plugin_gui.cpp:80
n
Definition: test.py:6
quint32 u32
Net * net
QWidget * viewport() const const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void setFrameStyle(int style)
QRectF sceneBoundingRect() const const
void centerOn(const QPointF &pos)
void setDragMode(QGraphicsView::DragMode mode)
void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode)
QPointF mapToScene(const QPoint &point) const const
void setRenderHint(QPainter::RenderHint hint, bool enabled)
void setScene(QGraphicsScene *scene)
void setTransformationAnchor(QGraphicsView::ViewportAnchor anchor)
virtual int exec() override
void setWindowTitle(const QString &title)
void setText(const QString &text)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QThread * thread() const const
QPoint bottomRight() const const
QPoint topLeft() const const
QPointF center() const const
qreal height() const const
QRectF marginsAdded(const QMarginsF &margins) const const
void moveCenter(const QPointF &position)
void setHeight(qreal height)
void setWidth(qreal width)
qreal width() const const
QSet::iterator begin()
QSet::const_iterator constBegin() const const
QSet::const_iterator constEnd() const const
bool contains(const T &value) const const
bool empty() const const
QSet::iterator erase(QSet::iterator pos)
QSet::iterator insert(const T &value)
bool intersects(const QSet< T > &other) const const
bool isEmpty() const const
int size() const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
KeepAspectRatio
QueuedConnection
Key_Left
ControlModifier
QThread * currentThread()
int toInt(bool *ok) const const
QRectF toRectF() const const
void valueChanged(const QVariant &value)
virtual bool event(QEvent *event) override
bool hasFocus() const const
void hide()
bool isHidden() const const
void setMinimumSize(const QSize &)
void setFocus()
void show()
void setSizePolicy(QSizePolicy)