updated to jalview 2.1 and begun ArchiveClient/VamsasClient/VamsasStore updates.
[jalview.git] / src / jalview / gui / FeatureSettings.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import jalview.datamodel.*;
22 import javax.swing.*;
23 import javax.swing.event.*;
24 import java.awt.*;
25 import java.util.*;
26 import javax.swing.BorderFactory;
27 import java.awt.event.*;
28 import javax.swing.table.*;
29 import java.io.*;
30 import jalview.io.JalviewFileChooser;
31 import java.awt.BorderLayout;
32 import java.awt.Font;
33 import java.awt.Color;
34
35 public class FeatureSettings extends JPanel
36 {
37   DasSourceBrowser dassourceBrowser;
38   JPanel settingsPane = new JPanel();
39   JPanel dasSettingsPane = new JPanel();
40
41   final FeatureRenderer fr;
42   final AlignFrame af;
43   Object [][] originalData;
44   final JInternalFrame frame;
45   JScrollPane scrollPane = new JScrollPane();
46   JTable table;
47   JPanel groupPanel;
48   JSlider transparency = new JSlider();
49
50   JPanel transPanel = new JPanel(new FlowLayout());
51
52   public FeatureSettings(AlignFrame af)
53   {
54     this.af = af;
55     fr = af.getFeatureRenderer();
56
57     transparency.setMaximum( 100 - (int)(fr.transparency*100) ) ;
58
59    try
60    {
61      jbInit();
62    }
63    catch (Exception ex)
64    {
65      ex.printStackTrace();
66    }
67
68    table = new JTable();
69    table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
70    table.setFont(new Font("Verdana", Font.PLAIN, 12));
71    table.setDefaultRenderer(Color.class,
72                             new ColorRenderer());
73
74    table.setDefaultEditor(Color.class,
75                           new ColorEditor());
76
77    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
78
79    table.addMouseListener(new MouseAdapter()
80    {
81      public void mousePressed(MouseEvent evt)
82      {
83        selectedRow = table.rowAtPoint(evt.getPoint());
84      }
85    });
86
87    table.addMouseMotionListener(new MouseMotionAdapter()
88    {
89      public void mouseDragged(MouseEvent evt)
90      {
91        int newRow = table.rowAtPoint(evt.getPoint());
92        if (newRow != selectedRow
93            && selectedRow != -1
94            && newRow != -1)
95        {
96          Object[] temp = new Object[3];
97          temp[0] = table.getValueAt(selectedRow, 0);
98          temp[1] = table.getValueAt(selectedRow, 1);
99          temp[2] = table.getValueAt(selectedRow, 2);
100
101          table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
102          table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
103          table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
104
105          table.setValueAt(temp[0], newRow, 0);
106          table.setValueAt(temp[1], newRow, 1);
107          table.setValueAt(temp[2], newRow, 2);
108
109          selectedRow = newRow;
110        }
111      }
112    });
113
114    scrollPane.setViewportView(table);
115
116     dassourceBrowser = new DasSourceBrowser();
117     dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
118
119
120
121     if (af.getViewport().featuresDisplayed == null || fr.renderOrder==null)
122        fr.findAllFeatures();
123
124     setTableData();
125
126     frame = new JInternalFrame();
127     frame.setContentPane(this);
128     Desktop.addInternalFrame(frame, "Sequence Feature Settings", 400, 450);
129     frame.setLayer(JLayeredPane.PALETTE_LAYER);
130   }
131
132   synchronized public void setTableData()
133   {
134     if (fr.featureGroups == null)
135       fr.featureGroups = new Hashtable();
136
137     Vector allFeatures = new Vector();
138     Vector allGroups = new Vector();
139     SequenceFeature[] tmpfeatures;
140     String group;
141
142     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
143     {
144       if (af.getViewport().alignment.getSequenceAt(i).getDatasetSequence().getSequenceFeatures() == null)
145         continue;
146
147       tmpfeatures = af.getViewport().alignment.getSequenceAt(i).getDatasetSequence().getSequenceFeatures();
148
149       int index = 0;
150       while (index < tmpfeatures.length)
151       {
152         if(tmpfeatures[index].begin == 0 && tmpfeatures[index].end ==0)
153         {
154           index++;
155           continue;
156         }
157
158         if(tmpfeatures[index].getFeatureGroup()!=null)
159         {
160           group = tmpfeatures[index].featureGroup;
161           if(!allGroups.contains(group))
162            {
163              allGroups.addElement(group);
164
165              boolean visible = true;
166              if (fr.featureGroups.containsKey(group))
167              {
168                visible = ( (Boolean) fr.featureGroups.get(group)).booleanValue();
169              }
170
171                if (groupPanel == null)
172                {
173                  groupPanel = new JPanel();
174                }
175
176                boolean alreadyAdded = false;
177                for(int g=0; g<groupPanel.getComponentCount(); g++)
178                {
179                  if(((JCheckBox)groupPanel.getComponent(g))
180                     .getText().equals(group))
181                  {
182                    alreadyAdded = true;
183                    break;
184                  }
185                }
186
187                if(alreadyAdded)
188                  continue;
189
190                fr.featureGroups.put(group, new Boolean(visible));
191
192                final JCheckBox check = new JCheckBox(group, visible);
193                check.setFont(new Font("Serif", Font.BOLD, 12));
194                check.addItemListener(new ItemListener()
195                {
196                  public void itemStateChanged(ItemEvent evt)
197                  {
198                    fr.featureGroups.put(check.getText(),
199                                         new Boolean(check.isSelected()));
200                    af.alignPanel.seqPanel.seqCanvas.repaint();
201                    if (af.alignPanel.overviewPanel != null)
202                      af.alignPanel.overviewPanel.updateOverviewImage();
203
204                    resetTable(true);
205                  }
206                });
207                groupPanel.add(check);
208              }
209        }
210
211        if (!allFeatures.contains(tmpfeatures[index].getType()))
212        {
213            allFeatures.addElement(tmpfeatures[index].getType());
214        }
215        index ++;
216     }
217   }
218
219      resetTable(false);
220
221      validate();
222   }
223
224
225   void resetTable(boolean groupsChanged)
226   {
227    SequenceFeature [] tmpfeatures;
228    String group=null, type;
229    Vector visibleChecks = new Vector();
230
231    //Find out which features should be visible depending on which groups
232    //are selected / deselected
233     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
234     {
235
236         tmpfeatures = af.getViewport().alignment.getSequenceAt(i).getDatasetSequence().getSequenceFeatures();
237         if (tmpfeatures == null)
238           continue;
239
240         int index = 0;
241         while (index < tmpfeatures.length)
242         {
243           group = tmpfeatures[index].featureGroup;
244
245           if(tmpfeatures[index].begin==0 && tmpfeatures[index].end==0)
246           {
247             index ++;
248             continue;
249           }
250
251           if (group==null || fr.featureGroups.get(group)==null ||
252               ((Boolean) fr.featureGroups.get(group)).booleanValue())
253           {
254             type = tmpfeatures[index].getType();
255             if(!visibleChecks.contains(type) )
256             {
257               visibleChecks.addElement(type);
258             }
259           }
260           index++;
261         }
262     }
263
264     int fSize = visibleChecks.size();
265     Object [][] data = new Object[fSize][3];
266     int dataIndex = 0;
267
268     if(fr.renderOrder!=null)
269     {
270       //First add the checks in the previous render order,
271       //in case the window has been closed and reopened
272       for(int ro=fr.renderOrder.length-1; ro>-1; ro--)
273       {
274            type = fr.renderOrder[ro];
275
276            if(!visibleChecks.contains(type))
277              continue;
278
279            data[dataIndex][0] = type;
280            data[dataIndex][1] = fr.getColour(type);
281            data[dataIndex][2] = new Boolean(af.getViewport().featuresDisplayed.containsKey(type));
282            dataIndex++;
283            visibleChecks.removeElement(type);
284       }
285     }
286
287     fSize = visibleChecks.size();
288     for(int i=0; i<fSize; i++)
289     {
290       //These must be extra features belonging to the group
291       //which was just selected
292       type = visibleChecks.elementAt(i).toString();
293       data[dataIndex][0] = type;
294
295       data[dataIndex][1] = fr.getColour(type);
296       data[dataIndex][2] = new Boolean(true);
297       dataIndex++;
298     }
299
300     if(originalData==null)
301     {
302       originalData = new Object[data.length][3];
303       System.arraycopy(data,0,originalData,0,data.length);
304     }
305
306     table.setModel(new FeatureTableModel(data));
307     table.getColumnModel().getColumn(0).setPreferredWidth(200);
308
309
310     if (groupPanel != null)
311     {
312       groupPanel.setLayout(
313           new GridLayout(fr.featureGroups.size() / 4 + 1, 4));
314
315       groupPanel.validate();
316       bigPanel.add(groupPanel, BorderLayout.NORTH);
317     }
318
319     updateFeatureRenderer(data);
320
321   }
322
323   void load()
324   {
325     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(
326                  "LAST_DIRECTORY"), new String[] { "fc" },
327              new String[] { "Sequence Feature Colours" }, "Sequence Feature Colours");
328      chooser.setFileView(new jalview.io.JalviewFileView());
329      chooser.setDialogTitle("Load Feature Colours");
330      chooser.setToolTipText("Load");
331
332      int value = chooser.showOpenDialog(this);
333
334      if (value == JalviewFileChooser.APPROVE_OPTION)
335      {
336        File file = chooser.getSelectedFile();
337
338        try
339        {
340          InputStreamReader in = new InputStreamReader(new FileInputStream(
341              file), "UTF-8");
342
343          jalview.binding.JalviewUserColours jucs = new jalview.binding.
344              JalviewUserColours();
345          jucs = (jalview.binding.JalviewUserColours) jucs.unmarshal(in);
346
347
348          for (int i = 0; i < jucs.getColourCount(); i++)
349          {
350            fr.setColour( jucs.getColour(i).getName(),
351                           new Color(Integer.parseInt( jucs.getColour(i).getRGB(), 16)));
352          }
353
354          setTableData();
355          af.alignPanel.repaint();
356        }
357        catch (Exception ex)
358        {
359          System.out.println("Error loading User Colour File\n" + ex);
360        }
361      }
362   }
363
364   void save()
365   {
366     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(
367                 "LAST_DIRECTORY"), new String[] { "fc" },
368             new String[] { "Sequence Feature Colours" }, "Sequence Feature Colours");
369     chooser.setFileView(new jalview.io.JalviewFileView());
370     chooser.setDialogTitle("Save Feature Colour Scheme");
371     chooser.setToolTipText("Save");
372
373     int value = chooser.showSaveDialog(this);
374
375     if (value == JalviewFileChooser.APPROVE_OPTION)
376     {
377         String choice = chooser.getSelectedFile().getPath();
378         jalview.binding.JalviewUserColours ucs = new jalview.binding.JalviewUserColours();
379         ucs.setSchemeName("Sequence Features");
380         try
381         {
382             PrintWriter out = new PrintWriter(new OutputStreamWriter(
383                         new FileOutputStream(choice), "UTF-8"));
384
385             Enumeration e = fr.featureColours.keys();
386            while(e.hasMoreElements())
387            {
388                 jalview.binding.Colour col = new jalview.binding.Colour();
389                 col.setName(e.nextElement().toString());
390                 col.setRGB(jalview.util.Format.getHexString(
391                         fr.getColour(col.getName())));
392                 ucs.addColour(col);
393             }
394
395             ucs.marshal(out);
396             out.close();
397         }
398         catch (Exception ex)
399         {
400             ex.printStackTrace();
401         }
402     }
403   }
404
405   public void invertSelection()
406   {
407     for(int i=0; i<table.getRowCount(); i++)
408     {
409       Boolean value = (Boolean)table.getValueAt(i,2);
410
411       table.setValueAt(
412           new Boolean(!value.booleanValue()),
413                        i,2);
414     }
415   }
416
417   public void close()
418   {
419     try
420     {
421       frame.setClosed(true);
422     }
423     catch (Exception exe)
424     {}
425
426   }
427
428   public void updateFeatureRenderer(Object [][] data)
429   {
430     fr.setFeaturePriority( data );
431     af.alignPanel.repaint();
432
433     if(af.alignPanel.overviewPanel!=null)
434       af.alignPanel.overviewPanel.updateOverviewImage();
435   }
436
437   int selectedRow =-1;
438   JTabbedPane tabbedPane = new JTabbedPane();
439   BorderLayout borderLayout1 = new BorderLayout();
440   BorderLayout borderLayout2 = new BorderLayout();
441   BorderLayout borderLayout3 = new BorderLayout();
442   JPanel bigPanel = new JPanel();
443   BorderLayout borderLayout4 = new BorderLayout();
444   JButton invert = new JButton();
445   JPanel buttonPanel = new JPanel();
446   JButton cancel = new JButton();
447   JButton ok = new JButton();
448   JButton loadColours = new JButton();
449   JButton saveColours = new JButton();
450   JPanel dasButtonPanel = new JPanel();
451   JButton fetchDAS = new JButton();
452   JButton saveDAS = new JButton();
453   private void jbInit()
454       throws Exception
455   {
456     this.setLayout(borderLayout1);
457     settingsPane.setLayout(borderLayout2);
458     dasSettingsPane.setLayout(borderLayout3);
459     bigPanel.setLayout(borderLayout4);
460     invert.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
461     invert.setText("Invert Selection");
462     invert.addActionListener(new ActionListener()
463     {
464       public void actionPerformed(ActionEvent e)
465       {
466         invertSelection();
467       }
468     });
469     cancel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
470     cancel.setText("Cancel");
471     cancel.addActionListener(new ActionListener()
472     {
473       public void actionPerformed(ActionEvent e)
474       {
475           updateFeatureRenderer(originalData);
476           close();
477       }
478     });
479     ok.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
480     ok.setText("OK");
481     ok.addActionListener(new ActionListener()
482     {
483       public void actionPerformed(ActionEvent e)
484       {
485         close();
486       }
487     });
488     loadColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
489     loadColours.setText("Load Colours");
490     loadColours.addActionListener(new ActionListener()
491     {
492       public void actionPerformed(ActionEvent e)
493       {
494         load();
495       }
496     });
497     saveColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
498     saveColours.setText("Save Colours");
499     saveColours.addActionListener(new ActionListener()
500     {
501       public void actionPerformed(ActionEvent e)
502       {
503         save();
504       }
505     });
506     transparency.addChangeListener(new ChangeListener()
507     {
508       public void stateChanged(ChangeEvent evt)
509       {
510         fr.setTransparency( (float) (100 - transparency.getValue()) / 100f);
511         af.alignPanel.repaint();
512       }
513     });
514
515
516     transparency.setMaximum(70);
517     fetchDAS.setText("Fetch DAS Features");
518     fetchDAS.addActionListener(new ActionListener()
519     {
520       public void actionPerformed(ActionEvent e)
521       {
522         fetchDAS_actionPerformed(e);
523       }
524     });
525     saveDAS.setText("Save as default");
526     saveDAS.addActionListener(new ActionListener()
527     {
528       public void actionPerformed(ActionEvent e)
529       {
530         saveDAS_actionPerformed(e);
531       }
532     });
533     dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
534     dasSettingsPane.setBorder(null);
535     this.add(tabbedPane, java.awt.BorderLayout.CENTER);
536     tabbedPane.addTab("Feature Settings", settingsPane );
537     tabbedPane.addTab("DAS Settings", dasSettingsPane);
538     bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
539     transPanel.add(transparency);
540     transPanel.add(invert);
541     buttonPanel.add(ok);
542     buttonPanel.add(cancel);
543     buttonPanel.add(loadColours);
544     buttonPanel.add(saveColours);
545     bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
546     dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
547     dasButtonPanel.add(fetchDAS);
548     dasButtonPanel.add(saveDAS);
549     settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
550     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
551   }
552
553   public void fetchDAS_actionPerformed(ActionEvent e)
554   {
555     Vector selectedSources = dassourceBrowser.getSelectedSources();
556
557     SequenceI [] dataset, seqs ;
558     int iSize;
559
560     if(af.getViewport().getSelectionGroup()!=null
561       && af.getViewport().getSelectionGroup().getSize(false)>0)
562     {
563       iSize = af.getViewport().getSelectionGroup().getSize(false);
564       dataset = new SequenceI[iSize];
565       seqs = af.getViewport().getSelectionGroup().
566           getSequencesInOrder(
567               af.getViewport().getAlignment());
568     }
569     else
570     {
571        iSize = af.getViewport().getAlignment().getHeight();
572        seqs = af.getViewport().getAlignment().getSequencesArray();
573     }
574
575     dataset = new SequenceI[iSize];
576     for (int i = 0; i < iSize; i++)
577     {
578       dataset[i] = seqs[i].getDatasetSequence();
579     }
580
581     new jalview.io.DasSequenceFeatureFetcher(
582         dataset,
583         af,
584         selectedSources);
585
586     af.getViewport().setShowSequenceFeatures(true);
587     af.showSeqFeatures.setSelected(true);
588   }
589
590   public void saveDAS_actionPerformed(ActionEvent e)
591   {
592     dassourceBrowser.saveProperties(jalview.bin.Cache.applicationProperties);
593   }
594
595   /////////////////////////////////////////////////////////////////////////
596   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
597   /////////////////////////////////////////////////////////////////////////
598   class FeatureTableModel
599       extends AbstractTableModel
600   {
601     FeatureTableModel(Object[][] data)
602     {
603       this.data = data;
604     }
605
606     private String[] columnNames = {"Feature Type", "Colour","Display"};
607           private Object[][] data;
608
609           public Object[][] getData()
610           {
611             return data;
612           }
613
614           public void setData(Object[][] data)
615           {
616             this.data = data;
617           }
618
619           public int getColumnCount() {
620               return columnNames.length;
621           }
622
623           public Object[] getRow(int row)
624           {
625             return data[row];
626           }
627
628           public int getRowCount() {
629               return data.length;
630           }
631
632           public String getColumnName(int col) {
633               return columnNames[col];
634           }
635
636           public Object getValueAt(int row, int col) {
637               return data[row][col];
638           }
639
640           public Class getColumnClass(int c) {
641               return getValueAt(0, c).getClass();
642           }
643
644           public boolean isCellEditable(int row, int col) {
645               return col==0 ? false:true;
646           }
647
648           public void setValueAt(Object value, int row, int col) {
649               data[row][col] = value;
650               fireTableCellUpdated(row, col);
651               updateFeatureRenderer(data);
652           }
653
654     }
655     class ColorRenderer extends JLabel
656                               implements TableCellRenderer {
657        javax.swing.border.Border unselectedBorder = null;
658        javax.swing.border.Border selectedBorder = null;
659
660        public ColorRenderer() {
661            setOpaque(true); //MUST do this for background to show up.
662        }
663
664        public Component getTableCellRendererComponent(
665                                JTable table, Object color,
666                                boolean isSelected, boolean hasFocus,
667                                int row, int column) {
668            Color newColor = (Color)color;
669            setBackground(newColor);
670                if (isSelected) {
671                    if (selectedBorder == null) {
672                        selectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
673                                                  table.getSelectionBackground());
674                    }
675                    setBorder(selectedBorder);
676                } else {
677                    if (unselectedBorder == null) {
678                        unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
679                                                  table.getBackground());
680                    }
681                    setBorder(unselectedBorder);
682                }
683
684            setToolTipText("RGB value: " + newColor.getRed() + ", "
685                                         + newColor.getGreen() + ", "
686                                         + newColor.getBlue());
687            return this;
688        }
689    }
690 }
691
692  class ColorEditor extends AbstractCellEditor
693                           implements TableCellEditor,
694                                      ActionListener {
695      Color currentColor;
696      JButton button;
697      JColorChooser colorChooser;
698      JDialog dialog;
699      protected static final String EDIT = "edit";
700
701      public ColorEditor() {
702          //Set up the editor (from the table's point of view),
703          //which is a button.
704          //This button brings up the color chooser dialog,
705          //which is the editor from the user's point of view.
706          button = new JButton();
707          button.setActionCommand(EDIT);
708          button.addActionListener(this);
709          button.setBorderPainted(false);
710          //Set up the dialog that the button brings up.
711          colorChooser = new JColorChooser();
712          dialog = JColorChooser.createDialog(button,
713                                          "Select new Colour",
714                                          true,  //modal
715                                          colorChooser,
716                                          this,  //OK button handler
717                                          null); //no CANCEL button handler
718      }
719
720      /**
721       * Handles events from the editor button and from
722       * the dialog's OK button.
723       */
724      public void actionPerformed(ActionEvent e) {
725
726           if (EDIT.equals(e.getActionCommand())) {
727              //The user has clicked the cell, so
728              //bring up the dialog.
729              button.setBackground(currentColor);
730              colorChooser.setColor(currentColor);
731              dialog.setVisible(true);
732
733              //Make the renderer reappear.
734              fireEditingStopped();
735
736          } else { //User pressed dialog's "OK" button.
737              currentColor = colorChooser.getColor();
738          }
739      }
740
741      //Implement the one CellEditor method that AbstractCellEditor doesn't.
742      public Object getCellEditorValue() {
743          return currentColor;
744      }
745
746      //Implement the one method defined by TableCellEditor.
747      public Component getTableCellEditorComponent(JTable table,
748                                                   Object value,
749                                                   boolean isSelected,
750                                                   int row,
751                                                   int column) {
752          currentColor = (Color)value;
753          return button;
754      }
755 }