FeatureGroups added
[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   public FeatureSettings(AlignViewport av, final AlignmentPanel ap)\r
45   {\r
46     this.ap = ap;\r
47     this.av = av;\r
48     fr = ap.seqPanel.seqCanvas.getFeatureRenderer();\r
49     av.alignment.getSequences();\r
50     frame = new JInternalFrame();\r
51     frame.setContentPane(this);\r
52     Desktop.addInternalFrame(frame, "Sequence Feature Settings", 400, 300);\r
53 \r
54     setTableData();\r
55 \r
56     final JSlider transparency = new JSlider(0, 70, 0);\r
57     transparency.addChangeListener(new ChangeListener()\r
58     {\r
59       public void stateChanged(ChangeEvent evt)\r
60       {\r
61         fr.setTransparency( (float) (100 - transparency.getValue()) / 100f);\r
62         ap.repaint();\r
63       }\r
64     });\r
65 \r
66     JPanel transPanel = new JPanel(new FlowLayout());\r
67     transPanel.add(new JLabel("Transparency"));\r
68     transPanel.add(transparency);\r
69 \r
70     //////////////////////////////////////////////\r
71     //We're going to need those OK cancel buttons\r
72     JPanel buttonPanel = new JPanel(new FlowLayout());\r
73     JButton button = new JButton("OK");\r
74     button.addActionListener(new ActionListener()\r
75     {\r
76       public void actionPerformed(ActionEvent evt)\r
77       {\r
78         try\r
79         {\r
80           frame.setClosed(true);\r
81         }\r
82         catch (Exception exe)\r
83         {}\r
84       }\r
85     });\r
86     buttonPanel.add(button);\r
87     button = new JButton("Cancel");\r
88     button.addActionListener(new ActionListener()\r
89     {\r
90       public void actionPerformed(ActionEvent evt)\r
91       {\r
92         try\r
93         {\r
94           updateFeatureRenderer(originalData);\r
95           frame.setClosed(true);\r
96         }\r
97         catch (Exception exe)\r
98         {}\r
99       }\r
100     });\r
101     buttonPanel.add(button);\r
102 \r
103     button = new JButton("Load Colours");\r
104     button.addActionListener(new ActionListener()\r
105     {\r
106       public void actionPerformed(ActionEvent evt)\r
107       {\r
108         load();\r
109       }\r
110     });\r
111     buttonPanel.add(button);\r
112     button = new JButton("Save Colours");\r
113     button.addActionListener(new ActionListener()\r
114     {\r
115       public void actionPerformed(ActionEvent evt)\r
116       {\r
117         save();\r
118       }\r
119     });\r
120     buttonPanel.add(button);\r
121 \r
122     this.setLayout(new BorderLayout());\r
123     JPanel bigPanel = new JPanel(new BorderLayout());\r
124     bigPanel.add(transPanel, BorderLayout.SOUTH);\r
125     bigPanel.add(scrollPane, BorderLayout.CENTER);\r
126     if(groupPanel!=null)\r
127       bigPanel.add(groupPanel, BorderLayout.NORTH);\r
128 \r
129     add(bigPanel, BorderLayout.CENTER);\r
130     add(buttonPanel, BorderLayout.SOUTH);\r
131 \r
132 \r
133   }\r
134 \r
135   void setTableData()\r
136   {\r
137     if (fr.featureGroups == null)\r
138       fr.featureGroups = new Hashtable();\r
139     else\r
140       fr.featureGroups.clear();\r
141 \r
142     Vector allFeatures = new Vector();\r
143     Vector features;\r
144     Enumeration e;\r
145     SequenceFeature sf;\r
146 \r
147   for (int i = 0; i < av.alignment.getHeight(); i++)\r
148   {\r
149     features = av.alignment.getSequenceAt(i).getDatasetSequence().\r
150         getSequenceFeatures();\r
151     if (features == null)\r
152       continue;\r
153 \r
154     e = features.elements();\r
155     while (e.hasMoreElements())\r
156     {\r
157       sf = (SequenceFeature) e.nextElement();\r
158       if(sf.getFeatureGroup()!=null\r
159           && !fr.featureGroups.containsKey(sf.getFeatureGroup()))\r
160        {\r
161          fr.featureGroups.put(sf.getFeatureGroup(), new Boolean(true));\r
162          if(groupPanel==null)\r
163             groupPanel = new JPanel();\r
164 \r
165           final JCheckBox check = new JCheckBox(sf.getFeatureGroup(), true);\r
166           check.setFont(new Font("Serif", Font.BOLD, 12));\r
167           check.addItemListener(new ItemListener()\r
168               {\r
169                 public void itemStateChanged(ItemEvent evt)\r
170                 {\r
171                   if (fr.featureGroups.containsKey(check.getText()))\r
172                   {\r
173                     fr.featureGroups.put(check.getText(),\r
174                                          new Boolean(check.isSelected()));\r
175                     ap.seqPanel.seqCanvas.repaint();\r
176                     if (ap.overviewPanel != null)\r
177                       ap.overviewPanel.updateOverviewImage();\r
178                   }\r
179 \r
180                 }\r
181               });\r
182           groupPanel.add(check);\r
183        }\r
184       if (!allFeatures.contains(sf.getType()))\r
185       {\r
186         allFeatures.addElement(sf.getType());\r
187       }\r
188     }\r
189   }\r
190     if(allFeatures.size()<1)\r
191      {\r
192        try\r
193        { frame.setClosed(true);  }\r
194        catch (Exception ex){}\r
195 \r
196        JOptionPane.showInternalMessageDialog(\r
197            Desktop.desktop, "No features have been added to this alignment!",\r
198            "No Sequence Features", JOptionPane.WARNING_MESSAGE);\r
199 \r
200        return;\r
201      }\r
202 \r
203     int fSize = allFeatures.size();\r
204 \r
205     String type;\r
206     boolean originalExists = false;\r
207     if(originalData!=null)\r
208       originalExists = true;\r
209     else\r
210       originalData = new Object[fSize][3];\r
211 \r
212     Object [][] data = new Object[fSize][3];\r
213     for(int i=0; i<fSize; i++)\r
214     {\r
215       type = allFeatures.elementAt(i).toString();\r
216       Color col = fr.getColour(type);\r
217       data[i][0]=type;\r
218       data[i][1]=col;\r
219       if(fr.featuresDisplayed!=null)\r
220          data[i][2] = new Boolean(fr.featuresDisplayed.contains(type));\r
221       else\r
222         data[i][2] = new Boolean(true);\r
223 \r
224       if(!originalExists)\r
225       {\r
226         originalData[i][0] = type;\r
227         originalData[i][1] = col;\r
228         if (fr.featuresDisplayed != null)\r
229           originalData[i][2] = new Boolean(fr.featuresDisplayed.contains(type));\r
230         else\r
231           originalData[i][2] = new Boolean(true);\r
232       }\r
233     }\r
234     ////////////////////////////////////\r
235         //Now build the table\r
236         table = new JTable(new FeatureTableModel(data));\r
237         scrollPane.setViewportView(table);\r
238         table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));\r
239         table.setFont(new Font("Verdana", Font.PLAIN, 12));\r
240         table.setDefaultRenderer(Color.class,\r
241                              new ColorRenderer());\r
242 \r
243         table.setDefaultEditor(Color.class,\r
244                           new ColorEditor());\r
245 \r
246         table.getColumnModel().getColumn(0).setPreferredWidth(200);\r
247 \r
248         table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
249 \r
250         table.addMouseListener(new MouseAdapter()\r
251             {\r
252               public void mousePressed(MouseEvent evt)\r
253               {\r
254                 selectedRow = table.rowAtPoint(evt.getPoint());\r
255               }\r
256             });\r
257 \r
258         table.addMouseMotionListener(new MouseMotionAdapter()\r
259             {\r
260               public void mouseDragged(MouseEvent evt)\r
261               {\r
262                 int newRow = table.rowAtPoint(evt.getPoint());\r
263                 if(newRow!=selectedRow\r
264                    && selectedRow!=-1\r
265                    && newRow!=-1)\r
266                 {\r
267                   Object[] temp = new Object[3];\r
268                   temp[0] = table.getValueAt(selectedRow, 0);\r
269                   temp[1] = table.getValueAt(selectedRow, 1);\r
270                   temp[2] = table.getValueAt(selectedRow, 2);\r
271 \r
272                   table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);\r
273                   table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);\r
274                   table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);\r
275 \r
276                   table.setValueAt(temp[0], newRow, 0);\r
277                   table.setValueAt(temp[1], newRow, 1);\r
278                   table.setValueAt(temp[2], newRow, 2);\r
279 \r
280                   selectedRow = newRow;\r
281                 }\r
282               }\r
283         });\r
284 \r
285   }\r
286 \r
287   void load()\r
288   {\r
289     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(\r
290                  "LAST_DIRECTORY"), new String[] { "fc" },\r
291              new String[] { "Sequence Feature Colours" }, "Sequence Feature Colours");\r
292      chooser.setFileView(new jalview.io.JalviewFileView());\r
293      chooser.setDialogTitle("Load Feature Colours");\r
294      chooser.setToolTipText("Load");\r
295 \r
296      int value = chooser.showOpenDialog(this);\r
297 \r
298      if (value == JalviewFileChooser.APPROVE_OPTION)\r
299      {\r
300        File file = chooser.getSelectedFile();\r
301 \r
302        try\r
303        {\r
304          InputStreamReader in = new InputStreamReader(new FileInputStream(\r
305              file), "UTF-8");\r
306 \r
307          jalview.binding.JalviewUserColours jucs = new jalview.binding.\r
308              JalviewUserColours();\r
309          jucs = (jalview.binding.JalviewUserColours) jucs.unmarshal(in);\r
310 \r
311 \r
312          for (int i = 0; i < jucs.getColourCount(); i++)\r
313          {\r
314            fr.setColour( jucs.getColour(i).getName(),\r
315                           new Color(Integer.parseInt( jucs.getColour(i).getRGB(), 16)));\r
316          }\r
317 \r
318          setTableData();\r
319          ap.repaint();\r
320        }\r
321        catch (Exception ex)\r
322        {\r
323          System.out.println("Error loading User ColourFile\n" + ex);\r
324        }\r
325      }\r
326   }\r
327 \r
328   void save()\r
329   {\r
330     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(\r
331                 "LAST_DIRECTORY"), new String[] { "fc" },\r
332             new String[] { "Sequence Feature Colours" }, "Sequence Feature Colours");\r
333     chooser.setFileView(new jalview.io.JalviewFileView());\r
334     chooser.setDialogTitle("Save Feature Colour Scheme");\r
335     chooser.setToolTipText("Save");\r
336 \r
337     int value = chooser.showSaveDialog(this);\r
338 \r
339     if (value == JalviewFileChooser.APPROVE_OPTION)\r
340     {\r
341         String choice = chooser.getSelectedFile().getPath();\r
342         jalview.binding.JalviewUserColours ucs = new jalview.binding.JalviewUserColours();\r
343         ucs.setSchemeName("Sequence Features");\r
344         try\r
345         {\r
346             PrintWriter out = new PrintWriter(new OutputStreamWriter(\r
347                         new FileOutputStream(choice), "UTF-8"));\r
348 \r
349             Enumeration e = fr.featureColours.keys();\r
350            while(e.hasMoreElements())\r
351            {\r
352 \r
353 \r
354                 jalview.binding.Colour col = new jalview.binding.Colour();\r
355                 col.setName(e.nextElement().toString());\r
356                 col.setRGB(jalview.util.Format.getHexString(\r
357                         fr.getColour(col.getName())));\r
358                 ucs.addColour(col);\r
359             }\r
360 \r
361             ucs.marshal(out);\r
362             out.close();\r
363         }\r
364         catch (Exception ex)\r
365         {\r
366             ex.printStackTrace();\r
367         }\r
368     }\r
369 \r
370   }\r
371 \r
372   public void updateFeatureRenderer(Object [][] data)\r
373   {\r
374     fr.setFeaturePriority( data );\r
375     ap.repaint();\r
376 \r
377     if(ap.overviewPanel!=null)\r
378       ap.overviewPanel.updateOverviewImage();\r
379   }\r
380 \r
381   int selectedRow =-1;\r
382 \r
383 \r
384   /////////////////////////////////////////////////////////////////////////\r
385   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html\r
386   /////////////////////////////////////////////////////////////////////////\r
387   class FeatureTableModel\r
388       extends AbstractTableModel\r
389   {\r
390     FeatureTableModel(Object[][] data)\r
391     {\r
392       this.data = data;\r
393     }\r
394 \r
395     private String[] columnNames = {"Feature Type", "Colour","Display"};\r
396           private Object[][] data;\r
397 \r
398           public Object[][] getData()\r
399           {\r
400             return data;\r
401           }\r
402 \r
403           public int getColumnCount() {\r
404               return columnNames.length;\r
405           }\r
406 \r
407           public Object[] getRow(int row)\r
408           {\r
409             return data[row];\r
410           }\r
411 \r
412           public int getRowCount() {\r
413               return data.length;\r
414           }\r
415 \r
416           public String getColumnName(int col) {\r
417               return columnNames[col];\r
418           }\r
419 \r
420           public Object getValueAt(int row, int col) {\r
421               return data[row][col];\r
422           }\r
423 \r
424           public Class getColumnClass(int c) {\r
425               return getValueAt(0, c).getClass();\r
426           }\r
427 \r
428           public boolean isCellEditable(int row, int col) {\r
429               return col==0 ? false:true;\r
430           }\r
431 \r
432           public void setValueAt(Object value, int row, int col) {\r
433               data[row][col] = value;\r
434               fireTableCellUpdated(row, col);\r
435               updateFeatureRenderer(data);\r
436           }\r
437 \r
438     }\r
439     class ColorRenderer extends JLabel\r
440                               implements TableCellRenderer {\r
441        javax.swing.border.Border unselectedBorder = null;\r
442        javax.swing.border.Border selectedBorder = null;\r
443 \r
444        public ColorRenderer() {\r
445            setOpaque(true); //MUST do this for background to show up.\r
446        }\r
447 \r
448        public Component getTableCellRendererComponent(\r
449                                JTable table, Object color,\r
450                                boolean isSelected, boolean hasFocus,\r
451                                int row, int column) {\r
452            Color newColor = (Color)color;\r
453            setBackground(newColor);\r
454                if (isSelected) {\r
455                    if (selectedBorder == null) {\r
456                        selectedBorder = BorderFactory.createMatteBorder(2,5,2,5,\r
457                                                  table.getSelectionBackground());\r
458                    }\r
459                    setBorder(selectedBorder);\r
460                } else {\r
461                    if (unselectedBorder == null) {\r
462                        unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5,\r
463                                                  table.getBackground());\r
464                    }\r
465                    setBorder(unselectedBorder);\r
466                }\r
467 \r
468            setToolTipText("RGB value: " + newColor.getRed() + ", "\r
469                                         + newColor.getGreen() + ", "\r
470                                         + newColor.getBlue());\r
471            return this;\r
472        }\r
473    }\r
474 }\r
475 \r
476  class ColorEditor extends AbstractCellEditor\r
477                           implements TableCellEditor,\r
478                                      ActionListener {\r
479      Color currentColor;\r
480      JButton button;\r
481      JColorChooser colorChooser;\r
482      JDialog dialog;\r
483      protected static final String EDIT = "edit";\r
484 \r
485      public ColorEditor() {\r
486          //Set up the editor (from the table's point of view),\r
487          //which is a button.\r
488          //This button brings up the color chooser dialog,\r
489          //which is the editor from the user's point of view.\r
490          button = new JButton();\r
491          button.setActionCommand(EDIT);\r
492          button.addActionListener(this);\r
493          button.setBorderPainted(false);\r
494          //Set up the dialog that the button brings up.\r
495          colorChooser = new JColorChooser();\r
496          dialog = JColorChooser.createDialog(button,\r
497                                          "Select new Colour",\r
498                                          true,  //modal\r
499                                          colorChooser,\r
500                                          this,  //OK button handler\r
501                                          null); //no CANCEL button handler\r
502      }\r
503 \r
504      /**\r
505       * Handles events from the editor button and from\r
506       * the dialog's OK button.\r
507       */\r
508      public void actionPerformed(ActionEvent e) {\r
509 \r
510           if (EDIT.equals(e.getActionCommand())) {\r
511              //The user has clicked the cell, so\r
512              //bring up the dialog.\r
513              button.setBackground(currentColor);\r
514              colorChooser.setColor(currentColor);\r
515              dialog.setVisible(true);\r
516 \r
517              //Make the renderer reappear.\r
518              fireEditingStopped();\r
519 \r
520          } else { //User pressed dialog's "OK" button.\r
521              currentColor = colorChooser.getColor();\r
522          }\r
523      }\r
524 \r
525      //Implement the one CellEditor method that AbstractCellEditor doesn't.\r
526      public Object getCellEditorValue() {\r
527          return currentColor;\r
528      }\r
529 \r
530      //Implement the one method defined by TableCellEditor.\r
531      public Component getTableCellEditorComponent(JTable table,\r
532                                                   Object value,\r
533                                                   boolean isSelected,\r
534                                                   int row,\r
535                                                   int column) {\r
536          currentColor = (Color)value;\r
537          return button;\r
538      }\r
539 }\r