recovery of identical dataset sequence object on undo (rather than creation of a...
[jalview.git] / src / jalview / appletgui / FeatureSettings.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 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.appletgui;\r
20 \r
21 import java.util.*;\r
22 \r
23 import java.awt.*;\r
24 import java.awt.event.*;\r
25 \r
26 import jalview.datamodel.*;\r
27 \r
28 public class FeatureSettings\r
29     extends Panel implements ItemListener,\r
30     MouseListener, MouseMotionListener, ActionListener, AdjustmentListener\r
31 {\r
32   FeatureRenderer fr;\r
33   AlignmentPanel ap;\r
34   AlignViewport av;\r
35   Frame frame;\r
36   Panel groupPanel;\r
37   Panel featurePanel = new Panel();\r
38   ScrollPane scrollPane;\r
39   boolean alignmentHasFeatures = false;\r
40   Image linkImage;\r
41   Scrollbar transparency;\r
42 \r
43   public FeatureSettings(final AlignmentPanel ap)\r
44   {\r
45     this.ap = ap;\r
46     this.av = ap.av;\r
47     fr = ap.seqPanel.seqCanvas.getFeatureRenderer();\r
48 \r
49     transparency = new Scrollbar(Scrollbar.HORIZONTAL,\r
50                                  100 - (int) (fr.transparency * 100), 1, 1, 100);\r
51 \r
52     if (fr.transparencySetter != null)\r
53     {\r
54       transparency.addAdjustmentListener(this);\r
55     }\r
56     else\r
57     {\r
58       transparency.setEnabled(false);\r
59     }\r
60 \r
61     java.net.URL url = getClass().getResource("/images/link.gif");\r
62     if (url != null)\r
63     {\r
64       linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);\r
65     }\r
66 \r
67     if (av.featuresDisplayed == null)\r
68     {\r
69       fr.findAllFeatures();\r
70     }\r
71 \r
72     setTableData();\r
73 \r
74     this.setLayout(new BorderLayout());\r
75     scrollPane = new ScrollPane();\r
76     scrollPane.add(featurePanel);\r
77     if (alignmentHasFeatures)\r
78     {\r
79       add(scrollPane, BorderLayout.CENTER);\r
80     }\r
81 \r
82     Button invert = new Button("Invert Selection");\r
83     invert.addActionListener(this);\r
84 \r
85     Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));\r
86     lowerPanel.add(invert);\r
87 \r
88     Panel tPanel = new Panel(new BorderLayout());\r
89 \r
90     if (fr.transparencySetter != null)\r
91     {\r
92       tPanel.add(transparency, BorderLayout.CENTER);\r
93       tPanel.add(new Label("Transparency"), BorderLayout.EAST);\r
94     }\r
95     else\r
96     {\r
97       tPanel.add(new Label("Transparency not available in this web browser"),\r
98                  BorderLayout.CENTER);\r
99     }\r
100 \r
101     lowerPanel.add(tPanel, BorderLayout.SOUTH);\r
102 \r
103     add(lowerPanel, BorderLayout.SOUTH);\r
104 \r
105     if (groupPanel != null)\r
106     {\r
107       groupPanel.setLayout(\r
108           new GridLayout(fr.featureGroups.size() / 4 + 1, 4));\r
109       groupPanel.validate();\r
110 \r
111       add(groupPanel, BorderLayout.NORTH);\r
112     }\r
113     frame = new Frame();\r
114     frame.add(this);\r
115     int height = featurePanel.getComponentCount() * 50 + 60;\r
116 \r
117     height = Math.max(200, height);\r
118     height = Math.min(400, height);\r
119 \r
120     jalview.bin.JalviewLite.addFrame(frame, "Feature Settings", 280,\r
121                                      height);\r
122   }\r
123 \r
124   public void paint(Graphics g)\r
125   {\r
126     g.setColor(Color.black);\r
127     g.drawString("No Features added to this alignment!!", 10, 20);\r
128     g.drawString("(Features can be added from searches or", 10, 40);\r
129     g.drawString("from Jalview / GFF features files)", 10, 60);\r
130   }\r
131 \r
132   void setTableData()\r
133   {\r
134     alignmentHasFeatures = false;\r
135 \r
136     if (fr.featureGroups == null)\r
137     {\r
138       fr.featureGroups = new Hashtable();\r
139     }\r
140 \r
141     Vector allFeatures = new Vector();\r
142     Vector allGroups = new Vector();\r
143     SequenceFeature[] tmpfeatures;\r
144     String group;\r
145 \r
146     for (int i = 0; i < av.alignment.getHeight(); i++)\r
147     {\r
148       if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)\r
149       {\r
150         continue;\r
151       }\r
152 \r
153       alignmentHasFeatures = true;\r
154 \r
155       tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();\r
156       int index = 0;\r
157       while (index < tmpfeatures.length)\r
158       {\r
159         if (tmpfeatures[index].getFeatureGroup() != null)\r
160         {\r
161           group = tmpfeatures[index].featureGroup;\r
162           if (!allGroups.contains(group))\r
163           {\r
164             allGroups.addElement(group);\r
165 \r
166             boolean visible = true;\r
167             if (fr.featureGroups.containsKey(group))\r
168             {\r
169               visible = ( (Boolean) fr.featureGroups.get(group)).booleanValue();\r
170             }\r
171 \r
172             fr.featureGroups.put(group, new Boolean(visible));\r
173 \r
174             if (groupPanel == null)\r
175             {\r
176               groupPanel = new Panel();\r
177             }\r
178 \r
179             Checkbox check = new MyCheckbox(\r
180                 group,\r
181                 visible,\r
182                 (fr.featureLinks != null && fr.featureLinks.containsKey(group))\r
183                 );\r
184 \r
185             check.addMouseListener(this);\r
186             check.setFont(new Font("Serif", Font.BOLD, 12));\r
187             check.addItemListener(this);\r
188             groupPanel.add(check);\r
189           }\r
190         }\r
191 \r
192         if (!allFeatures.contains(tmpfeatures[index].getType()))\r
193         {\r
194           allFeatures.addElement(tmpfeatures[index].getType());\r
195         }\r
196         index++;\r
197       }\r
198     }\r
199 \r
200     resetTable(false);\r
201   }\r
202 \r
203   //This routine adds and removes checkboxes depending on\r
204   //Group selection states\r
205   void resetTable(boolean groupsChanged)\r
206   {\r
207     SequenceFeature[] tmpfeatures;\r
208     String group = null, type;\r
209     Vector visibleChecks = new Vector();\r
210 \r
211     for (int i = 0; i < av.alignment.getHeight(); i++)\r
212     {\r
213       if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)\r
214       {\r
215         continue;\r
216       }\r
217 \r
218       tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();\r
219       int index = 0;\r
220       while (index < tmpfeatures.length)\r
221       {\r
222         group = tmpfeatures[index].featureGroup;\r
223 \r
224         if (group == null || fr.featureGroups.get(group) == null ||\r
225             ( (Boolean) fr.featureGroups.get(group)).booleanValue())\r
226         {\r
227           type = tmpfeatures[index].getType();\r
228           if (!visibleChecks.contains(type))\r
229           {\r
230             visibleChecks.addElement(type);\r
231           }\r
232         }\r
233         index++;\r
234       }\r
235     }\r
236 \r
237     Component[] comps;\r
238     int cSize = featurePanel.getComponentCount();\r
239     Checkbox check;\r
240     //This will remove any checkboxes which shouldn't be\r
241     //visible\r
242     for (int i = 0; i < cSize; i++)\r
243     {\r
244       comps = featurePanel.getComponents();\r
245       check = (Checkbox) comps[i];\r
246       if (!visibleChecks.contains(check.getLabel()))\r
247       {\r
248         featurePanel.remove(i);\r
249         cSize--;\r
250         i--;\r
251       }\r
252     }\r
253 \r
254     if (fr.renderOrder != null)\r
255     {\r
256       //First add the checks in the previous render order,\r
257       //in case the window has been closed and reopened\r
258       for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)\r
259       {\r
260         String item = fr.renderOrder[ro];\r
261 \r
262         if (!visibleChecks.contains(item))\r
263         {\r
264           continue;\r
265         }\r
266 \r
267         visibleChecks.removeElement(item);\r
268 \r
269         addCheck(false, item);\r
270       }\r
271     }\r
272 \r
273     // now add checkboxes which should be visible,\r
274     // if they have not already been added\r
275     Enumeration en = visibleChecks.elements();\r
276 \r
277     while (en.hasMoreElements())\r
278     {\r
279       addCheck(groupsChanged, en.nextElement().toString());\r
280     }\r
281 \r
282     featurePanel.setLayout(new GridLayout(featurePanel.getComponentCount(), 1,\r
283                                           10, 5));\r
284     featurePanel.validate();\r
285 \r
286     if (scrollPane != null)\r
287     {\r
288       scrollPane.validate();\r
289     }\r
290 \r
291     itemStateChanged(null);\r
292   }\r
293 \r
294   void addCheck(boolean groupsChanged, String type)\r
295   {\r
296     boolean addCheck;\r
297     Component[] comps = featurePanel.getComponents();\r
298     Checkbox check;\r
299     addCheck = true;\r
300     for (int i = 0; i < featurePanel.getComponentCount(); i++)\r
301     {\r
302       check = (Checkbox) comps[i];\r
303       if (check.getLabel().equals(type))\r
304       {\r
305         addCheck = false;\r
306         break;\r
307       }\r
308     }\r
309 \r
310     if (addCheck)\r
311     {\r
312       boolean selected = false;\r
313       if (groupsChanged || av.featuresDisplayed.containsKey(type))\r
314       {\r
315         selected = true;\r
316       }\r
317 \r
318       check = new MyCheckbox(type,\r
319                              selected,\r
320                              (fr.featureLinks != null &&\r
321                               fr.featureLinks.containsKey(type))\r
322           );\r
323 \r
324       check.addMouseListener(this);\r
325       check.addMouseMotionListener(this);\r
326       check.setBackground(fr.getColour(type));\r
327       check.addItemListener(this);\r
328       featurePanel.add(check);\r
329     }\r
330   }\r
331 \r
332   public void actionPerformed(ActionEvent evt)\r
333   {\r
334     for (int i = 0; i < featurePanel.getComponentCount(); i++)\r
335     {\r
336       Checkbox check = (Checkbox) featurePanel.getComponent(i);\r
337       check.setState(!check.getState());\r
338     }\r
339     selectionChanged();\r
340   }\r
341 \r
342   public void itemStateChanged(ItemEvent evt)\r
343   {\r
344     if (evt != null)\r
345     {\r
346       //Is the source a top level featureGroup?\r
347       Checkbox source = (Checkbox) evt.getSource();\r
348       if (fr.featureGroups.containsKey(source.getLabel()))\r
349       {\r
350         fr.featureGroups.put(source.getLabel(), new Boolean(source.getState()));\r
351         ap.seqPanel.seqCanvas.repaint();\r
352         if (ap.overviewPanel != null)\r
353         {\r
354           ap.overviewPanel.updateOverviewImage();\r
355         }\r
356 \r
357         resetTable(true);\r
358         return;\r
359       }\r
360     }\r
361     selectionChanged();\r
362   }\r
363 \r
364   void selectionChanged()\r
365   {\r
366     Component[] comps = featurePanel.getComponents();\r
367     int cSize = comps.length;\r
368 \r
369     Object[][] tmp = new Object[cSize][3];\r
370     int tmpSize = 0;\r
371     for (int i = 0; i < cSize; i++)\r
372     {\r
373       Checkbox check = (Checkbox) comps[i];\r
374       tmp[tmpSize][0] = check.getLabel();\r
375       tmp[tmpSize][1] = fr.getColour(check.getLabel());\r
376       tmp[tmpSize][2] = new Boolean(check.getState());\r
377       tmpSize++;\r
378     }\r
379 \r
380     Object[][] data = new Object[tmpSize][3];\r
381     System.arraycopy(tmp, 0, data, 0, tmpSize);\r
382 \r
383     fr.setFeaturePriority(data);\r
384 \r
385     ap.paintAlignment(true);\r
386   }\r
387 \r
388   MyCheckbox selectedCheck;\r
389   boolean dragging = false;\r
390 \r
391   public void mousePressed(MouseEvent evt)\r
392   {\r
393 \r
394     selectedCheck = (MyCheckbox) evt.getSource();\r
395 \r
396     if (fr.featureLinks != null\r
397         && fr.featureLinks.containsKey(selectedCheck.getLabel())\r
398         )\r
399     {\r
400       if (evt.getX() > selectedCheck.stringWidth + 20)\r
401       {\r
402         evt.consume();\r
403       }\r
404     }\r
405 \r
406   }\r
407 \r
408   public void mouseDragged(MouseEvent evt)\r
409   {\r
410     if ( ( (Component) evt.getSource()).getParent() != featurePanel)\r
411     {\r
412       return;\r
413     }\r
414     dragging = true;\r
415   }\r
416 \r
417   public void mouseReleased(MouseEvent evt)\r
418   {\r
419     if ( ( (Component) evt.getSource()).getParent() != featurePanel)\r
420     {\r
421       return;\r
422     }\r
423 \r
424     Component comp = null;\r
425     Checkbox target = null;\r
426 \r
427     int height = evt.getY() + evt.getComponent().getLocation().y;\r
428 \r
429     if (height > featurePanel.getSize().height)\r
430     {\r
431 \r
432       comp = featurePanel.getComponent(featurePanel.getComponentCount() - 1);\r
433     }\r
434     else if (height < 0)\r
435     {\r
436       comp = featurePanel.getComponent(0);\r
437     }\r
438     else\r
439     {\r
440       comp = featurePanel.getComponentAt(evt.getX(),\r
441                                          evt.getY() +\r
442                                          evt.getComponent().getLocation().y);\r
443     }\r
444 \r
445     if (comp != null && comp instanceof Checkbox)\r
446     {\r
447       target = (Checkbox) comp;\r
448     }\r
449 \r
450     if (selectedCheck != null\r
451         && target != null\r
452         && selectedCheck != target)\r
453     {\r
454       int targetIndex = -1;\r
455       for (int i = 0; i < featurePanel.getComponentCount(); i++)\r
456       {\r
457         if (target == featurePanel.getComponent(i))\r
458         {\r
459           targetIndex = i;\r
460           break;\r
461         }\r
462       }\r
463 \r
464       featurePanel.remove(selectedCheck);\r
465       featurePanel.add(selectedCheck, targetIndex);\r
466       featurePanel.validate();\r
467       itemStateChanged(null);\r
468     }\r
469   }\r
470 \r
471   public void setUserColour(String feature, Color col)\r
472   {\r
473     fr.setColour(feature, col);\r
474     featurePanel.removeAll();\r
475     resetTable(false);\r
476     ap.paintAlignment(true);\r
477   }\r
478 \r
479   public void mouseEntered(MouseEvent evt)\r
480   {}\r
481 \r
482   public void mouseExited(MouseEvent evt)\r
483   {}\r
484 \r
485   public void mouseClicked(MouseEvent evt)\r
486   {\r
487     MyCheckbox check = (MyCheckbox) evt.getSource();\r
488 \r
489     if (fr.featureLinks != null\r
490         && fr.featureLinks.containsKey(check.getLabel()))\r
491     {\r
492       if (evt.getX() > check.stringWidth + 20)\r
493       {\r
494         evt.consume();\r
495         String link = fr.featureLinks.get(check.getLabel()).toString();\r
496         ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),\r
497                               link.substring(0, link.indexOf("|")));\r
498       }\r
499     }\r
500 \r
501     if (check.getParent() != featurePanel)\r
502     {\r
503       return;\r
504     }\r
505 \r
506     if (evt.getClickCount() > 1)\r
507     {\r
508       new UserDefinedColours(this, check.getLabel(),\r
509                              fr.getColour(check.getLabel()));\r
510     }\r
511   }\r
512 \r
513   public void mouseMoved(MouseEvent evt)\r
514   {}\r
515 \r
516   public void adjustmentValueChanged(AdjustmentEvent evt)\r
517   {\r
518     fr.transparency = ( (float) (100 - transparency.getValue()) / 100f);\r
519     ap.seqPanel.seqCanvas.repaint();\r
520 \r
521   }\r
522 \r
523   class MyCheckbox\r
524       extends Checkbox\r
525   {\r
526     public int stringWidth;\r
527     boolean hasLink;\r
528     public MyCheckbox(String label, boolean checked, boolean haslink)\r
529     {\r
530       super(label, checked);\r
531 \r
532       FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());\r
533       stringWidth = fm.stringWidth(label);\r
534       this.hasLink = haslink;\r
535     }\r
536 \r
537     public void paint(Graphics g)\r
538     {\r
539       if (hasLink)\r
540       {\r
541         g.drawImage(linkImage, stringWidth + 25, (\r
542             getSize().height - linkImage.getHeight(this)) / 2,\r
543                     this);\r
544       }\r
545     }\r
546   }\r
547 }\r