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