Merge lp:~yrke/tapaal/fix1947517 into lp:tapaal

Proposed by Kenneth Yrke Jørgensen
Status: Rejected
Rejected by: Kenneth Yrke Jørgensen
Proposed branch: lp:~yrke/tapaal/fix1947517
Merge into: lp:tapaal
Diff against target: 1181 lines (+371/-308)
13 files modified
src/dk/aau/cs/gui/TabContent.java (+16/-54)
src/dk/aau/cs/gui/TemplateExplorer.java (+108/-238)
src/dk/aau/cs/gui/debug/Debug.kt (+38/-0)
src/dk/aau/cs/gui/debug/UndoRedoSpy.kt (+53/-0)
src/net/tapaal/TAPAAL.java (+2/-1)
src/net/tapaal/swinghelpers/GridBagHelper.java (+12/-6)
src/pipe/gui/GuiFrame.java (+13/-5)
src/pipe/gui/handler/ArcHandler.java (+1/-1)
src/pipe/gui/handler/ArcPathPointHandler.java (+1/-1)
src/pipe/gui/handler/PetriNetObjectHandler.java (+1/-1)
src/pipe/gui/handler/PlaceTransitionObjectHandler.java (+1/-1)
src/pipe/gui/undo/CompundCommand.java (+29/-0)
src/pipe/gui/undo/UndoRedoBuffer.java (+96/-0)
To merge this branch: bzr merge lp:~yrke/tapaal/fix1947517
Reviewer Review Type Date Requested Status
Jiri Srba Approve
Review via email: mp+410811@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jiri Srba (srba) wrote :

Tested and fixes the problem.

review: Approve

Unmerged revisions

1156. By Kenneth Yrke Jørgensen

Fixed bug #1947517, no popup on arcpathpoint

Fixes a bug where rightclicking on an arcpath point would not
display the popup menu.
Also fixes a bug where a NPE would be throw if you right clicked on the
first archpath point.

1155. By Kenneth Yrke Jørgensen

Synced with trunk

1154. By Kenneth Yrke Jørgensen

Made members final

1153. By Kenneth Yrke Jørgensen

Code cleanup

1152. By Kenneth Yrke Jørgensen

Refactored to use gridbaghelper

1151. By Kenneth Yrke Jørgensen

Refactored to use GridBagHelper

1150. By Kenneth Yrke Jørgensen

Code cleanup

1149. By Kenneth Yrke Jørgensen

Removed unsed param from switchToAnimationMode

1148. By Kenneth Yrke Jørgensen

Changed animator Panel to use its own copy of TemplateExplorer
insted of swapping the component between editor and animator

1147. By Kenneth Yrke Jørgensen

Code cleanup

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/dk/aau/cs/gui/TabContent.java'
2--- src/dk/aau/cs/gui/TabContent.java 2021-10-15 14:47:54 +0000
3+++ src/dk/aau/cs/gui/TabContent.java 2021-10-26 13:37:18 +0000
4@@ -838,7 +838,6 @@
5 private AnimationControlSidePanel animControlerBox;
6 private AnimationHistorySidePanel animationHistorySidePanel;
7
8- private JScrollPane animationControllerScrollPane;
9 private AnimationHistoryList abstractAnimationPane = null;
10 private JPanel animationControlsPanel;
11 private TransitionFiringComponent transitionFiring;
12@@ -1119,16 +1118,11 @@
13 safeApp.ifPresent(tab -> tab.updatedTabName(this));
14 }
15
16- /** Creates a new animationHistory text area, and returns a reference to it */
17- private void createAnimationHistory() {
18+ private void createAnimatorSplitPane() {
19+
20 animationHistorySidePanel = new AnimationHistorySidePanel();
21- }
22-
23- private void createAnimatorSplitPane() {
24-
25- createAnimationHistory();
26-
27- if (animControlerBox == null) {
28+
29+ if (animControlerBox == null) {
30 createAnimationControlSidePanel();
31 }
32 if (transitionFiring == null) {
33@@ -1193,9 +1187,6 @@
34 )
35 );
36
37- JButton dummy = new JButton("AnimatorDummy");
38- dummy.setMinimumSize(templateExplorer.getMinimumSize());
39- dummy.setPreferredSize(templateExplorer.getPreferredSize());
40 animatorSplitPane.add(new JPanel(), templateExplorerName);
41
42 animatorSplitPane.add(animationControlsPanel, animControlName);
43@@ -1205,7 +1196,7 @@
44 animatorSplitPane.repaint();
45 }
46
47- public void switchToAnimationComponents(boolean showEnabledTransitions) {
48+ public void switchToAnimationComponents() {
49
50 //Remove dummy
51 Component dummy = animatorSplitPane.getMultiSplitLayout().getComponentForNode(animatorSplitPane.getMultiSplitLayout().getNodeForName(templateExplorerName));
52@@ -1214,17 +1205,9 @@
53 }
54
55 //Add the templateExplorer
56- animatorSplitPane.add(templateExplorer, templateExplorerName);
57-
58- // Inserts dummy to avoid nullpointerexceptions from the displaynode
59- // method. A component can only be on one splitpane at the time
60- dummy = new JButton("EditorDummy");
61- dummy.setMinimumSize(templateExplorer.getMinimumSize());
62- dummy.setPreferredSize(templateExplorer.getPreferredSize());
63- editorSplitPane.add(dummy, templateExplorerName);
64-
65- templateExplorer.switchToAnimationMode();
66- showEnabledTransitionsList(showEnabledTransitions);
67+ var t = new TemplateExplorer(this);
68+ t.switchToAnimationMode();
69+ animatorSplitPane.add(t, templateExplorerName);
70
71 this.setLeftComponent(animatorSplitPaneScroller);
72 }
73@@ -1236,32 +1219,7 @@
74
75 }
76
77- public void switchToEditorComponents() {
78-
79- //Remove dummy
80- Component dummy = editorSplitPane.getMultiSplitLayout().getComponentForNode(editorSplitPane.getMultiSplitLayout().getNodeForName(templateExplorerName));
81- if(dummy != null){
82- editorSplitPane.remove(dummy);
83- }
84-
85- //Add the templateexplorer again
86- editorSplitPane.add(templateExplorer, templateExplorerName);
87- if (animatorSplitPane != null) {
88-
89- // Inserts dummy to avoid nullpointerexceptions from the displaynode
90- // method. A component can only be on one splitpane at the time
91- dummy = new JButton("AnimatorDummy");
92- dummy.setMinimumSize(templateExplorer.getMinimumSize());
93- dummy.setPreferredSize(templateExplorer.getPreferredSize());
94- animatorSplitPane.add(dummy, templateExplorerName);
95- }
96-
97- templateExplorer.switchToEditorMode();
98- this.setLeftComponent(editorSplitPaneScroller);
99- //drawingSurface.repaintAll();
100- }
101-
102- public AnimationHistoryList getUntimedAnimationHistory() {
103+ public AnimationHistoryList getUntimedAnimationHistory() {
104 return abstractAnimationPane;
105 }
106
107@@ -1744,7 +1702,7 @@
108 if (!animationmode) {
109 if (numberOfActiveTemplates() > 0) {
110 app.ifPresent(o->o.setGUIMode(GuiFrame.GUIMode.animation));
111- switchToAnimationComponents(true);
112+ switchToAnimationComponents();
113
114 setManager(animationModeController);
115
116@@ -1793,9 +1751,9 @@
117 getAnimator().restoreModel();
118 }
119
120- switchToEditorComponents();
121+ this.setLeftComponent(editorSplitPaneScroller);
122
123- setManager(notingManager);
124+ setManager(notingManager);
125
126 drawingSurface().setBackground(Pipe.ELEMENT_FILL_COLOUR);
127 setMode(Pipe.ElementType.SELECT);
128@@ -2738,6 +2696,10 @@
129 e-> ((Arc) e.pno).getMouseHandler().getPopup(e.e).show(e.pno, e.e.getX(), e.e.getY())
130 );
131 registerEvent(
132+ e->e.pno instanceof ArcPathPoint && e.a == MouseAction.rightClicked,
133+ e-> ((ArcPathPoint) e.pno).getMouseHandler().getPopup(e.e).show(e.pno, e.e.getX(), e.e.getY())
134+ );
135+ registerEvent(
136 e->e.pno instanceof AnnotationNote && e.a == MouseAction.doubleClicked,
137 e-> ((AnnotationNote) e.pno).enableEditMode()
138 );
139
140=== modified file 'src/dk/aau/cs/gui/TemplateExplorer.java'
141--- src/dk/aau/cs/gui/TemplateExplorer.java 2021-04-08 08:30:29 +0000
142+++ src/dk/aau/cs/gui/TemplateExplorer.java 2021-10-26 13:37:18 +0000
143@@ -39,6 +39,7 @@
144 import dk.aau.cs.gui.undo.MoveElementDownCommand;
145 import dk.aau.cs.gui.undo.MoveElementUpCommand;
146 import net.tapaal.resourcemanager.ResourceManager;
147+import net.tapaal.swinghelpers.GridBagHelper;
148 import net.tapaal.swinghelpers.SwingHelper;
149 import pipe.dataLayer.DataLayer;
150 import pipe.dataLayer.TAPNQuery;
151@@ -66,6 +67,9 @@
152 import dk.aau.cs.util.Tuple;
153 import pipe.gui.widgets.SidePane;
154
155+import static net.tapaal.swinghelpers.GridBagHelper.Anchor.*;
156+import static net.tapaal.swinghelpers.GridBagHelper.Fill.BOTH;
157+
158 public class TemplateExplorer extends JPanel implements SidePane {
159
160 // Template explorer panel items
161@@ -96,8 +100,6 @@
162 private final static String toolTipSortComponents = "Sort the components alphabetically";
163 private final static String toolTipMoveUp = "Move the selected component up";
164 private final static String toolTipMoveDown = "Move the selected component down";
165- //private static final String toolTipComponents ="Here you can manage the different components of the Net.<html><br/></html>" +
166- // "A Net can be broken up in several components and connected via shared places and transitions.";
167
168 public TemplateExplorer(TabContent parent) {
169 this(parent, false);
170@@ -140,29 +142,17 @@
171 addCreatedComponents(hideButtons);
172
173 this.addComponentListener(new ComponentListener() {
174- int minimumHegiht = TemplateExplorer.this.getMinimumSize().height + sortButton.getMinimumSize().height;
175- public void componentShown(ComponentEvent e) {
176- }
177-
178-
179+ final int minimumHegiht = TemplateExplorer.this.getMinimumSize().height + sortButton.getMinimumSize().height;
180+
181 public void componentResized(ComponentEvent e) {
182-
183 if(!isInAnimationMode){
184- if(TemplateExplorer.this.getSize().height <= minimumHegiht){
185- sortButton.setVisible(false);
186- } else {
187- sortButton.setVisible(true);
188- }
189+ sortButton.setVisible(TemplateExplorer.this.getSize().height > minimumHegiht);
190 }
191 }
192-
193-
194- public void componentMoved(ComponentEvent e) {
195- }
196-
197-
198- public void componentHidden(ComponentEvent e) {
199- }
200+
201+ public void componentShown(ComponentEvent e) {}
202+ public void componentMoved(ComponentEvent e) {}
203+ public void componentHidden(ComponentEvent e) {}
204 });
205 }
206
207@@ -195,11 +185,7 @@
208
209 listModel.addListDataListener(new ListDataListener() {
210 public void contentsChanged(ListDataEvent arg0) {
211- if (parent.numberOfActiveTemplates() > 1) {
212- removeTemplateButton.setEnabled(false);
213- } else {
214- removeTemplateButton.setEnabled(true);
215- }
216+ removeTemplateButton.setEnabled(parent.numberOfActiveTemplates() <= 1);
217 }
218
219 public void intervalAdded(ListDataEvent arg0) {
220@@ -218,74 +204,59 @@
221
222 templateList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
223 templateList.setSelectedIndex(0);
224- templateList.setCellRenderer(new TemplateListCellRenderer(templateList.getCellRenderer()));
225+ templateList.setCellRenderer(new TemplateListCellRenderer<>(templateList.getCellRenderer()));
226
227 TemplateListManager manager = new TemplateListManager(templateList);
228 templateList.addListSelectionListener(manager);
229 templateList.addMouseListener(manager);
230-
231- //templateList.setFocusTraversalKeysEnabled(false);
232
233 scrollpane = new JScrollPane(templateList);
234 //Add 10 pixel to the minimumsize of the scrollpane
235 scrollpane.setMinimumSize(new Dimension(scrollpane.getMinimumSize().width, scrollpane.getMinimumSize().height + 20));
236
237- GridBagConstraints gbc = new GridBagConstraints();
238- gbc.gridx = 0;
239- gbc.gridy = 0;
240+ var gbc = GridBagHelper.as(0, 0, NORTHWEST, BOTH);
241 gbc.gridheight = 3;
242 gbc.weightx = 1;
243 gbc.weighty = 1;
244- gbc.fill = GridBagConstraints.BOTH;
245- gbc.anchor = GridBagConstraints.NORTHWEST;
246+
247 templatePanel.add(scrollpane, gbc);
248
249 moveUpButton = new JButton(ResourceManager.getIcon("Up.png"));
250 moveUpButton.setMargin(new Insets(2,2,2,2));
251 moveUpButton.setEnabled(false);
252 moveUpButton.setToolTipText(toolTipMoveUp);
253- moveUpButton.addActionListener(new ActionListener() {
254- public void actionPerformed(ActionEvent e) {
255- int index = templateList.getSelectedIndex();
256-
257- if(index > 0) {
258- Command c = new MoveElementUpCommand(TemplateExplorer.this, index, index-1);
259- undoManager.addNewEdit(c);
260- c.redo();
261- templateList.ensureIndexIsVisible(index+1);
262- templateList.setSelectedIndex(index-1);
263- }
264- }
265- });
266+ moveUpButton.addActionListener(e -> {
267+ int index = templateList.getSelectedIndex();
268+
269+ if(index > 0) {
270+ Command c = new MoveElementUpCommand(TemplateExplorer.this, index, index-1);
271+ undoManager.addNewEdit(c);
272+ c.redo();
273+ templateList.ensureIndexIsVisible(index+1);
274+ templateList.setSelectedIndex(index-1);
275+ }
276+ });
277
278- gbc = new GridBagConstraints();
279- gbc.gridx = 1;
280- gbc.gridy = 0;
281- gbc.anchor = GridBagConstraints.SOUTH;
282+ gbc = GridBagHelper.as(1, 0, SOUTH);
283 templatePanel.add(moveUpButton,gbc);
284
285 moveDownButton = new JButton(ResourceManager.getIcon("Down.png"));
286 moveDownButton.setMargin(new Insets(2,2,2,2));
287 moveDownButton.setEnabled(false);
288 moveDownButton.setToolTipText(toolTipMoveDown);
289- moveDownButton.addActionListener(new ActionListener() {
290- public void actionPerformed(ActionEvent e) {
291- int index = templateList.getSelectedIndex();
292-
293- if(index < parent.network().allTemplates().size() - 1) {
294- Command c = new MoveElementDownCommand(TemplateExplorer.this, index, index+1);
295- undoManager.addNewEdit(c);
296- c.redo();
297- templateList.ensureIndexIsVisible(index+1);
298- templateList.setSelectedIndex(index+1);
299- }
300- }
301- });
302+ moveDownButton.addActionListener(e -> {
303+ int index = templateList.getSelectedIndex();
304+
305+ if(index < parent.network().allTemplates().size() - 1) {
306+ Command c = new MoveElementDownCommand(TemplateExplorer.this, index, index+1);
307+ undoManager.addNewEdit(c);
308+ c.redo();
309+ templateList.ensureIndexIsVisible(index+1);
310+ templateList.setSelectedIndex(index+1);
311+ }
312+ });
313
314- gbc = new GridBagConstraints();
315- gbc.gridx = 1;
316- gbc.gridy = 1;
317- gbc.anchor = GridBagConstraints.NORTH;
318+ gbc = GridBagHelper.as(1, 1, NORTH);
319 templatePanel.add(moveDownButton,gbc);
320
321 //Sort button
322@@ -299,11 +270,7 @@
323 command.redo();
324 });
325
326- gbc = new GridBagConstraints();
327- gbc.gridx = 1;
328- gbc.gridy = 2;
329- gbc.fill = GridBagConstraints.HORIZONTAL;
330- gbc.anchor = GridBagConstraints.NORTH;
331+ gbc = GridBagHelper.as(1,2, NORTH, GridBagHelper.Fill.HORIZONTAL);
332 templatePanel.add(sortButton,gbc);
333 }
334
335@@ -402,10 +369,7 @@
336 }
337 });
338
339- gbc = new GridBagConstraints();
340- gbc.gridx = 0;
341- gbc.gridy = 1;
342- gbc.anchor = GridBagConstraints.WEST;
343+ gbc = GridBagHelper.as(0, 1, WEST);
344 buttonPanel.add(removeTemplateButton, gbc);
345
346 renameButton = new JButton("Rename");
347@@ -413,17 +377,12 @@
348 renameButton.setPreferredSize(dimension);
349 renameButton.setToolTipText(toolTipRenameComponent);
350
351- renameButton.addActionListener(new ActionListener() {
352- public void actionPerformed(ActionEvent arg0) {
353- showRenameTemplateDialog("");
354- templateList.validate();
355- }
356- });
357+ renameButton.addActionListener(arg0 -> {
358+ showRenameTemplateDialog("");
359+ templateList.validate();
360+ });
361
362- gbc = new GridBagConstraints();
363- gbc.gridx = 1;
364- gbc.gridy = 0;
365- gbc.anchor = GridBagConstraints.WEST;
366+ gbc = GridBagHelper.as(1, 0, WEST);
367 buttonPanel.add(renameButton, gbc);
368
369 copyButton = new JButton("Copy");
370@@ -459,18 +418,14 @@
371 }
372 });
373
374- gbc = new GridBagConstraints();
375- gbc.gridx = 0;
376- gbc.gridy = 0;
377- gbc.anchor = GridBagConstraints.WEST;
378+ gbc = GridBagHelper.as(0, 0, WEST);
379 buttonPanel.add(copyButton, gbc);
380 }
381
382 private EscapableDialog dialog;
383 private JPanel container;
384 private JTextField nameTextField;
385- private Dimension size;
386- private JLabel nameLabel;
387+ private JLabel nameLabel;
388 private JPanel buttonContainer;
389 private JButton okButton;
390 private JButton cancelButton;
391@@ -550,9 +505,8 @@
392 container.setLayout(new GridBagLayout());
393 nameContainer = new JPanel();
394 nameContainer.setLayout(new GridBagLayout());
395- size = new Dimension(330, 25);
396
397- nameTextField = new javax.swing.JTextField();
398+ nameTextField = new javax.swing.JTextField();
399 SwingHelper.setPreferredWidth(nameTextField,330);
400 nameTextField.setText(nameToShow);
401 nameTextField.addAncestorListener(new RequestFocusListener());
402@@ -561,23 +515,12 @@
403 okButton.doClick();
404 });
405
406- GridBagConstraints gbc = new GridBagConstraints();
407- gbc.gridx = 0;
408- gbc.gridy = 1;
409- gbc.gridwidth = 1;
410- gbc.anchor = GridBagConstraints.WEST;
411- //gbc.fill = GridBagConstraints.HORIZONTAL;
412- gbc.insets = new Insets(4, 4, 2, 4);
413+ var gbc = GridBagHelper.as(0, 1, 1, WEST, new Insets(4, 4, 2, 4));
414 nameContainer.add(nameTextField,gbc);
415
416 nameLabel = new JLabel();
417 nameLabel.setText("Name of component: ");
418- gbc = new GridBagConstraints();
419- gbc.gridx = 0;
420- gbc.gridy = 0;
421- gbc.gridwidth = 1;
422- gbc.insets = new Insets(4, 4, 2, 4);
423- gbc.anchor = GridBagConstraints.WEST;
424+ gbc = GridBagHelper.as(0, 0, 1, WEST, new Insets(4, 4, 2, 4));
425 nameContainer.add(nameLabel,gbc);
426
427 buttonContainer = new JPanel();
428@@ -589,11 +532,7 @@
429 okButton.setMinimumSize(new java.awt.Dimension(100, 25));
430 okButton.setPreferredSize(new java.awt.Dimension(100, 25));
431 okButton.setMnemonic(KeyEvent.VK_O);
432- gbc = new GridBagConstraints();
433- gbc.gridx = 1;
434- gbc.gridy = 0;
435- gbc.anchor = java.awt.GridBagConstraints.WEST;
436- gbc.insets = new java.awt.Insets(5, 5, 5, 5);
437+ gbc = GridBagHelper.as(1,0, WEST, new Insets(5, 5, 5, 5));
438 buttonContainer.add(okButton,gbc);
439
440 cancelButton = new JButton();
441@@ -602,39 +541,17 @@
442 cancelButton.setMinimumSize(new java.awt.Dimension(100, 25));
443 cancelButton.setPreferredSize(new java.awt.Dimension(100, 25));
444 cancelButton.setMnemonic(KeyEvent.VK_C);
445- gbc = new GridBagConstraints();
446- gbc.gridx = 0;
447- gbc.gridy = 0;
448- gbc.gridwidth = java.awt.GridBagConstraints.RELATIVE;
449- gbc.anchor = GridBagConstraints.EAST;
450+ gbc = GridBagHelper.as(0, 0, GridBagConstraints.RELATIVE, EAST);
451 buttonContainer.add(cancelButton,gbc);
452
453- okButton.addActionListener(new ActionListener() {
454- public void actionPerformed(ActionEvent e) {
455- onOK();
456- }
457- });
458-
459- cancelButton.addActionListener(new ActionListener() {
460- public void actionPerformed(ActionEvent e) {
461- exit();
462- }
463- });
464-
465- gbc = new GridBagConstraints();
466- gbc.insets = new Insets(0, 8, 5, 8);
467- gbc.gridx = 0;
468- gbc.gridy = 1;
469- gbc.gridwidth = 1;
470- gbc.anchor = GridBagConstraints.EAST;
471+ okButton.addActionListener(e -> onOK());
472+
473+ cancelButton.addActionListener(e -> exit());
474+
475+ gbc = GridBagHelper.as(0,1,1,EAST, new Insets(0, 8, 5, 8));
476 container.add(buttonContainer,gbc);
477
478- gbc = new GridBagConstraints();
479- gbc.insets = new Insets(0, 8, 5, 8);
480- gbc.gridx = 0;
481- gbc.gridy = 0;
482- gbc.gridwidth = 1;
483- gbc.anchor = GridBagConstraints.WEST;
484+ gbc = GridBagHelper.as(0, 0, 1, WEST, new Insets(0, 8, 5, 8));
485 container.add(nameContainer,gbc);
486 }
487
488@@ -656,8 +573,7 @@
489 }
490
491 private void initComponentsOfRenameTemplateDialog(String oldname) {
492- container = new JPanel();
493- container.setLayout(new GridBagLayout());
494+
495 nameContainer = new JPanel();
496 nameContainer.setLayout(new GridBagLayout());
497
498@@ -669,23 +585,13 @@
499 okButton.requestFocusInWindow();
500 okButton.doClick();
501 });
502- GridBagConstraints gbc = new GridBagConstraints();
503- gbc.gridx = 0;
504- gbc.gridy = 1;
505- gbc.gridwidth = 1;
506- gbc.anchor = GridBagConstraints.WEST;
507- //gbc.fill = GridBagConstraints.HORIZONTAL;
508- gbc.insets = new Insets(4, 4, 2, 4);
509+ var gbc = GridBagHelper.as(0,1, 1, WEST, new Insets(4, 4, 2, 4));
510 nameContainer.add(nameTextField,gbc);
511
512 nameLabel = new JLabel();
513 nameLabel.setText("Name of component: ");
514- gbc = new GridBagConstraints();
515- gbc.gridx = 0;
516- gbc.gridy = 0;
517- gbc.gridwidth = 1;
518- gbc.insets = new Insets(4, 4, 2, 4);
519- gbc.anchor = GridBagConstraints.WEST;
520+
521+ gbc = GridBagHelper.as(0,0,1,WEST,new Insets(4, 4, 2, 4));
522 nameContainer.add(nameLabel,gbc);
523
524 buttonContainer = new JPanel();
525@@ -697,11 +603,8 @@
526 okButton.setMinimumSize(new java.awt.Dimension(100, 25));
527 okButton.setPreferredSize(new java.awt.Dimension(100, 25));
528 okButton.setMnemonic(KeyEvent.VK_O);
529- gbc = new GridBagConstraints();
530- gbc.gridx = 1;
531- gbc.gridy = 0;
532- gbc.anchor = java.awt.GridBagConstraints.WEST;
533- gbc.insets = new java.awt.Insets(5, 5, 5, 5);
534+
535+ gbc = GridBagHelper.as(1,0, WEST, new Insets(5, 5, 5, 5));
536 buttonContainer.add(okButton,gbc);
537
538 cancelButton = new JButton();
539@@ -710,31 +613,21 @@
540 cancelButton.setMinimumSize(new java.awt.Dimension(100, 25));
541 cancelButton.setPreferredSize(new java.awt.Dimension(100, 25));
542 cancelButton.setMnemonic(KeyEvent.VK_C);
543- gbc = new GridBagConstraints();
544- gbc.gridx = 0;
545- gbc.gridy = 0;
546- gbc.gridwidth = java.awt.GridBagConstraints.RELATIVE;
547- gbc.anchor = GridBagConstraints.EAST;
548+
549+ gbc = GridBagHelper.as(0,0, GridBagConstraints.RELATIVE, EAST);
550 buttonContainer.add(cancelButton,gbc);
551
552 okButton.addActionListener(e -> onOKRenameTemplate());
553
554 cancelButton.addActionListener(e -> exit());
555+
556+ container = new JPanel();
557+ container.setLayout(new GridBagLayout());
558
559- gbc = new GridBagConstraints();
560- gbc.insets = new Insets(0, 8, 5, 8);
561- gbc.gridx = 0;
562- gbc.gridy = 1;
563- gbc.gridwidth = 1;
564- gbc.anchor = GridBagConstraints.EAST;
565+ gbc = GridBagHelper.as(0,1,1,EAST,new Insets(0, 8, 5, 8));
566 container.add(buttonContainer,gbc);
567
568- gbc = new GridBagConstraints();
569- gbc.insets = new Insets(0, 8, 5, 8);
570- gbc.gridx = 0;
571- gbc.gridy = 0;
572- gbc.gridwidth = 1;
573- gbc.anchor = GridBagConstraints.WEST;
574+ gbc = GridBagHelper.as(0,0,1,WEST,new Insets(0, 8, 5, 8));
575 container.add(nameContainer,gbc);
576
577 }
578@@ -777,7 +670,6 @@
579 if (tapn != null) {
580 parent.changeToTemplate(tapn);
581 }
582- //parent.drawingSurface().repaintAll();
583 }
584
585 public Template selectedModel() {
586@@ -809,7 +701,7 @@
587 }
588
589 public void selectFirst() {
590- templateList.setSelectedIndex(0);
591+ templateList.setSelectedIndex(0);
592 }
593
594 public void hideButtons() {
595@@ -853,46 +745,45 @@
596 return templateList;
597 }
598
599- private class TemplateListCellRenderer extends JPanel implements ListCellRenderer {
600+ private class TemplateListCellRenderer<T> extends JPanel implements ListCellRenderer<T> {
601
602 private static final String UNCHECK_TO_DEACTIVATE = "Uncheck to deactive the component";
603 private static final String CHECK_TO_ACTIVATE = "Check to active the component";
604 private final JCheckBox activeCheckbox = new JCheckBox();
605- private final ListCellRenderer cellRenderer;
606-
607-
608- public TemplateListCellRenderer(ListCellRenderer renderer) {
609+ private final ListCellRenderer<T> cellRenderer;
610+
611+
612+ public TemplateListCellRenderer(ListCellRenderer<T> renderer) {
613 cellRenderer = renderer;
614 setLayout(new BorderLayout());
615 setOpaque(false);
616 activeCheckbox.setOpaque(false);
617 }
618-
619- public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
620- Component renderer = cellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
621- removeAll();
622- if(!isInAnimationMode) {
623- boolean isActive = ((Template)value).isActive();
624- activeCheckbox.setSelected(isActive);
625- setToolTipText((isActive)? UNCHECK_TO_DEACTIVATE : CHECK_TO_ACTIVATE);
626- add(activeCheckbox, BorderLayout.WEST);
627- } else {
628-
629- setToolTipText(null);
630- }
631- add(renderer, BorderLayout.CENTER);
632- return this;
633- }
634-
635-
636- }
637+
638+ @Override
639+ public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
640+ Component renderer = cellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
641+ removeAll();
642+ if(!isInAnimationMode) {
643+ boolean isActive = ((Template)value).isActive();
644+ activeCheckbox.setSelected(isActive);
645+ setToolTipText((isActive)? UNCHECK_TO_DEACTIVATE : CHECK_TO_ACTIVATE);
646+ add(activeCheckbox, BorderLayout.WEST);
647+ } else {
648+
649+ setToolTipText(null);
650+ }
651+ add(renderer, BorderLayout.CENTER);
652+ return this;
653+ }
654+ }
655
656 private class TemplateListManager extends MouseAdapter implements ListSelectionListener, ActionListener {
657 private final int checkBoxWidth = new JCheckBox().getPreferredSize().width;
658 private final ListSelectionModel selectionModel;
659- private final JList list;
660+ private final JList<Template> list;
661
662- public TemplateListManager(JList list) {
663+ public TemplateListManager(JList<Template> list) {
664 this.list = list;
665 selectionModel = list.getSelectionModel();
666 this.list.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), JComponent.WHEN_FOCUSED);
667@@ -902,7 +793,7 @@
668 if(index<0)
669 return;
670
671- Template item = ((Template)list.getModel().getElementAt(index));
672+ Template item = list.getModel().getElementAt(index);
673
674
675 if(!selectionModel.isSelectedIndex(index)) {
676@@ -925,11 +816,7 @@
677 if (!selectedModel().isActive()){
678 removeTemplateButton.setEnabled(true);
679 }else {
680- if (parent.numberOfActiveTemplates() <= 1) {
681- removeTemplateButton.setEnabled(false);
682- } else {
683- removeTemplateButton.setEnabled(true);
684- }
685+ removeTemplateButton.setEnabled(parent.numberOfActiveTemplates() > 1);
686 }
687
688 toggleAffectedQueries();
689@@ -960,9 +847,10 @@
690 if(index<0)
691 return;
692
693- if(e.getX()>templateList.getCellBounds(index, index).x+checkBoxWidth)
694- return;
695-
696+ if(e.getX()>templateList.getCellBounds(index, index).x+checkBoxWidth) {
697+ return;
698+ }
699+
700 if (!isInAnimationMode){
701 toggleSelection(index);
702 }
703@@ -984,32 +872,14 @@
704 if (parent.numberOfActiveTemplates() > 1){
705 removeTemplateButton.setEnabled(true);
706 }else{
707- if (selectedModel().isActive()){
708- removeTemplateButton.setEnabled(false);
709- } else {
710- removeTemplateButton.setEnabled(true);
711- }
712+ removeTemplateButton.setEnabled(!selectedModel().isActive());
713 }
714 renameButton.setEnabled(true);
715 copyButton.setEnabled(true);
716- if(templateList.getModel().getSize() >= 2) {
717- sortButton.setEnabled(true);
718- } else {
719- sortButton.setEnabled(false);
720- }
721+ sortButton.setEnabled(templateList.getModel().getSize() >= 2);
722
723- if(index > 0) {
724- moveUpButton.setEnabled(true);
725- } else {
726- moveUpButton.setEnabled(false);
727- }
728-
729-
730- if(index < parent.network().allTemplates().size() - 1) {
731- moveDownButton.setEnabled(true);
732- } else {
733- moveDownButton.setEnabled(false);
734- }
735+ moveUpButton.setEnabled(index > 0);
736+ moveDownButton.setEnabled(index < parent.network().allTemplates().size() - 1);
737 }
738 templateList.ensureIndexIsVisible(index);
739 openSelectedTemplate();
740
741=== added directory 'src/dk/aau/cs/gui/debug'
742=== added file 'src/dk/aau/cs/gui/debug/Debug.kt'
743--- src/dk/aau/cs/gui/debug/Debug.kt 1970-01-01 00:00:00 +0000
744+++ src/dk/aau/cs/gui/debug/Debug.kt 2021-10-26 13:37:18 +0000
745@@ -0,0 +1,38 @@
746+package dk.aau.cs.gui.debug
747+
748+import dk.aau.cs.gui.TabContentActions
749+import pipe.gui.GuiFrame
750+import pipe.gui.action.GuiAction
751+import java.awt.event.ActionEvent
752+import java.util.function.Consumer
753+import javax.swing.AbstractAction
754+import javax.swing.Action
755+import javax.swing.JMenu
756+import javax.swing.KeyStroke
757+
758+fun noOp() {}
759+object DEBUG {
760+
761+ @JvmStatic fun buildMenuDEBUG(): JMenu? {
762+
763+ val debugMenu = JMenu("DEBUG")
764+
765+ with(debugMenu) {
766+
767+ add(object : AbstractAction("Break Point") {
768+ override fun actionPerformed(e: ActionEvent) = noOp()
769+ })
770+
771+ add(object : AbstractAction("Throw Exception") {
772+ override fun actionPerformed(e: ActionEvent) = throw RuntimeException("Casted Exception from DEBUG")
773+ })
774+
775+ add(object : AbstractAction("Show undo/redo stack") {
776+ override fun actionPerformed(e: ActionEvent) = UndoRedoSpy().show()
777+ })
778+ }
779+
780+ return debugMenu
781+ }
782+
783+}
784\ No newline at end of file
785
786=== added file 'src/dk/aau/cs/gui/debug/UndoRedoSpy.kt'
787--- src/dk/aau/cs/gui/debug/UndoRedoSpy.kt 1970-01-01 00:00:00 +0000
788+++ src/dk/aau/cs/gui/debug/UndoRedoSpy.kt 2021-10-26 13:37:18 +0000
789@@ -0,0 +1,53 @@
790+package dk.aau.cs.gui.debug
791+
792+import dk.aau.cs.gui.undo.Command
793+import pipe.gui.CreateGui
794+import pipe.gui.undo.UndoManager
795+import java.awt.event.ActionEvent
796+import java.util.ArrayList
797+import javax.swing.*
798+import javax.swing.tree.DefaultMutableTreeNode
799+import javax.swing.tree.DefaultTreeModel
800+
801+class UndoRedoSpy() : JFrame() {
802+
803+ private val reloadBtn = JButton(object : AbstractAction("Reload") {
804+ override fun actionPerformed(e: ActionEvent?) {
805+ reloadUndoRedoStack()
806+ }
807+ })
808+
809+ private val treeRoot = DefaultMutableTreeNode("root")
810+ private val tree = JTree(treeRoot)
811+
812+ private fun reloadUndoRedoStack() {
813+ val m = tree.model as DefaultTreeModel
814+ treeRoot.removeAllChildren()
815+
816+ val edits = UndoManager::class.java.getDeclaredField("edits")
817+ edits.isAccessible = true
818+ val e = edits.get(CreateGui.getCurrentTab().undoManager) as ArrayList<ArrayList<Command>?>
819+
820+ e.forEach {
821+ if (it != null && it.size > 0) {
822+ treeRoot.add(DefaultMutableTreeNode(it))
823+ }
824+ }
825+ m.reload()
826+ tree.expandRow(0)
827+ }
828+
829+
830+ init {
831+ contentPane.layout = BoxLayout(contentPane, BoxLayout.Y_AXIS)
832+ contentPane.add(reloadBtn)
833+
834+ contentPane.add(JScrollPane(tree))
835+
836+ reloadUndoRedoStack()
837+ pack()
838+
839+ }
840+
841+
842+}
843\ No newline at end of file
844
845=== modified file 'src/net/tapaal/TAPAAL.java'
846--- src/net/tapaal/TAPAAL.java 2020-10-15 15:44:06 +0000
847+++ src/net/tapaal/TAPAAL.java 2021-10-26 13:37:18 +0000
848@@ -40,6 +40,7 @@
849
850 public static final String TOOLNAME = "TAPAAL";
851 public static final String VERSION = "DEV";
852+ public static final boolean IS_DEV = "DEV".equals(VERSION);
853
854 public static String getProgramName(){
855 return "" + TAPAAL.TOOLNAME + " " + TAPAAL.VERSION;
856@@ -69,7 +70,7 @@
857 Logger.enableLogging(true);
858 }
859
860- if (TAPAAL.VERSION.equals("DEV")){
861+ if (IS_DEV){
862 Logger.enableLogging(true);
863 Logger.log("Debug logging is enabled by default in DEV branch");
864 }
865
866=== modified file 'src/net/tapaal/swinghelpers/GridBagHelper.java'
867--- src/net/tapaal/swinghelpers/GridBagHelper.java 2020-07-23 09:18:11 +0000
868+++ src/net/tapaal/swinghelpers/GridBagHelper.java 2021-10-26 13:37:18 +0000
869@@ -4,14 +4,12 @@
870
871 public class GridBagHelper {
872
873-
874-
875-
876 public enum Anchor {
877 NORTH(11),
878 EAST(13),
879 WEST(17),
880- NORTHWEST(18);
881+ NORTHWEST(18),
882+ SOUTH(15);
883
884 public final int value;
885
886@@ -21,7 +19,8 @@
887 }
888
889 public enum Fill {
890- HORIZONTAL(2);
891+ HORIZONTAL(2),
892+ BOTH(1);
893
894 public final int value;
895
896@@ -50,6 +49,10 @@
897
898 return gridBagConstraints;
899 }
900+
901+ public static GridBagConstraints as(int gridx, int gridy, Anchor anchor, Fill fill) {
902+ return as(gridx, gridy, anchor, fill, null);
903+ }
904 public static GridBagConstraints as(int gridx, int gridy, Anchor anchor, Insets inset) {
905 return as(gridx, gridy, anchor, null, inset);
906 }
907@@ -59,7 +62,7 @@
908 }
909
910 public static GridBagConstraints as(int gridx, int gridy, Anchor anchor) {
911- return as(gridx, gridy, anchor, null);
912+ return as(gridx, gridy, anchor, (Fill)null);
913 }
914
915 public static GridBagConstraints as(int gridx, int gridy, Insets inset) {
916@@ -69,6 +72,9 @@
917 public static GridBagConstraints as(int gridx, int gridy) {
918 return as(gridx, gridy, null, null, null);
919 }
920+ public static GridBagConstraints as(int gridx, int gridy, int gridwidth, Anchor anchor) {
921+ return as(gridx, gridy, gridwidth, 1, anchor, null);
922+ }
923 public static GridBagConstraints as(int gridx, int gridy, int gridwidth, Anchor anchor, Insets inset) {
924 return as(gridx, gridy, gridwidth, 1, anchor, inset);
925 }
926
927=== modified file 'src/pipe/gui/GuiFrame.java'
928--- src/pipe/gui/GuiFrame.java 2021-10-03 13:47:00 +0000
929+++ src/pipe/gui/GuiFrame.java 2021-10-26 13:37:18 +0000
930@@ -18,6 +18,7 @@
931
932 import com.sun.jna.Platform;
933 import dk.aau.cs.gui.*;
934+import dk.aau.cs.gui.debug.DEBUG;
935 import dk.aau.cs.util.JavaUtil;
936 import dk.aau.cs.verification.VerifyTAPN.VerifyPN;
937 import net.tapaal.Preferences;
938@@ -58,8 +59,8 @@
939 JMenu viewMenu;
940 private JToolBar drawingToolBar;
941 private final JLabel featureInfoText = new JLabel();
942- private JComboBox<String> timeFeatureOptions = new JComboBox(new String[]{"No", "Yes"});
943- private JComboBox<String> gameFeatureOptions = new JComboBox(new String[]{"No", "Yes"});
944+ private final JComboBox<String> timeFeatureOptions = new JComboBox(new String[]{"No", "Yes"});
945+ private final JComboBox<String> gameFeatureOptions = new JComboBox(new String[]{"No", "Yes"});
946 private JComboBox<String> zoomComboBox;
947
948 private static final int shortcutkey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
949@@ -405,7 +406,7 @@
950 };
951
952
953- private GuiAction prevcomponentAction = new GuiAction("Previous component", "Previous component", "pressed UP") {
954+ private final GuiAction prevcomponentAction = new GuiAction("Previous component", "Previous component", "pressed UP") {
955 public void actionPerformed(ActionEvent e) {
956 currentTab.ifPresent(TabContentActions::previousComponent);
957 }
958@@ -416,14 +417,14 @@
959 }
960 };
961
962- private GuiAction changeTimeFeatureAction = new GuiAction("Time", "Change time semantics") {
963+ private final GuiAction changeTimeFeatureAction = new GuiAction("Time", "Change time semantics") {
964 public void actionPerformed(ActionEvent e) {
965 boolean isTime = timeFeatureOptions.getSelectedIndex() != 0;
966 currentTab.ifPresent(o -> o.changeTimeFeature(isTime));
967 }
968 };
969
970- private GuiAction changeGameFeatureAction = new GuiAction("Game", "Change game semantics") {
971+ private final GuiAction changeGameFeatureAction = new GuiAction("Game", "Change game semantics") {
972 public void actionPerformed(ActionEvent e) {
973 boolean isGame = gameFeatureOptions.getSelectedIndex() != 0;
974 currentTab.ifPresent(o -> o.changeGameFeature(isGame));
975@@ -587,12 +588,19 @@
976
977 menuBar.add(buildMenuAnimation());
978 menuBar.add(buildMenuTools());
979+
980+ if (TAPAAL.IS_DEV) {
981+ menuBar.add(DEBUG.buildMenuDEBUG());
982+ }
983+
984 menuBar.add(buildMenuHelp());
985
986 setJMenuBar(menuBar);
987
988 }
989
990+
991+
992 private JMenu buildMenuEdit() {
993
994 /* Edit Menu */
995
996=== modified file 'src/pipe/gui/handler/ArcHandler.java'
997--- src/pipe/gui/handler/ArcHandler.java 2020-07-20 07:19:51 +0000
998+++ src/pipe/gui/handler/ArcHandler.java 2021-10-26 13:37:18 +0000
999@@ -33,7 +33,7 @@
1000 popup.insert(new JPopupMenu.Separator(), popupIndex);
1001
1002
1003- if ("DEV".equals(TAPAAL.VERSION)){
1004+ if (TAPAAL.IS_DEV){
1005 JTextArea pane = new JTextArea();
1006 pane.setEditable(false);
1007
1008
1009=== modified file 'src/pipe/gui/handler/ArcPathPointHandler.java'
1010--- src/pipe/gui/handler/ArcPathPointHandler.java 2020-10-09 09:58:09 +0000
1011+++ src/pipe/gui/handler/ArcPathPointHandler.java 2021-10-26 13:37:18 +0000
1012@@ -37,7 +37,7 @@
1013 popup.insert(new JPopupMenu.Separator(), 0);
1014
1015 if (((ArcPathPoint) myObject).getIndex() == 0) {
1016- return null;
1017+ return popup;
1018 } else {
1019 JMenuItem menuItem = new JMenuItem(new ToggleArcPointAction((ArcPathPoint) myObject));
1020
1021
1022=== modified file 'src/pipe/gui/handler/PetriNetObjectHandler.java'
1023--- src/pipe/gui/handler/PetriNetObjectHandler.java 2020-08-11 06:00:12 +0000
1024+++ src/pipe/gui/handler/PetriNetObjectHandler.java 2021-10-26 13:37:18 +0000
1025@@ -47,7 +47,7 @@
1026 menuItem.setText("Delete");
1027 popup.add(menuItem);
1028
1029- if ("DEV".equals(TAPAAL.VERSION)){
1030+ if (TAPAAL.IS_DEV){
1031 JTextArea pane = new JTextArea();
1032 pane.setEditable(false);
1033
1034
1035=== modified file 'src/pipe/gui/handler/PlaceTransitionObjectHandler.java'
1036--- src/pipe/gui/handler/PlaceTransitionObjectHandler.java 2020-06-29 10:58:39 +0000
1037+++ src/pipe/gui/handler/PlaceTransitionObjectHandler.java 2021-10-26 13:37:18 +0000
1038@@ -28,7 +28,7 @@
1039 @Override
1040 public JPopupMenu getPopup(MouseEvent e) {
1041 JPopupMenu popup = super.getPopup(e);
1042- if ("DEV".equals(TAPAAL.VERSION)){
1043+ if (TAPAAL.IS_DEV){
1044 JTextArea pane = new JTextArea();
1045 pane.setEditable(false);
1046
1047
1048=== added file 'src/pipe/gui/undo/CompundCommand.java'
1049--- src/pipe/gui/undo/CompundCommand.java 1970-01-01 00:00:00 +0000
1050+++ src/pipe/gui/undo/CompundCommand.java 2021-10-26 13:37:18 +0000
1051@@ -0,0 +1,29 @@
1052+package pipe.gui.undo;
1053+
1054+import dk.aau.cs.gui.undo.Command;
1055+
1056+import java.util.ArrayList;
1057+import java.util.List;
1058+
1059+public class CompundCommand extends Command {
1060+ private final List<Command> commands;
1061+
1062+ CompundCommand(List<Command> commands) {
1063+ this.commands = new ArrayList(commands);
1064+ }
1065+
1066+ @Override
1067+ public void undo() {
1068+ // Undo in reverse order (order matters)
1069+ for (int i = commands.size() - 1; i >= 0; i--) {
1070+ commands.get(i).undo();
1071+ }
1072+ }
1073+
1074+ @Override
1075+ public void redo() {
1076+ for (Command command : commands) {
1077+ command.redo();
1078+ }
1079+ }
1080+}
1081
1082=== added file 'src/pipe/gui/undo/UndoRedoBuffer.java'
1083--- src/pipe/gui/undo/UndoRedoBuffer.java 1970-01-01 00:00:00 +0000
1084+++ src/pipe/gui/undo/UndoRedoBuffer.java 2021-10-26 13:37:18 +0000
1085@@ -0,0 +1,96 @@
1086+package pipe.gui.undo;
1087+
1088+//WIP: Implemented 2021-10 to takeover current undo/redo implementation
1089+import java.util.ArrayList;
1090+import java.util.Arrays;
1091+
1092+public class UndoRedoBuffer<T> {
1093+
1094+ public final int CAPACITY;
1095+ private final T[] buffer;
1096+
1097+ private int currentIndex;
1098+
1099+ private int elements = 0;
1100+ private int elementsOnTop = 0;
1101+
1102+ UndoRedoBuffer(int capacity) {
1103+ if (capacity <= 0) {
1104+ throw new IllegalArgumentException("Capacity must by larger than zero");
1105+ }
1106+
1107+ this.CAPACITY = capacity;
1108+ this.currentIndex = CAPACITY-1;
1109+
1110+ buffer = (T[])new Object[CAPACITY];
1111+
1112+ }
1113+
1114+ private int nextIndex() {
1115+ return (CAPACITY + currentIndex + 1) % CAPACITY;
1116+ }
1117+ private int previousIndex() {
1118+ return (CAPACITY + currentIndex - 1) % CAPACITY;
1119+ }
1120+
1121+ public void add(T e) {
1122+ elementsOnTop = 0;
1123+ currentIndex = nextIndex();
1124+ elements = Math.min(elements+1, CAPACITY);
1125+ buffer[currentIndex] = e;
1126+ }
1127+
1128+ public T rollback() {
1129+ if (elements > 0) {
1130+ var t = top();
1131+
1132+ elementsOnTop += 1;
1133+ elements -= 1;
1134+
1135+ currentIndex = previousIndex();
1136+ return t;
1137+ }
1138+ return null;
1139+ }
1140+
1141+ public T repeat() {
1142+ if (elementsOnTop > 0) {
1143+ elementsOnTop -= 1;
1144+ elements +=1;
1145+ currentIndex = nextIndex();
1146+ return top();
1147+ }
1148+ return null;
1149+ }
1150+
1151+ public T top() {
1152+ if ( elements > 0 ) {
1153+ return buffer[currentIndex];
1154+ } else {
1155+ return null;
1156+ }
1157+ }
1158+
1159+ public boolean canRepeat() {
1160+ return elementsOnTop > 0;
1161+ }
1162+
1163+ public boolean canRollback() {
1164+ return elements > 0;
1165+ }
1166+
1167+ @Override
1168+ public String toString() {
1169+ return "UndoRedoBuffer{" +
1170+ "CAPACITY=" + CAPACITY +
1171+ ", buffer=" + Arrays.toString(buffer) +
1172+ ", currentIndex=" + currentIndex +
1173+ ", elements=" + elements +
1174+ ", elementsOnTop=" + elementsOnTop +
1175+ '}';
1176+ }
1177+
1178+ public void print() {
1179+ System.out.println(this);
1180+ }
1181+}

Subscribers

People subscribed via source and target branches