JAL-1075
[jalview.git] / src / jalview / gui / JDatabaseTree.java
1 package jalview.gui;
2
3 import jalview.bin.Cache;
4 import jalview.ws.seqfetcher.DbSourceProxy;
5
6 import java.awt.BorderLayout;
7 import java.awt.Component;
8 import java.awt.Container;
9 import java.awt.Dimension;
10 import java.awt.FlowLayout;
11 import java.awt.GridLayout;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.awt.event.KeyEvent;
15 import java.awt.event.KeyListener;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.Hashtable;
19 import java.util.List;
20 import java.util.Vector;
21
22 import javax.swing.JButton;
23 import javax.swing.JFrame;
24 import javax.swing.JLabel;
25 import javax.swing.JPanel;
26 import javax.swing.JScrollPane;
27 import javax.swing.JTree;
28 import javax.swing.event.TreeSelectionEvent;
29 import javax.swing.event.TreeSelectionListener;
30 import javax.swing.tree.DefaultMutableTreeNode;
31 import javax.swing.tree.DefaultTreeCellRenderer;
32 import javax.swing.tree.DefaultTreeModel;
33 import javax.swing.tree.TreeCellRenderer;
34 import javax.swing.tree.TreePath;
35 import javax.swing.tree.TreeSelectionModel;
36
37 public class JDatabaseTree extends JalviewDialog implements KeyListener
38 {
39   boolean allowMultiSelections = false;
40
41   JButton getDatabaseSelectorButton()
42   {
43     final JButton viewdbs = new JButton("--- Select Database ---");
44     viewdbs.addActionListener(new ActionListener()
45     {
46
47       @Override
48       public void actionPerformed(ActionEvent arg0)
49       {
50         showDialog(null);
51       }
52     });
53     return viewdbs;
54   }
55
56   JScrollPane svp;
57
58   JTree dbviews;
59
60   private jalview.ws.SequenceFetcher sfetcher;
61
62   private JLabel dbstatus, dbstatex;
63
64   public JDatabaseTree(jalview.ws.SequenceFetcher sfetch)
65   {
66     initDialogFrame(this, true, false, "Select Database Retrieval Source",
67             650, 490);
68     /*
69      * Dynamically generated database list will need a translation function from
70      * internal source to externally distinct names. UNIPROT and UP_NAME are
71      * identical DB sources, and should be collapsed.
72      */
73     DefaultMutableTreeNode tn = null, root = new DefaultMutableTreeNode();
74     Hashtable<String, DefaultMutableTreeNode> source = new Hashtable<String, DefaultMutableTreeNode>();
75     sfetcher = sfetch;
76     String dbs[] = sfetch.getSupportedDb();
77     Hashtable<String,String> ht = new Hashtable<String,String>();
78     for (int i = 0; i < dbs.length; i++)
79     {
80       tn = source.get(dbs[i]);
81       List<DbSourceProxy> srcs = sfetch.getSourceProxy(dbs[i]);
82       if (tn == null)
83       {
84         source.put(dbs[i], tn = new DefaultMutableTreeNode(dbs[i], true));
85       }
86       for (DbSourceProxy dbp : srcs)
87       {
88         if (ht.get(dbp.getDbName()) == null)
89         {
90           tn.add(new DefaultMutableTreeNode(dbp, false));
91           ht.put(dbp.getDbName(), dbp.getDbName());
92         }
93         else
94         {
95           System.err.println("dupe ig for : " + dbs[i] + " \t"
96                   + dbp.getDbName() + " (" + dbp.getDbSource() + ")");
97           source.remove(tn);
98         }
99       }
100     }
101     for (int i = 0; i < dbs.length; i++)
102     {
103       tn = source.get(dbs[i]);
104       if (tn == null)
105       {
106         continue;
107       }
108       if (tn.getChildCount() == 1)
109       {
110         DefaultMutableTreeNode ttn = (DefaultMutableTreeNode) tn
111                 .getChildAt(0);
112         // remove nodes with only one child
113         tn.setUserObject(ttn.getUserObject());
114         tn.removeAllChildren();
115         source.put(dbs[i], tn);
116         tn.setAllowsChildren(false);
117       }
118       root.add(tn);
119     }
120     svp = new JScrollPane();
121     // svp.setAutoscrolls(true);
122     dbviews = new JTree(new DefaultTreeModel(root, false));
123     dbviews.setCellRenderer(new DbTreeRenderer(this));
124
125     dbviews.getSelectionModel().setSelectionMode(
126             TreeSelectionModel.SINGLE_TREE_SELECTION);
127     svp.getViewport().setView(dbviews);
128     // svp.getViewport().setMinimumSize(new Dimension(300,200));
129     // svp.setSize(300,250);
130     // JPanel panel=new JPanel();
131     // panel.setSize(new Dimension(350,220));
132     // panel.add(svp);
133     dbviews.addTreeSelectionListener(new TreeSelectionListener()
134     {
135
136       @Override
137       public void valueChanged(TreeSelectionEvent arg0)
138       {
139         _setSelectionState();
140       }
141     });
142     JPanel jc = new JPanel(new BorderLayout()), j = new JPanel(
143             new FlowLayout());
144     jc.add(svp, BorderLayout.CENTER);
145
146     java.awt.Font f;
147     // TODO: make the panel stay a fixed size for longest dbname+example set.
148     JPanel dbstat = new JPanel(new GridLayout(2, 1));
149     dbstatus = new JLabel(" "); // set the height correctly for layout
150     dbstatus.setFont(f = JvSwingUtils.getLabelFont(false, true));
151     dbstatus.setSize(new Dimension(290, 50));
152     dbstatex = new JLabel(" ");
153     dbstatex.setFont(f);
154     dbstatex.setSize(new Dimension(290, 50));
155     dbstat.add(dbstatus);
156     dbstat.add(dbstatex);
157     jc.add(dbstat, BorderLayout.SOUTH);
158     jc.validate();
159     // j.setPreferredSize(new Dimension(300,50));
160     add(jc, BorderLayout.CENTER);
161     j.add(ok);
162     j.add(cancel);
163     add(j, BorderLayout.SOUTH);
164     dbviews.addKeyListener(this);
165     validate();
166   }
167
168   private class DbTreeRenderer extends DefaultTreeCellRenderer implements
169           TreeCellRenderer
170   {
171     JDatabaseTree us;
172
173     public DbTreeRenderer(JDatabaseTree me)
174     {
175       us = me;
176     }
177
178     private Component returnLabel(String txt)
179     {
180       JLabel jl = new JLabel(txt);
181       jl.setFont(JvSwingUtils.getLabelFont());
182       return jl;
183     }
184
185     @Override
186     public Component getTreeCellRendererComponent(JTree tree, Object value,
187             boolean selected, boolean expanded, boolean leaf, int row,
188             boolean hasFocus)
189     {
190       String val = "";
191       if (value != null && value instanceof DefaultMutableTreeNode)
192       {
193         DefaultMutableTreeNode vl = (DefaultMutableTreeNode) value;
194         value = vl.getUserObject();
195         if (value instanceof DbSourceProxy)
196         {
197           val = (((DbSourceProxy) value).getDbName());
198         }
199         else
200         {
201           if (value instanceof String)
202           {
203             val = ((String) value);
204           }
205         }
206       }
207       if (value == null)
208       {
209         val = ("");
210       }
211       return super.getTreeCellRendererComponent(tree, val, selected,
212               expanded, leaf, row, hasFocus);
213
214     }
215   }
216
217   List<DbSourceProxy> oldselection, selection = null;
218
219   TreePath[] tsel = null, oldtsel = null;
220
221   @Override
222   protected void raiseClosed()
223   {
224     for (ActionListener al : lstners)
225     {
226       al.actionPerformed(null);
227     }
228   }
229
230   @Override
231   protected void okPressed()
232   {
233     _setSelectionState();
234     closeDialog();
235   }
236
237   @Override
238   protected void cancelPressed()
239   {
240     selection = oldselection;
241     tsel = oldtsel;
242     _revertSelectionState();
243     closeDialog();
244   }
245
246   private void showDialog(Container parent)
247   {
248     oldselection = selection;
249     oldtsel = tsel;
250     validate();
251     waitForInput();
252   }
253
254   public boolean hasSelection()
255   {
256     return selection == null ? false : selection.size() == 0 ? false : true;
257   }
258
259   public List<DbSourceProxy> getSelectedSources()
260   {
261     return selection;
262   }
263
264   /**
265    * disable or enable selection handler
266    */
267   boolean handleSelections = true;
268
269   private void _setSelectionState()
270   {
271     if (!handleSelections)
272     {
273       return;
274     }
275     if (dbviews.getSelectionCount() == 0)
276     {
277       selection = null;
278     }
279     tsel = dbviews.getSelectionPaths();
280     boolean forcedFirstChild = false;
281     List<DbSourceProxy> srcs = new ArrayList<DbSourceProxy>();
282     if (tsel != null)
283     {
284       for (TreePath tp : tsel)
285       {
286         DefaultMutableTreeNode admt, dmt = (DefaultMutableTreeNode) tp
287                 .getLastPathComponent();
288         if (dmt.getUserObject() != null)
289         {
290           if (dmt.getUserObject() instanceof DbSourceProxy)
291           {
292             srcs.add((DbSourceProxy) dmt.getUserObject());
293           }
294           else
295           {
296             if (allowMultiSelections)
297             {
298               srcs.addAll(sfetcher.getSourceProxy((String) dmt
299                       .getUserObject()));
300             }
301             else
302             {
303               if ((admt = dmt.getFirstLeaf()) != null
304                       && admt.getUserObject() != null)
305               {
306                 // modify db selection to just first leaf.
307                 if (admt.getUserObject() instanceof DbSourceProxy)
308                 {
309                   srcs.add((DbSourceProxy) admt.getUserObject());
310                 }
311                 else
312                 {
313                   srcs.add(sfetcher.getSourceProxy(
314                           (String) admt.getUserObject()).get(0));
315                 }
316                 forcedFirstChild = true;
317               }
318             }
319           }
320         }
321       }
322     }
323     updateDbStatus(srcs, forcedFirstChild);
324     selection = srcs;
325   }
326
327   private void _revertSelectionState()
328   {
329     handleSelections = false;
330     if (selection == null || selection.size() == 0)
331     {
332       dbviews.clearSelection();
333     }
334     else
335     {
336       dbviews.setSelectionPaths(tsel);
337     }
338     handleSelections = true;
339   }
340
341   private void updateDbStatus(List<DbSourceProxy> srcs,
342           boolean forcedFirstChild)
343   {
344     int x = 0;
345     String nm = "", qr = "";
346     for (DbSourceProxy dbs : srcs)
347     {
348       String tq = dbs.getTestQuery();
349       nm = dbs.getDbName();
350       if (tq != null && tq.trim().length() > 0 && dbs.isValidReference(tq))
351       {
352         qr = tq;
353         x++;
354       }
355     }
356
357     if (allowMultiSelections)
358     {
359       dbstatus.setText("Selected "
360               + srcs.size()
361               + " database"
362               + (srcs.size() == 1 ? "" : "s")
363               + " to fetch from"
364               + (srcs.size() > 0 ? " with " + x + " test quer"
365                       + (x == 1 ? "y" : "ies") : "."));
366       dbstatex.setText(" ");
367     }
368     else
369     {
370       if (nm.length() > 0)
371       {
372         dbstatus.setText("Database: " + nm);
373         if (qr.length() > 0)
374         {
375           dbstatex.setText("Example: " + qr);
376         }
377         else
378         {
379           dbstatex.setText(" ");
380         }
381       }
382       else
383       {
384         dbstatus.setText(" ");
385       }
386     }
387     dbstatus.invalidate();
388     dbstatex.invalidate();
389   }
390
391   public String getSelectedItem()
392   {
393     if (hasSelection())
394     {
395       return getSelectedSources().get(0).getDbName();
396     }
397     return null;
398   }
399
400   public String getExampleQueries()
401   {
402     if (!hasSelection())
403     {
404       return null;
405     }
406     StringBuffer sb = new StringBuffer();
407     HashSet<String> hs = new HashSet<String>();
408     for (DbSourceProxy dbs : getSelectedSources())
409     {
410       String tq = dbs.getTestQuery();
411       ;
412       if (hs.add(tq))
413       {
414         if (sb.length() > 0)
415         {
416           sb.append(";");
417         }
418         sb.append(tq);
419       }
420     }
421     return sb.toString();
422   }
423
424   List<ActionListener> lstners = new Vector<ActionListener>();
425
426   public void addActionListener(ActionListener actionListener)
427   {
428     lstners.add(actionListener);
429   }
430
431   public void removeActionListener(ActionListener actionListener)
432   {
433     lstners.remove(actionListener);
434   }
435
436   public static void main(String args[])
437   {
438     Cache.getDasSourceRegistry();
439     JDatabaseTree jdt = new JDatabaseTree(new jalview.ws.SequenceFetcher());
440     JFrame foo = new JFrame();
441     foo.setLayout(new BorderLayout());
442     foo.add(jdt.getDatabaseSelectorButton(), BorderLayout.CENTER);
443     foo.pack();
444     foo.setVisible(true);
445     int nultimes = 5;
446     final Thread us = Thread.currentThread();
447     jdt.addActionListener(new ActionListener()
448     {
449
450       @Override
451       public void actionPerformed(ActionEvent e)
452       {
453         us.interrupt();
454       }
455     });
456     do
457     {
458       try
459       {
460         Thread.sleep(50);
461       } catch (InterruptedException x)
462       {
463         nultimes--;
464         if (!jdt.hasSelection())
465         {
466           System.out.println("No Selection");
467         }
468         else
469         {
470           System.out.println("Selection: " + jdt.getSelectedItem());
471           int s = 1;
472           for (DbSourceProxy pr : jdt.getSelectedSources())
473           {
474             System.out.println("Source " + s++ + ": " + pr.getDbName()
475                     + " (" + pr.getDbSource() + ") Version "
476                     + pr.getDbVersion() + ". Test:\t" + pr.getTestQuery());
477           }
478           System.out.println("Test queries: " + jdt.getExampleQueries());
479         }
480       }
481     } while (nultimes > 0 && foo.isVisible());
482     foo.setVisible(false);
483   }
484
485   @Override
486   public void keyPressed(KeyEvent arg0)
487   {
488     if (!arg0.isConsumed() && arg0.getKeyCode() == KeyEvent.VK_ENTER)
489     {
490       okPressed();
491     }
492     if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE)
493     {
494       cancelPressed();
495     }
496   }
497
498   @Override
499   public void keyReleased(KeyEvent arg0)
500   {
501     // TODO Auto-generated method stub
502
503   }
504
505   @Override
506   public void keyTyped(KeyEvent arg0)
507   {
508     // TODO Auto-generated method stub
509
510   }
511 }