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