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