Merge branch 'develop' into development/Release_2_12_Branch_JAL-4102_21126_merge
[jalview.git] / src / jalview / gui / SlivkaPreferences.java
1 package jalview.gui;
2
3 import jalview.bin.Cache;
4 import jalview.bin.Console;
5 import jalview.util.MessageManager;
6 import jalview.ws.WSDiscovererI;
7 import jalview.ws.slivkaws.SlivkaWSDiscoverer;
8
9 import java.awt.BorderLayout;
10 import java.awt.Color;
11 import java.awt.Component;
12 import java.awt.Dimension;
13 import java.awt.Font;
14 import java.awt.event.ActionEvent;
15 import java.awt.event.ActionListener;
16 import java.awt.event.MouseAdapter;
17 import java.awt.event.MouseEvent;
18 import java.awt.event.MouseListener;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.NoSuchElementException;
25 import java.util.concurrent.CompletableFuture;
26
27 import javax.swing.BorderFactory;
28 import javax.swing.Box;
29 import javax.swing.BoxLayout;
30 import javax.swing.JButton;
31 import javax.swing.JOptionPane;
32 import javax.swing.JPanel;
33 import javax.swing.JProgressBar;
34 import javax.swing.JScrollPane;
35 import javax.swing.JTable;
36 import javax.swing.SwingUtilities;
37 import javax.swing.UIManager;
38 import javax.swing.table.AbstractTableModel;
39 import javax.swing.table.DefaultTableCellRenderer;
40
41 @SuppressWarnings("serial")
42 public class SlivkaPreferences extends JPanel
43 {
44   {
45     setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
46     setPreferredSize(new Dimension(500, 450));
47   }
48
49   WSDiscovererI discoverer;
50
51   private final ArrayList<String> urls = new ArrayList<>();
52
53   private final Map<String, Integer> statuses = new HashMap<>();
54
55   private final AbstractTableModel urlTableModel = new AbstractTableModel()
56   {
57     final String[] columnNames = { "Service URL", "Status" };
58
59     @Override
60     public String getColumnName(int col)
61     {
62       return columnNames[col];
63     }
64
65     @Override
66     public Object getValueAt(int rowIndex, int columnIndex)
67     {
68       switch (columnIndex)
69       {
70       case 0:
71         return urls.get(rowIndex);
72       case 1:
73         return statuses.getOrDefault(urls.get(rowIndex), WSDiscovererI.STATUS_UNKNOWN);
74       default:
75         throw new NoSuchElementException();
76       }
77     }
78
79     @Override
80     public int getRowCount()
81     {
82       return urls.size();
83     }
84
85     @Override
86     public int getColumnCount()
87     {
88       return 2;
89     }
90   };
91
92   private class WSStatusCellRenderer extends DefaultTableCellRenderer
93   {
94     @Override
95     public Component getTableCellRendererComponent(JTable table,
96         Object value, boolean isSelected, boolean hasFocus, int row,
97         int column)
98     {
99       setHorizontalAlignment(CENTER);
100       super.getTableCellRendererComponent(table, "\u25CF", isSelected,
101           hasFocus, row, column);
102       switch ((Integer) value)
103       {
104       case WSDiscovererI.STATUS_NO_SERVICES:
105         setForeground(Color.ORANGE);
106         break;
107       case WSDiscovererI.STATUS_OK:
108         setForeground(Color.GREEN);
109         break;
110       case WSDiscovererI.STATUS_INVALID:
111         setForeground(Color.RED);
112         break;
113       case WSDiscovererI.STATUS_UNKNOWN:
114       default:
115         setForeground(Color.LIGHT_GRAY);
116       }
117       return this;
118     }
119   }
120
121   private JTable urlListTable = new JTable(urlTableModel);
122   {
123     urlListTable.getColumnModel().getColumn(1).setMaxWidth(60);
124     urlListTable.getColumnModel().getColumn(1)
125         .setCellRenderer(new WSStatusCellRenderer());
126   }
127
128   // URL control panel buttons
129   JButton newWsUrl = new JButton(
130       MessageManager.getString("label.new_service_url"));
131
132   JButton editWsUrl = new JButton(
133       MessageManager.getString("label.edit_service_url"));
134
135   JButton deleteWsUrl = new JButton(
136       MessageManager.getString("label.delete_service_url"));
137
138   JButton moveUrlUp = new JButton(
139       MessageManager.getString("action.move_up"));
140
141   JButton moveUrlDown = new JButton(
142       MessageManager.getString("action.move_down"));
143
144   private String showEditUrlDialog(String oldUrl)
145   {
146     String input = (String) JvOptionPane
147         .showInternalInputDialog(
148             this,
149             MessageManager.getString("label.url:"),
150             UIManager.getString("OptionPane.inputDialogTitle", MessageManager.getLocale()),
151             JOptionPane.QUESTION_MESSAGE,
152             null,
153             null,
154             oldUrl);
155     if (input == null)
156     {
157       return null;
158     }
159     try
160     {
161       new URL(input);
162     } catch (MalformedURLException ex)
163     {
164       JvOptionPane.showInternalMessageDialog(this,
165           MessageManager.getString("label.invalid_url"),
166           UIManager.getString("OptionPane.messageDialogTitle",
167               MessageManager.getLocale()),
168           JOptionPane.WARNING_MESSAGE);
169       return null;
170     }
171     return input;
172   }
173
174   // Button Action Listeners
175   private ActionListener newUrlAction = (ActionEvent e) -> {
176     final String input = showEditUrlDialog("");
177     if (input != null)
178     {
179       urls.add(input);
180       reloadStatusForUrl(input);
181       urlTableModel.fireTableRowsInserted(urls.size(), urls.size());
182       discoverer.setServiceUrls(urls);
183     }
184   };
185
186   private ActionListener editUrlAction = (ActionEvent e) -> {
187     final int i = urlListTable.getSelectedRow();
188     if (i >= 0)
189     {
190       final String input = showEditUrlDialog(urls.get(i));
191       if (input != null)
192       {
193         urls.set(i, input);
194         statuses.remove(input);
195         reloadStatusForUrl(input);
196         urlTableModel.fireTableRowsUpdated(i, i);
197         discoverer.setServiceUrls(urls);
198       }
199     }
200   };
201
202   private ActionListener deleteUrlAction = (ActionEvent e) -> {
203     final int i = urlListTable.getSelectedRow();
204     if (i >= 0)
205     {
206       urls.remove(i);
207       statuses.remove(i);
208       urlTableModel.fireTableRowsDeleted(i, i);
209       discoverer.setServiceUrls(urls);
210     }
211   };
212
213   private ActionListener moveUrlUpAction = (ActionEvent e) -> {
214     final int i = urlListTable.getSelectedRow();
215     if (i > 0)
216     {
217       moveTableRow(i, i - 1);
218       discoverer.setServiceUrls(urls);
219     }
220   };
221
222   private ActionListener moveUrlDownAction = (ActionEvent e) -> {
223     final int i = urlListTable.getSelectedRow();
224     if (i >= 0 && i < urls.size() - 1)
225     {
226       moveTableRow(i, i + 1);
227       discoverer.setServiceUrls(urls);
228     }
229   };
230
231   private MouseListener tableClickListener = new MouseAdapter()
232   {
233     final ActionEvent actionEvent = new ActionEvent(urlListTable,
234         ActionEvent.ACTION_PERFORMED, "edit");
235
236     @Override
237     public void mouseClicked(MouseEvent e)
238     {
239       if (e.getClickCount() > 1)
240       {
241         editUrlAction.actionPerformed(actionEvent);
242       }
243     }
244   };
245
246   // Setting up URL list Pane
247   {
248     Font font = new Font("Verdana", Font.PLAIN, 10);
249     JPanel urlPaneContainer = new JPanel(new BorderLayout(5, 5));
250     urlPaneContainer.setBorder(BorderFactory.createCompoundBorder(
251         BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
252             "Slivka Web Services"),
253         BorderFactory.createEmptyBorder(10, 5, 5, 5)));
254
255     newWsUrl.setFont(font);
256     editWsUrl.setFont(font);
257     deleteWsUrl.setFont(font);
258     moveUrlUp.setFont(font);
259     moveUrlDown.setFont(font);
260     JPanel editContainer = new JPanel();
261     editContainer.add(newWsUrl);
262     editContainer.add(editWsUrl);
263     editContainer.add(deleteWsUrl);
264     urlPaneContainer.add(editContainer, BorderLayout.PAGE_END);
265
266     JPanel moveContainer = new JPanel();
267     moveContainer
268         .setLayout(new BoxLayout(moveContainer, BoxLayout.PAGE_AXIS));
269     moveContainer.add(moveUrlUp);
270     moveContainer.add(Box.createRigidArea(new Dimension(0, 5)));
271     moveContainer.add(moveUrlDown);
272     urlPaneContainer.add(moveContainer, BorderLayout.LINE_START);
273
274     urlPaneContainer.add(new JScrollPane(urlListTable),
275         BorderLayout.CENTER);
276     this.add(urlPaneContainer);
277
278     // Connecting action listeners
279     urlListTable.addMouseListener(tableClickListener);
280     newWsUrl.addActionListener(newUrlAction);
281     editWsUrl.addActionListener(editUrlAction);
282     deleteWsUrl.addActionListener(deleteUrlAction);
283     moveUrlUp.addActionListener(moveUrlUpAction);
284     moveUrlDown.addActionListener(moveUrlDownAction);
285   }
286
287   private void moveTableRow(int fromIndex, int toIndex)
288   {
289     String url = urls.get(fromIndex);
290     int status = statuses.get(fromIndex);
291     urls.set(fromIndex, urls.get(toIndex));
292     urls.set(toIndex, url);
293     if (urlListTable.getSelectedRow() == fromIndex)
294     {
295       urlListTable.setRowSelectionInterval(toIndex, toIndex);
296     }
297     int firstRow = Math.min(toIndex, fromIndex);
298     int lastRow = Math.max(fromIndex, toIndex);
299     urlTableModel.fireTableRowsUpdated(firstRow, lastRow);
300   }
301
302   // Discoverer reloading buttons
303   JButton refreshServices = new JButton(
304       MessageManager.getString("action.refresh_services"));
305
306   JButton resetServices = new JButton(
307       MessageManager.getString("action.reset_services"));
308
309   JProgressBar progressBar = new JProgressBar();
310
311   // Discoverer buttons action listeners
312   private ActionListener refreshServicesAction = (ActionEvent e) -> {
313     progressBar.setVisible(true);
314     Console.info("Requesting service reload");
315     discoverer.startDiscoverer().handle((_discoverer, exception) -> {
316       if (exception == null)
317       {
318         Console.info("Reloading done");
319       }
320       else
321       {
322         Console.error("Reloading failed", exception);
323       }
324       SwingUtilities.invokeLater(() -> progressBar.setVisible(false));
325       return null;
326     });
327   };
328
329   private ActionListener resetServicesAction = (ActionEvent e) -> {
330     discoverer.setServiceUrls(null);
331     urls.clear();
332     statuses.clear();
333     urls.addAll(discoverer.getServiceUrls());
334     for (String url : urls)
335     {
336       reloadStatusForUrl(url);
337     }
338     urlTableModel.fireTableDataChanged();
339   };
340
341   {
342     Font font = new Font("Verdana", Font.PLAIN, 11);
343     refreshServices.setFont(font);
344     resetServices.setFont(font);
345     JPanel container = new JPanel();
346     container.add(refreshServices);
347     container.add(resetServices);
348     this.add(container);
349
350     // Connecting action listeners
351     refreshServices.addActionListener(refreshServicesAction);
352     resetServices.addActionListener(resetServicesAction);
353   }
354
355   {
356     progressBar.setVisible(false);
357     progressBar.setIndeterminate(true);
358     add(progressBar);
359   }
360
361   SlivkaPreferences()
362   {
363     // Initial URLs loading
364     discoverer = SlivkaWSDiscoverer.getInstance();
365     urls.addAll(discoverer.getServiceUrls());
366     for (String url : urls)
367     {
368       reloadStatusForUrl(url);
369     }
370   }
371
372   private void reloadStatusForUrl(String url)
373   {
374     CompletableFuture.supplyAsync(() -> discoverer.getServerStatusFor(url))
375         .thenAccept((status) -> {
376           statuses.put(url, status);
377           int row = urls.indexOf(url);
378           if (row >= 0)
379             urlTableModel.fireTableCellUpdated(row, 1);
380         });
381   }
382 }