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 <QTimer>
43 #include <QToolButton>
44 #include <QVBoxLayout>
45 #include <QVariantAnimation>
46 
47 namespace hal
48 {
49  SettingsItemSpinbox* GraphWidget::sSettingAnimationDuration =
50  new SettingsItemSpinbox("Animation Duration [1/10s]",
51  "graph_view/animation_duration",
52  10,
53  "Appearance:Graph View",
54  "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.");
55 
56  GraphWidget* GraphWidget::sInstance = nullptr;
57 
59  : ContentWidget("Graph", parent), mView(new GraphGraphicsView(this)), mContext(context), mOverlay(new WidgetOverlay(this)),
60  mNavigationWidgetV3(new GraphNavigationWidget(false)), mProgressBar(nullptr),
61  mSpinnerWidget(new SpinnerWidget(this)), mCommentWidget(nullptr), mCurrentExpansion(0)
62  {
63  connect(mNavigationWidgetV3, &GraphNavigationWidget::navigationRequested, this, &GraphWidget::handleNavigationJumpRequested);
64  connect(mNavigationWidgetV3, &GraphNavigationWidget::closeRequested, mOverlay, &WidgetOverlay::hide);
65  connect(mNavigationWidgetV3, &GraphNavigationWidget::closeRequested, this, &GraphWidget::resetFocus);
66 
67  connect(mOverlay, &WidgetOverlay::clicked, this, &GraphWidget::hideOverlay);
68 
69  connect(mView, &GraphGraphicsView::moduleDoubleClicked, this, &GraphWidget::handleModuleDoubleClicked);
70 
71  mOverlay->hide();
72  mOverlay->setWidget(mNavigationWidgetV3);
73  mSpinnerWidget->hide();
74  mContentLayout->addWidget(mView);
75 
80 
81  mContext->setParentWidget(this);
82 
83  if (!mContext->sceneUpdateInProgress())
84  {
85  mView->setScene(mContext->scene());
86  mView->centerOn(0, 0);
87  }
88  sInstance = this;
90  geif->register_progress_indicator(&GraphWidget::pluginProgressIndicator);
91  }
92 
94  {
95  sInstance = nullptr;
96  }
97 
99  {
100  return mContext;
101  }
102 
104  {
105  mView->setScene(mContext->scene());
106 
107  connect(mOverlay, &WidgetOverlay::clicked, this, &GraphWidget::hideOverlay);
108 
109  hideOverlay();
110  mSpinnerWidget->hide();
111  mOverlay->setWidget(mNavigationWidgetV3);
112 
113  if (hasFocus())
114  mView->setFocus();
115  else if (mStoreViewport.mValid)
116  {
117  mView->fitInView(restoreViewport(), Qt::KeepAspectRatio);
118  }
119  }
120 
121  void GraphWidget::hideOverlay()
122  {
123  mOverlay->hide();
124  if (mProgressBar)
125  {
126  mProgressBar->deleteLater();
127  mProgressBar = nullptr;
128  mOverlay->clearWidget();
129  }
130  if (mCommentWidget)
131  {
132  mCommentWidget->deleteLater();
133  mCommentWidget = nullptr;
134  mOverlay->clearWidget();
135  }
136  }
137 
138  void GraphWidget::showBusy(int percent, const QString &text)
139  {
140  if (!mProgressBar)
141  {
142  mProgressBar = new BusyIndicator;
143  mProgressBar->setMinimumSize(256,288);
144  mOverlay->setWidget(mProgressBar);
146  mOverlay->show();
147  }
148  if (!text.isEmpty())
149  mProgressBar->setText(text);
150  mProgressBar->setValue(percent);
151  qApp->processEvents();
152  }
153 
155  {
156  mCommentWidget = new CommentWidget(this);
157  mCommentWidget->nodeChanged(nd);
158  mOverlay->setWidget(mCommentWidget);
159  mOverlay->show();
160  }
161 
162  void GraphWidget::showProgress(int percent, const QString& text)
163  {
164  if (!mProgressBar)
165  {
166  mProgressBar = new ProgressBar(mContext->getLayouter()->canRollback() ? mContext : nullptr);
167  mProgressBar->setMinimumSize(width() / 2, height() / 4);
168  mOverlay->setWidget(mProgressBar);
170  mOverlay->show();
171  }
172 
173  if (!text.isEmpty())
174  mProgressBar->setText(text);
175  mProgressBar->setValue(percent);
176  qApp->processEvents();
177  }
178 
180  {
181  mView->setScene(nullptr);
182 
183  disconnect(mOverlay, &WidgetOverlay::clicked, this, &GraphWidget::hideOverlay);
184 
185  mOverlay->setWidget(mSpinnerWidget);
186 
187  if (mOverlay->isHidden())
188  mOverlay->show();
189  }
190 
192  {
193  mView->setScene(nullptr);
194  mContext = nullptr;
195  }
196 
198  {
199  if (!mContext->getLayouter()->done())
200  {
201  mStoreViewport.mValid = false;
202  return;
203  }
204  QRect vg = mView->viewport()->geometry();
205  QPoint viewportCenter = (vg.topLeft() + vg.bottomRight()) / 2;
206  mStoreViewport.mValid = true;
207  mStoreViewport.mRect = mView->mapToScene(mView->viewport()->geometry()).boundingRect();
208  mStoreViewport.mGrid = mView->closestLayouterPos(mView->mapToScene(QPoint(viewportCenter)));
209  // qDebug() << "store" << viewportCenter << mStoreViewport.mGrid.first << mStoreViewport.mGrid.second << mStoreViewport.mRect;
210  }
211 
212  QRectF GraphWidget::restoreViewport(bool reset)
213  {
214  if (!mStoreViewport.mValid)
215  return QRectF();
216  if (reset)
217  mStoreViewport.mValid = false;
218 
219  QPointF centerPos(mContext->getLayouter()->gridXposition(mStoreViewport.mGrid.first.x()), mContext->getLayouter()->gridYposition(mStoreViewport.mGrid.first.y()));
220  QPointF topLeft = mStoreViewport.mRect.topLeft() - mStoreViewport.mGrid.second + centerPos;
221  return QRectF(topLeft, mStoreViewport.mRect.size());
222  }
223 
225  {
226  if (!mContext)
227  return;
228 
229  if (mContext->sceneUpdateInProgress())
230  return;
231 
232  switch (event->key())
233  {
234  case Qt::Key_Left: {
235  handleNavigationLeftRequest();
236  break;
237  }
238  case Qt::Key_Right: {
239  handleNavigationRightRequest();
240  break;
241  }
242  case Qt::Key_Up: {
243  handleNavigationUpRequest();
244  break;
245  }
246  case Qt::Key_Down: {
247  handleNavigationDownRequest();
248  break;
249  }
250  case Qt::Key_Z: {
251  if (event->modifiers() & Qt::ControlModifier) // modifiers are set as bitmasks
252  {
253  }
254  break;
255  }
256  case Qt::Key_Escape: {
258  break;
259  }
260  default:
261  break;
262  }
263  }
264 
265  void GraphWidget::substituteByVisibleModules(const QSet<u32>& gates,
266  const QSet<u32>& modules,
267  QSet<u32>& target_gates,
268  QSet<u32>& target_modules,
269  QSet<u32>& remove_gates,
270  QSet<u32>& remove_modules) const
271  {
272  // EXPAND SELECTION AND CONTEXT UP THE HIERARCHY TREE
273 
274  for (auto& mid : modules)
275  {
276  auto m = gNetlist->get_module_by_id(mid);
277  QSet<u32> common = gui_utility::parentModules(m) & mContext->modules();
278  if (common.empty())
279  {
280  // we can select the module
281  target_modules.insert(mid);
282  }
283  else
284  {
285  // we must select the respective parent module instead
286  // (this "common" set only has one element)
287  assert(common.size() == 1);
288  target_modules += common;
289  }
290  }
291 
292  for (auto& gid : gates)
293  {
294  auto g = gNetlist->get_gate_by_id(gid);
295  QSet<u32> common = gui_utility::parentModules(g) & mContext->modules();
296  if (common.empty())
297  {
298  target_gates.insert(gid);
299  }
300  else
301  {
302  // At this stage, "common" could contain multiple elements because
303  // we might have inserted a parent module where its child module is
304  // already mVisible. This is cleaned up later.
305  target_modules += common;
306  }
307  }
308 
309  // PRUNE SELECTION AND CONTEXT DOWN THE HIERARCHY TREE
310 
311  // discard (and if required schedule for removal) all modules whose
312  // parent modules we'll be showing
313  QSet<u32> new_module_set = mContext->modules() + target_modules;
314  for (auto& mid : mContext->modules())
315  {
316  auto m = gNetlist->get_module_by_id(mid);
317  if (gui_utility::parentModules(m).intersects(new_module_set))
318  {
319  remove_modules.insert(mid);
320  }
321  }
322  auto it = target_modules.constBegin();
323  while (it != target_modules.constEnd())
324  {
325  auto m = gNetlist->get_module_by_id(*it);
326  if (gui_utility::parentModules(m).intersects(new_module_set))
327  {
328  it = target_modules.erase(it);
329  }
330  else
331  {
332  ++it;
333  }
334  }
335 
336  // discard (and if required schedule for removal) all gates whose
337  // parent modules we'll be showing
338  new_module_set = (mContext->modules() - remove_modules) + target_modules;
339  for (auto& gid : mContext->gates())
340  {
341  auto g = gNetlist->get_gate_by_id(gid);
342  if (gui_utility::parentModules(g).intersects(new_module_set))
343  {
344  remove_gates.insert(gid);
345  }
346  }
347  it = target_gates.constBegin();
348  while (it != target_gates.constEnd())
349  {
350  auto g = gNetlist->get_gate_by_id(*it);
351  if (gui_utility::parentModules(g).intersects(new_module_set))
352  {
353  it = target_gates.erase(it);
354  }
355  else
356  {
357  ++it;
358  }
359  }
360  // qDebug() << "-----------";
361  // qDebug() << "requested gates" << gates;
362  // qDebug() << "requested modules" << modules;
363  // qDebug() << "target gates" << target_gates;
364  // qDebug() << "target modules" << target_modules;
365  // qDebug() << "remove gates" << remove_gates;
366  // qDebug() << "remove modules" << remove_modules;
367  }
368 
369  void GraphWidget::handleNavigationJumpRequested(const Node& origin, const u32 via_net, const QSet<u32>& to_gates, const QSet<u32>& to_modules)
370  {
371  // bool bail_animation = false;
372 
373  setFocus();
374  storeViewport();
375 
376  // ASSERT INPUTS ARE VALID
377  auto n = gNetlist->get_net_by_id(via_net);
378  if (!n || (to_gates.empty() && to_modules.empty()))
379  {
380  // prevent stuck navigation widget
381  mOverlay->hide();
382  mView->setFocus();
383  return;
384  }
385 
386  // Substitute all gates by their modules if we're showing them.
387  // This avoids ripping gates out of their already mVisible modules.
388  QSet<u32> final_modules, remove_modules;
389  QSet<u32> final_gates, remove_gates;
390  substituteByVisibleModules(to_gates, to_modules, final_gates, final_modules, remove_gates, remove_modules);
391 
392  // find out which gates and modules we still need to add to the context
393  // (this makes the cone view work)
394  QSet<u32> nonvisible_gates = final_gates - mContext->gates();
395  QSet<u32> nonvisible_modules = final_modules - mContext->modules();
396 
397  // if we don't have all gates and modules, we need to add them
398  if (!nonvisible_gates.empty() || !nonvisible_modules.empty())
399  {
400  // hint the layouter at the direction we're navigating in
401  // (so the cone view nicely extends to the right or left)
402  // either they're all inputs or all outputs, so just check the first one
403 
404  std::vector<Net*> in_nets;
405  if (to_gates.empty())
406  {
408  }
409  else
410  {
411  in_nets = utils::to_vector(gNetlist->get_gate_by_id(*to_gates.begin())->get_fan_in_nets());
412  }
413  bool netIsInput = std::find(in_nets.begin(), in_nets.end(), n) != in_nets.cend();
415 
416  // add all new gates and modules
417  if (!remove_modules.isEmpty() || !remove_gates.isEmpty())
418  {
419  ActionRemoveItemsFromObject* act = new ActionRemoveItemsFromObject(remove_modules, remove_gates);
420  act->setObject(UserActionObject(mContext->id(), UserActionObjectType::ContextView));
421  act->exec();
422  }
423  if (!nonvisible_modules.isEmpty() || !nonvisible_gates.isEmpty())
424  {
425  ActionAddItemsToObject* act = new ActionAddItemsToObject(nonvisible_modules, nonvisible_gates);
426  act->setPlacementHint(PlacementHint(placementMode, origin));
427  act->setObject(UserActionObject(mContext->id(), UserActionObjectType::ContextView));
428  act->exec();
429  }
430 
431  // FIXME find out how to do this properly
432  // If we have added any gates, the scene may have resized. In that case, the animation can be erratic,
433  // so we set a mFlag here that we can't run the animation. See end of this method for more details.
434  // bail_animation = true;
435  }
436  else
437  {
438  // if we don't need to add anything, we're done here
439 
440  mOverlay->hide();
441  //if (hasFocus())
442  mView->setFocus();
443  }
444 
445  // SELECT IN RELAY
447 
448  // TODO implement subselections on modules, then add a case for when the
449  // selection is only one module (instead of one gate)
450 
452  u32 sfinx = 0;
453 
454  if (final_gates.size() == 1 && final_modules.empty())
455  {
456  // subfocus only possible when just one gate selected
457  u32 gid = *final_gates.begin();
458  auto g = gNetlist->get_gate_by_id(gid);
459 
460  u32 cnt = 0;
461  // TODO simplify (we do actually know if we're navigating left or right)
462  for (const auto& pin : g->get_type()->get_input_pins())
463  {
464  if (g->get_fan_in_net(pin) == n) // input net
465  {
467  sfinx = cnt;
468  break;
469  }
470  cnt++;
471  }
472  if (sfoc == SelectionRelay::Subfocus::None)
473  {
474  cnt = 0;
475  for (const auto& pin : g->get_type()->get_output_pins())
476  {
477  if (g->get_fan_out_net(pin) == n) // input net
478  {
480  sfinx = cnt;
481  break;
482  }
483  cnt++;
484  }
485  }
487  }
488  else if (final_modules.size() == 1 && final_gates.empty())
489  {
490  // subfocus only possible when just one module selected
491  u32 mid = *final_modules.begin();
492  auto m = gNetlist->get_module_by_id(mid);
493  Q_ASSERT(m);
494 
495  // TODO simplify (we do actually know if we're navigating left or right)
496  Node needle(mid, Node::Module);
497  const NodeBox* nbox = mContext->getLayouter()->boxes().boxForNode(needle);
498  Q_ASSERT(nbox);
499  const GraphicsNode* gnode = static_cast<const GraphicsNode*>(nbox->item());
500  Q_ASSERT(gnode);
501 
502  int inx = gnode->inputByNet(n->get_id());
503  if (inx < 0)
504  {
505  inx = gnode->outputByNet(n->get_id());
506  if (inx >= 0)
507  {
509  sfinx = inx;
510  }
511  }
512  else
513  {
515  sfinx = inx;
516  }
518  }
519 
520  gSelectionRelay->setSelectedModules(final_modules);
521  gSelectionRelay->setSelectedGates(final_gates);
523 
524  // FIXME If the scene has been resized during this method, the animation triggered by
525  // ensure_gates_visible is broken. Thus, if that is the case, we bail out here and not
526  // trigger the animation.
527  // if (bail_animation)
528  // return;
529 
530  // JUMP TO THE GATES AND MODULES
531  ensureItemsVisible(final_gates, final_modules);
532  }
533 
534  void GraphWidget::handleModuleDoubleClicked(const u32 id)
535  {
536  // CONNECT DIRECTLY TO HANDLE ???
537  // MAYBE ADDITIONAL CODE NECESSARY HERE...
538  handleEnterModuleRequested(id);
539  }
540 
541  // ADD SOUND OR ERROR MESSAGE TO FAILED NAVIGATION ATTEMPTS
542  void GraphWidget::handleNavigationLeftRequest()
543  {
544  if (!hasFocusedItem(SelectionRelay::Subfocus::Left)) return;
546  mOverlay->setWidget(mNavigationWidgetV3);
547  switch (gSelectionRelay->focusType())
548  {
550  return;
551  }
554 
555  if (!g)
556  return;
557 
559  {
560  const GatePin* pin = g->get_type()->get_input_pins().at(gSelectionRelay->subfocusIndex());
561  Net* n = g->get_fan_in_net(pin);
562 
563  if (!n)
564  return;
565 
566  if (n->get_num_of_sources() == 0)
567  {
569  gSelectionRelay->addNet(n->get_id());
572  }
573  else if (n->get_num_of_sources() == 1)
574  {
575  handleNavigationJumpRequested(Node(g->get_id(), Node::Gate), n->get_id(), {n->get_sources().at(0)->get_gate()->get_id()}, {});
576  }
577  else
578  {
579  mNavigationWidgetV3->setup(navigateLeft);
580  mNavigationWidgetV3->setFocus();
581  mOverlay->show();
582  }
583  }
584  else if (g->get_type()->get_input_pins().size())
585  {
589  }
590 
591  return;
592  }
595 
596  if (!n)
597  return;
598 
599  if (n->get_num_of_sources() == 0)
600  return;
601 
602  if (n->get_num_of_sources() == 1)
603  {
604  handleNavigationJumpRequested(mContext->getNetDestination(n), n->get_id(), {n->get_sources()[0]->get_gate()->get_id()}, {});
605  }
606  else
607  {
608  mNavigationWidgetV3->setup(navigateLeft);
609  mNavigationWidgetV3->setFocus();
610  mOverlay->show();
611  }
612 
613  return;
614  }
617 
618  if (!m)
619  return;
620 
622  {
623  Node needle(m->get_id(), Node::Module);
624  const NodeBox* nbox = mContext->getLayouter()->boxes().boxForNode(needle);
625  Q_ASSERT(nbox);
626  const GraphicsNode* gnode = static_cast<const GraphicsNode*>(nbox->item());
627  Q_ASSERT(gnode);
628  Net* n = gNetlist->get_net_by_id(gnode->inputNets().at(gSelectionRelay->subfocusIndex()));
629  Q_ASSERT(n);
630 
631  if (n->get_num_of_sources() == 0)
632  {
634  gSelectionRelay->addNet(n->get_id());
637  }
638  else if (n->get_num_of_sources() == 1)
639  {
640  handleNavigationJumpRequested(Node(m->get_id(), Node::Module), n->get_id(), {n->get_sources()[0]->get_gate()->get_id()}, {});
641  }
642  else
643  {
644  mNavigationWidgetV3->setup(navigateLeft);
645  mNavigationWidgetV3->setFocus();
646  mOverlay->show();
647  }
648  }
649  else if (m->get_input_nets().size())
650  {
653  }
654 
655  return;
656  }
657  }
658  }
659 
660  void GraphWidget::handleNavigationRightRequest()
661  {
662  if (!hasFocusedItem(SelectionRelay::Subfocus::Right)) return;
664  mOverlay->setWidget(mNavigationWidgetV3);
665  switch (gSelectionRelay->focusType())
666  {
668  return;
669  }
672 
673  if (!g)
674  return;
675 
677  {
678  auto n = g->get_fan_out_net(g->get_type()->get_output_pins()[gSelectionRelay->subfocusIndex()]);
679  if (!n)
680  return;
681 
682  if (n->get_num_of_destinations() == 0)
683  {
685  gSelectionRelay->addNet(n->get_id());
688  }
689  else if (n->get_num_of_destinations() == 1)
690  {
691  handleNavigationJumpRequested(Node(g->get_id(), Node::Gate), n->get_id(), {n->get_destinations()[0]->get_gate()->get_id()}, {});
692  }
693  else
694  {
695  mNavigationWidgetV3->setup(navigateRight);
696  mNavigationWidgetV3->setFocus();
697  mOverlay->show();
698  }
699  }
700  else if (g->get_type()->get_output_pins().size())
701  {
704  }
705 
706  return;
707  }
710 
711  if (!n)
712  return;
713 
714  if (n->get_num_of_destinations() == 0)
715  return;
716 
717  if (n->get_num_of_destinations() == 1)
718  {
719  handleNavigationJumpRequested(mContext->getNetSource(n), n->get_id(), {n->get_destinations()[0]->get_gate()->get_id()}, {});
720  }
721  else
722  {
723  mNavigationWidgetV3->setup(navigateRight);
724  mNavigationWidgetV3->setFocus();
725  mOverlay->show();
726  }
727 
728  return;
729  }
732 
733  if (!m)
734  return;
735 
737  {
738  Node needle(m->get_id(), Node::Module);
739  const NodeBox* nbox = mContext->getLayouter()->boxes().boxForNode(needle);
740  Q_ASSERT(nbox);
741  const GraphicsNode* gnode = static_cast<const GraphicsNode*>(nbox->item());
742  Q_ASSERT(gnode);
743  Net* n = gNetlist->get_net_by_id(gnode->outputNets().at(gSelectionRelay->subfocusIndex()));
744  Q_ASSERT(n);
745 
746  if (n->get_num_of_destinations() == 0)
747  {
749  gSelectionRelay->addNet(n->get_id());
752  }
753  else if (n->get_num_of_destinations() == 1)
754  {
755  handleNavigationJumpRequested(Node(m->get_id(), Node::Module), n->get_id(), {n->get_destinations()[0]->get_gate()->get_id()}, {});
756  }
757  else
758  {
759  mNavigationWidgetV3->setup(navigateRight);
760  mNavigationWidgetV3->setFocus();
761  mOverlay->show();
762  }
763  }
764  else if (m->get_output_nets().size())
765  {
768  }
769  }
770  }
771  }
772 
773  bool GraphWidget::hasFocusedItem(SelectionRelay::Subfocus navigateDirection) const
774  {
776  {
777  // focus item has not been set but since only one item is selected it is obvious what the user wants
784  }
785 
786  u32 id = gSelectionRelay->focusId();
787  switch (gSelectionRelay->focusType())
788  {
790  if (!mContext->modules().contains(id))
791  {
792  log_warning("gui", "Cannot navigate from selected origin module ID={}, folded module not found on current view.", id);
793  return false;
794  }
795  break;
797  if (!mContext->gates().contains(id))
798  {
799  log_warning("gui", "Cannot navigate from selected origin gate ID={}, gate not found on current view.", id);
800  return false;
801  }
802  break;
804  switch (navigateDirection) {
806  for (const Endpoint* ep : gNetlist->get_net_by_id(id)->get_destinations())
807  if (hasGate(ep->get_gate())) return true;
808  break;
810  for (const Endpoint* ep : gNetlist->get_net_by_id(id)->get_sources())
811  if (hasGate(ep->get_gate())) return true;
812  break;
813  default:
814  return true;
815  }
816  log_warning("gui", "Cannot navigate from selected origin net ID={}, net not found on current view.", id);
817  return false;
818  default:
819  log_warning("gui", "Cannot navigate, no origin selected");
820  return false;
821  }
822  return true;
823  }
824 
825  bool GraphWidget::hasGate(const Gate* g) const
826  {
827  if (!g) return false;
828  if (mContext->gates().contains(g->get_id())) return true;
829  const Module* parentModule = g->get_module();
830  while (parentModule)
831  {
832  if (mContext->modules().contains(parentModule->get_id()))
833  return true;
834  parentModule = parentModule->get_parent_module();
835  }
836  return false;
837  }
838 
839  void GraphWidget::handleNavigationUpRequest()
840  {
841  if (!hasFocusedItem(SelectionRelay::Subfocus::None)) return;
842  // FIXME this is ugly
846  }
847 
848  void GraphWidget::handleNavigationDownRequest()
849  {
850  if (!hasFocusedItem(SelectionRelay::Subfocus::None)) return;
851  // FIXME this is ugly
855  }
856 
857  void GraphWidget::handleEnterModuleRequested(const u32 id)
858  {
859  auto m = gNetlist->get_module_by_id(id);
860  if (m->get_gates().empty() && m->get_submodules().empty())
861  {
862  QMessageBox msg;
863  msg.setText("This module does not contain any gates, it cannot be unfolded.");
864  msg.setWindowTitle("Error");
865  msg.exec();
866  return;
867  // We would otherwise allow creation of a context with no gates, which
868  // is bad because that context won't react to any updates since empty
869  // contexts can't infer their corresponding module from their contents
870  }
871 
872  if (mContext->gates().isEmpty() && mContext->modules() == QSet<u32>({id}))
873  {
874  ActionUnfoldModule* act = new ActionUnfoldModule(id);
875  act->setContextId(mContext->id());
876  act->exec();
877  }
878  else
880  }
881 
882  void GraphWidget::ensureItemsVisible(const QSet<u32>& gates, const QSet<u32>& modules)
883  {
884  if (mContext->sceneUpdateInProgress())
885  return;
886 
887  int min_x = INT_MAX;
888  int min_y = INT_MAX;
889  int max_x = INT_MIN;
890  int max_y = INT_MIN;
891 
892  for (auto id : gates)
893  {
894  auto rect = mContext->scene()->getGateItem(id)->sceneBoundingRect();
895 
896  min_x = std::min(min_x, static_cast<int>(rect.left()));
897  max_x = std::max(max_x, static_cast<int>(rect.right()));
898  min_y = std::min(min_y, static_cast<int>(rect.top()));
899  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
900  }
901 
902  // TODO clean up redundancy
903  for (auto id : modules)
904  {
905  auto rect = mContext->scene()->getModuleItem(id)->sceneBoundingRect();
906 
907  min_x = std::min(min_x, static_cast<int>(rect.left()));
908  max_x = std::max(max_x, static_cast<int>(rect.right()));
909  min_y = std::min(min_y, static_cast<int>(rect.top()));
910  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
911  }
912 
913  auto targetRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y).marginsAdded(QMarginsF(20, 20, 20, 20));
914 
915  focusRect(targetRect, true);
916  }
917 
919  {
920  if (mContext->sceneUpdateInProgress())
921  return;
922 
923  if (!mContext->gates().contains(gSelectionRelay->selectedGates()) || !mContext->nets().contains(gSelectionRelay->selectedNets())
924  || !mContext->modules().contains(gSelectionRelay->selectedModules()))
925  return;
926 
927  int min_x = INT_MAX;
928  int min_y = INT_MAX;
929  int max_x = INT_MIN;
930  int max_y = INT_MIN;
931 
932  for (auto id : gSelectionRelay->selectedGatesList())
933  {
934  auto rect = mContext->scene()->getGateItem(id)->sceneBoundingRect();
935 
936  min_x = std::min(min_x, static_cast<int>(rect.left()));
937  max_x = std::max(max_x, static_cast<int>(rect.right()));
938  min_y = std::min(min_y, static_cast<int>(rect.top()));
939  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
940  }
941 
942  for (auto id : gSelectionRelay->selectedNetsList())
943  {
944  auto rect = mContext->scene()->getNetItem(id)->sceneBoundingRect();
945 
946  min_x = std::min(min_x, static_cast<int>(rect.left()));
947  max_x = std::max(max_x, static_cast<int>(rect.right()));
948  min_y = std::min(min_y, static_cast<int>(rect.top()));
949  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
950  }
951 
952  for (auto id : gSelectionRelay->selectedModulesList())
953  {
954  auto rect = mContext->scene()->getModuleItem(id)->sceneBoundingRect();
955 
956  min_x = std::min(min_x, static_cast<int>(rect.left()));
957  max_x = std::max(max_x, static_cast<int>(rect.right()));
958  min_y = std::min(min_y, static_cast<int>(rect.top()));
959  max_y = std::max(max_y, static_cast<int>(rect.bottom()));
960  }
961 
962  auto targetRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y).marginsAdded(QMarginsF(20, 20, 20, 20));
963 
964  focusRect(targetRect, true);
965  }
966 
967  void GraphWidget::focusRect(QRectF targetRect, bool applyCenterFix)
968  {
969  QRectF currentRect;
970  if (mStoreViewport.mValid)
971  {
972  currentRect = restoreViewport();
973  mView->fitInView(currentRect, Qt::KeepAspectRatio);
974  /*
975  QRect vg = mView->viewport()->geometry();
976  QPoint viewportCenter = (vg.topLeft() + vg.bottomRight()) / 2;
977  QVector<QPoint> qp = mView->closestLayouterPos(mView->mapToScene(QPoint(viewportCenter)));
978  qDebug() << "focus" << viewportCenter << qp[0] << qp[1] << currentRect;
979  */
980  }
981  else
982  {
983  currentRect = mView->mapToScene(mView->viewport()->geometry()).boundingRect();
984  }
985 
986  int durationMsec = sSettingAnimationDuration->value().toInt() * 100;
987 
988  //check prevents jitter bug / resizing bug occuring due to error in 'fitToView'
989  //only happens when current and target are the same as on last usage
990  //solution -> just disable the focus alltogether if current and target are the same as last time, no need to fire animation again
991  if (!(targetRect == mLastTargetRect && currentRect == mRectAfterFocus))
992  {
993  mLastTargetRect = targetRect;
994 
995  if (applyCenterFix)
996  {
997  auto centerFix = targetRect.center();
998  targetRect.setWidth(std::max(targetRect.width(), currentRect.width()));
999  targetRect.setHeight(std::max(targetRect.height(), currentRect.height()));
1000  targetRect.moveCenter(centerFix);
1001  }
1002 
1003  if (durationMsec)
1004  {
1005  auto anim = new QVariantAnimation();
1006  anim->setDuration(durationMsec);
1007  anim->setStartValue(currentRect);
1008  anim->setEndValue(targetRect);
1009 
1010  connect(anim, &QVariantAnimation::valueChanged, [=](const QVariant& value) { mView->fitInView(value.toRectF(), Qt::KeepAspectRatio); });
1011 
1012  connect(anim, &QVariantAnimation::finished, [this]() { mRectAfterFocus = mView->mapToScene(mView->viewport()->geometry()).boundingRect(); });
1013 
1015  }
1016  else
1017  {
1018  mView->fitInView(targetRect, Qt::KeepAspectRatio);
1019  }
1020  }
1021  }
1022 
1024  {
1025  const GraphicsGate* gate = mContext->scene()->getGateItem(gateId);
1026 
1027  if (gate)
1028  {
1029  QRectF targetRect = gate->sceneBoundingRect().marginsAdded(QMargins(50, 50, 50, 50));
1030  focusRect(targetRect, false);
1031  }
1032  }
1033 
1035  {
1036  const GraphicsNet* net = mContext->scene()->getNetItem(netId);
1037 
1038  if (net)
1039  {
1040  QRectF targetRect = net->sceneBoundingRect().marginsAdded(QMargins(50, 50, 50, 50));
1041  focusRect(targetRect, false);
1042  }
1043  }
1044 
1046  {
1047  const GraphicsModule* module = mContext->scene()->getModuleItem(moduleId);
1048 
1049  if (module)
1050  {
1051  QRectF targetRect = module->sceneBoundingRect().marginsAdded(QMargins(50, 50, 50, 50));
1052  focusRect(targetRect, false);
1053  }
1054  }
1055 
1056  void GraphWidget::resetFocus()
1057  {
1058  mView->setFocus();
1059  }
1060 
1062  {
1063  return mView;
1064  }
1065 
1066  void GraphWidget::pluginProgressIndicator(int percent, const std::string& msg)
1067  {
1068  // qDebug() << "progress" << hex << (quintptr) QThread::currentThread() << (quintptr) gPythonContext->currentThread() << (quintptr) dynamic_cast<PythonThread*>(QThread::currentThread());
1069  if (!sInstance || gPythonContext->pythonThread()) return;
1070  if (percent==100)
1071  sInstance->handleSceneAvailable();
1072  else
1073  sInstance->showBusy(percent, QString::fromStdString(msg));
1074  }
1075 
1076 } // 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:540
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:202
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
PythonContext * gPythonContext
Definition: plugin_gui.cpp:88
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)
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)
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
Key_Left
ControlModifier
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)