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