Use internal SNAPSHOT couplings again
[trakem2.git] / TrakEM2_ / src / main / java / ini / trakem2 / display / TreeConnectorsView.java
blob5e360fbf24837999cac206cbd96f2104eb067db0
1 package ini.trakem2.display;
3 import ini.trakem2.utils.Bureaucrat;
4 import ini.trakem2.utils.IJError;
5 import ini.trakem2.utils.Utils;
6 import ini.trakem2.utils.Worker;
8 import java.awt.Dimension;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.awt.event.MouseAdapter;
12 import java.awt.event.MouseEvent;
13 import java.awt.event.WindowAdapter;
14 import java.awt.event.WindowEvent;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
25 import javax.swing.JFrame;
26 import javax.swing.JMenuItem;
27 import javax.swing.JPopupMenu;
28 import javax.swing.JScrollPane;
29 import javax.swing.JTabbedPane;
30 import javax.swing.JTable;
31 import javax.swing.SwingUtilities;
32 import javax.swing.table.AbstractTableModel;
34 /** List all connectors whose origins intersect with the given tree. */
35 public class TreeConnectorsView {
37 static private Map<Tree<?>,TreeConnectorsView> open = Collections.synchronizedMap(new HashMap<Tree<?>,TreeConnectorsView>());
39 private JFrame frame;
40 private TargetsTableModel outgoing_model = new TargetsTableModel(),
41 incoming_model = new TargetsTableModel();
42 private Tree<?> tree;
44 public TreeConnectorsView(final Tree<?> tree) {
45 this.tree = tree;
46 update();
47 createGUI();
48 open.put(tree,this);
51 static public Bureaucrat create(final Tree<?> tree) {
52 return Bureaucrat.createAndStart(new Worker.Task("Opening connectors table") {
53 public void exec() {
54 TreeConnectorsView tcv = open.get(tree);
55 if (null != tcv) {
56 tcv.update();
57 tcv.frame.setVisible(true);
58 tcv.frame.toFront();
59 } else {
60 // Create and store in the Map of 'open'
61 new TreeConnectorsView(tree);
64 }, tree.getProject());
67 static public void dispose(final Tree<?> tree) {
68 TreeConnectorsView tcv = open.remove(tree);
69 if (null == tcv) return;
70 tcv.frame.dispose();
73 static private final Comparator<Displayable> IDSorter = new Comparator<Displayable>() {
74 @Override
75 public int compare(Displayable o1, Displayable o2) {
76 if (o1.getId() < o1.getId()) return -1;
77 return 1;
81 private class Row {
82 final Connector connector;
83 final int i;
84 final ArrayList<Displayable> origins, targets;
85 String originids, targetids;
86 Row(final Connector c, final int i, final ArrayList<Displayable> origins, final ArrayList<Displayable> targets) {
87 this.connector = c;
88 this.i = i;
89 this.origins = origins;
90 this.targets = targets;
91 for (final Iterator<Displayable> it = this.targets.iterator(); it.hasNext(); ) {
92 if (it.next().getClass() == Connector.class) {
93 it.remove();
97 final Coordinate<Node<Float>> getCoordinate(int col) {
98 switch (col) {
99 case 0:
100 case 1:
101 return connector.getCoordinateAtOrigin();
102 case 2:
103 return connector.getCoordinate(i);
104 default:
105 Utils.log2("Can't deal with column " + col);
106 return null;
109 private final long getFirstId(final ArrayList<Displayable> c) {
110 if (c.isEmpty())
111 return 0;
112 else
113 return c.get(0).getId();
115 final long getColumn(final int col) {
116 switch (col) {
117 case 0:
118 return connector.getId();
119 case 1:
120 return getFirstId(origins);
121 case 2:
122 return getFirstId(targets);
123 default:
124 Utils.log2("Don't know how to deal with column " + col);
125 return 0;
128 private final String getIds(String ids, final ArrayList<Displayable> ds) {
129 if (null == ids) {
130 switch (ds.size()) {
131 case 0:
132 ids = "";
133 break;
134 case 1:
135 ids = ds.get(0).toString();
136 break;
137 default:
138 final StringBuilder sb = new StringBuilder();
139 for (final Displayable d : ds) {
140 sb.append(d).append(',').append(' ');
142 sb.setLength(sb.length() -2);
143 ids = sb.toString();
144 break;
147 return ids;
149 final String getTargetIds() {
150 targetids = getIds(targetids, targets);
151 return targetids;
153 final String getOriginIds() {
154 originids = getIds(originids, origins);
155 return originids;
159 public void update() {
160 // Find all Connector instances intersecting with the nodes of Tree
161 try {
162 final Collection<Connector>[] connectors = this.tree.findConnectors();
163 outgoing_model.setData(connectors[0]);
164 incoming_model.setData(connectors[1]);
165 } catch (Exception e) {
166 IJError.print(e);
170 private void addTab(JTabbedPane tabs, String title, TargetsTableModel model) {
171 JTable table = new Table();
172 table.setModel(model);
173 JScrollPane jsp = new JScrollPane(table);
174 jsp.setPreferredSize(new Dimension(500, 500));
175 tabs.addTab(title, jsp);
178 private void createGUI() {
179 this.frame = new JFrame("Connectors for Tree #" + this.tree.getId());
180 frame.addWindowListener(new WindowAdapter() {
181 public void windowClosing(WindowEvent we) {
182 open.remove(tree);
185 JTabbedPane tabs = new JTabbedPane();
186 addTab(tabs, "Outgoing", outgoing_model);
187 addTab(tabs, "Incoming", incoming_model);
188 frame.getContentPane().add(tabs);
189 frame.pack();
190 frame.setVisible(true);
193 private class Table extends JTable {
194 private static final long serialVersionUID = 1L;
195 Table() {
196 super();
197 getTableHeader().addMouseListener(new MouseAdapter() {
198 public void mouseClicked(MouseEvent me) {
199 if (2 != me.getClickCount()) return;
200 int viewColumn = getColumnModel().getColumnIndexAtX(me.getX());
201 int column = convertColumnIndexToModel(viewColumn);
202 if (-1 == column) return;
203 ((TargetsTableModel)getModel()).sortByColumn(column, me.isShiftDown());
206 addMouseListener(new MouseAdapter() {
207 public void mousePressed(MouseEvent me) {
208 final int row = Table.this.rowAtPoint(me.getPoint());
209 final int col = Table.this.columnAtPoint(me.getPoint());
210 if (2 == me.getClickCount()) {
211 go(col, row);
212 } else if (Utils.isPopupTrigger(me)) {
213 JPopupMenu popup = new JPopupMenu();
214 final JMenuItem go = new JMenuItem("Go"); popup.add(go);
215 final JMenuItem goandsel = new JMenuItem("Go and select"); popup.add(go);
216 final JMenuItem update = new JMenuItem("Update"); popup.add(update);
217 ActionListener listener = new ActionListener() {
218 public void actionPerformed(ActionEvent ae) {
219 final Object src = ae.getSource();
220 if (src == go) go(col, row);
221 else if (src == goandsel) {
222 go(col, row);
223 if (0 != (ae.getModifiers() ^ ActionEvent.SHIFT_MASK)) Display.getFront().getSelection().clear();
224 TargetsTableModel ttm = (TargetsTableModel)getModel();
225 Display.getFront().getSelection().add(ttm.rows.get(row).connector);
226 } else if (src == update) {
227 Bureaucrat.createAndStart(new Worker.Task("Updating...") {
228 public void exec() {
229 TreeConnectorsView.this.update();
231 }, TreeConnectorsView.this.tree.getProject());
235 go.addActionListener(listener);
236 goandsel.addActionListener(listener);
237 update.addActionListener(listener);
238 popup.show(Table.this, me.getX(), me.getY());
243 void go(int col, int row) {
244 TargetsTableModel ttm = (TargetsTableModel)getModel();
245 Display.centerAt(ttm.rows.get(row).getCoordinate(col));
249 private class TargetsTableModel extends AbstractTableModel {
250 private static final long serialVersionUID = 1L;
251 List<Row> rows = null;
253 synchronized public void setData(final Collection<Connector> connectors) {
254 this.rows = new ArrayList<Row>(connectors.size());
255 for (final Connector c : connectors) {
256 int i = 0;
257 final ArrayList<Displayable> origins = new ArrayList<Displayable>(c.getOrigins(VectorData.class, true));
258 Collections.sort(origins, IDSorter);
259 for (final Set<Displayable> targets : c.getTargets(VectorData.class, true)) {
260 final ArrayList<Displayable> ts = new ArrayList<Displayable>(targets);
261 Collections.sort(ts, IDSorter);
262 this.rows.add(new Row(c, i++, origins, ts));
265 SwingUtilities.invokeLater(new Runnable() {public void run() {
266 fireTableDataChanged();
267 fireTableStructureChanged();
268 }});
271 public int getColumnCount() { return 3; }
272 public String getColumnName(final int col) {
273 switch (col) {
274 case 0: return "Connector id";
275 case 1: return "Origin id";
276 case 2: return "Target id";
277 default: return null;
280 public int getRowCount() { return rows.size(); }
281 public Object getValueAt(final int row, final int col) {
282 switch (col) {
283 case 0: return rows.get(row).connector.getId();
284 case 1: return rows.get(row).getOriginIds();
285 case 2: return rows.get(row).getTargetIds();
286 default: return null;
289 public boolean isCellEditable(int row, int col) { return false; }
290 public void setValueAt(Object value, int row, int col) {}
291 final void sortByColumn(final int col, final boolean descending) {
292 final ArrayList<Row> rows = new ArrayList<Row>(this.rows);
293 Collections.sort(rows, new Comparator<Row>() {
294 public int compare(final Row r1, final Row r2) {
295 final long op = r1.getColumn(col) - r2.getColumn(col);
296 if (descending) {
297 if (op > 0) return -1;
298 if (op < 0) return 1;
299 return 0;
301 if (op < 0) return -1;
302 if (op > 0) return 1;
303 return 0;
306 this.rows = rows; // swap
307 fireTableDataChanged();
308 fireTableStructureChanged();