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