JAL-2808 pass FeatureSettings to controller as array of data bean
[jalview.git] / src / jalview / appletgui / FeatureSettings.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.api.FeatureColourI;
24 import jalview.api.FeatureSettingsControllerI;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.util.MessageManager;
28 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
29
30 import java.awt.BorderLayout;
31 import java.awt.Button;
32 import java.awt.Checkbox;
33 import java.awt.Color;
34 import java.awt.Component;
35 import java.awt.Dimension;
36 import java.awt.Font;
37 import java.awt.FontMetrics;
38 import java.awt.Frame;
39 import java.awt.Graphics;
40 import java.awt.GridLayout;
41 import java.awt.Image;
42 import java.awt.Label;
43 import java.awt.MenuItem;
44 import java.awt.Panel;
45 import java.awt.PopupMenu;
46 import java.awt.ScrollPane;
47 import java.awt.Scrollbar;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.awt.event.AdjustmentEvent;
51 import java.awt.event.AdjustmentListener;
52 import java.awt.event.InputEvent;
53 import java.awt.event.ItemEvent;
54 import java.awt.event.ItemListener;
55 import java.awt.event.MouseEvent;
56 import java.awt.event.MouseListener;
57 import java.awt.event.MouseMotionListener;
58 import java.awt.event.WindowAdapter;
59 import java.awt.event.WindowEvent;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.HashSet;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66
67 public class FeatureSettings extends Panel
68         implements ItemListener, MouseListener, MouseMotionListener,
69         ActionListener, AdjustmentListener, FeatureSettingsControllerI
70 {
71   private static final int TYPE_COLUMN = 0;
72
73   private static final int COLOUR_COLUMN = 1;
74
75   private static final int SHOW_COLUMN = 2;
76
77   FeatureRenderer fr;
78
79   AlignmentPanel ap;
80
81   AlignViewport av;
82
83   Frame frame;
84
85   Panel groupPanel;
86
87   Panel featurePanel = new Panel();
88
89   ScrollPane scrollPane;
90
91   Image linkImage;
92
93   Scrollbar transparency;
94
95   public FeatureSettings(final AlignmentPanel ap)
96   {
97     this.ap = ap;
98     this.av = ap.av;
99     ap.av.featureSettings = this;
100     fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
101
102     transparency = new Scrollbar(Scrollbar.HORIZONTAL,
103             100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
104
105     transparency.addAdjustmentListener(this);
106
107     java.net.URL url = getClass().getResource("/images/link.gif");
108     if (url != null)
109     {
110       linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
111     }
112
113     if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
114     {
115       fr.findAllFeatures(true); // was default - now true to make all visible
116     }
117     groupPanel = new Panel();
118
119     discoverAllFeatureData();
120
121     this.setLayout(new BorderLayout());
122     scrollPane = new ScrollPane();
123     scrollPane.add(featurePanel);
124     if (fr.getAllFeatureColours() != null
125             && fr.getAllFeatureColours().size() > 0)
126     {
127       add(scrollPane, BorderLayout.CENTER);
128     }
129
130     Button invert = new Button("Invert Selection");
131     invert.addActionListener(this);
132
133     Panel lowerPanel = new Panel(new GridLayout(2, 1, 5, 10));
134     lowerPanel.add(invert);
135
136     Panel tPanel = new Panel(new BorderLayout());
137
138     tPanel.add(transparency, BorderLayout.CENTER);
139     tPanel.add(new Label("Transparency"), BorderLayout.EAST);
140
141     lowerPanel.add(tPanel, BorderLayout.SOUTH);
142
143     add(lowerPanel, BorderLayout.SOUTH);
144
145     groupPanel.setLayout(
146             new GridLayout((fr.getFeatureGroupsSize()) / 4 + 1, 4)); // JBPNote
147                                                                      // - this
148                                                                      // was
149                                                                      // scaled
150                                                                      // on
151                                                                      // number
152                                                                      // of
153                                                                      // visible
154                                                                      // groups.
155                                                                      // seems
156                                                                      // broken
157     groupPanel.validate();
158
159     add(groupPanel, BorderLayout.NORTH);
160
161     frame = new Frame();
162     frame.add(this);
163     final FeatureSettings me = this;
164     frame.addWindowListener(new WindowAdapter()
165     {
166       @Override
167       public void windowClosing(WindowEvent e)
168       {
169         if (me.av.featureSettings == me)
170         {
171           me.av.featureSettings = null;
172           me.ap = null;
173           me.av = null;
174         }
175       }
176     });
177     int height = featurePanel.getComponentCount() * 50 + 60;
178
179     height = Math.max(200, height);
180     height = Math.min(400, height);
181     int width = 300;
182     jalview.bin.JalviewLite.addFrame(frame,
183             MessageManager.getString("label.sequence_feature_settings"),
184             width, height);
185   }
186
187   @Override
188   public void paint(Graphics g)
189   {
190     g.setColor(Color.black);
191     g.drawString(MessageManager.getString(
192             "label.no_features_added_to_this_alignment"), 10, 20);
193     g.drawString(MessageManager.getString(
194             "label.features_can_be_added_from_searches_1"), 10, 40);
195     g.drawString(MessageManager.getString(
196             "label.features_can_be_added_from_searches_2"), 10, 60);
197   }
198
199   protected void popupSort(final MyCheckbox check,
200           final Map<String, float[][]> minmax, int x, int y)
201   {
202     final String type = check.type;
203     final FeatureColourI typeCol = fr.getFeatureStyle(type);
204     PopupMenu men = new PopupMenu(MessageManager
205             .formatMessage("label.settings_for_type", new String[]
206             { type }));
207     java.awt.MenuItem scr = new MenuItem(
208             MessageManager.getString("label.sort_by_score"));
209     men.add(scr);
210     final FeatureSettings me = this;
211     scr.addActionListener(new ActionListener()
212     {
213
214       @Override
215       public void actionPerformed(ActionEvent e)
216       {
217         me.ap.alignFrame.avc
218                 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
219                 { type }));
220       }
221
222     });
223     MenuItem dens = new MenuItem(
224             MessageManager.getString("label.sort_by_density"));
225     dens.addActionListener(new ActionListener()
226     {
227
228       @Override
229       public void actionPerformed(ActionEvent e)
230       {
231         me.ap.alignFrame.avc
232                 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
233                 { type }));
234       }
235
236     });
237     men.add(dens);
238
239     if (minmax != null)
240     {
241       final float[][] typeMinMax = minmax.get(type);
242       /*
243        * final java.awt.CheckboxMenuItem chb = new
244        * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
245        * moment chb.setState(minmax.get(type) != null);
246        * chb.addActionListener(new ActionListener() {
247        * 
248        * public void actionPerformed(ActionEvent e) {
249        * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
250        * null); } else { minmax.put(type, typeMinMax); } }
251        * 
252        * }); men.add(chb);
253        */
254       if (typeMinMax != null && typeMinMax[0] != null)
255       {
256         // graduated colourschemes for those where minmax exists for the
257         // positional features
258         MenuItem mxcol = new MenuItem(
259                 (typeCol.isSimpleColour()) ? "Graduated Colour"
260                         : "Single Colour");
261         men.add(mxcol);
262         mxcol.addActionListener(new ActionListener()
263         {
264
265           @Override
266           public void actionPerformed(ActionEvent e)
267           {
268             if (typeCol.isSimpleColour())
269             {
270               new FeatureColourChooser(me, type);
271               // write back the current colour object to update the table
272               check.updateColor(fr.getFeatureStyle(type));
273             }
274             else
275             {
276               new UserDefinedColours(me, check.type, typeCol);
277             }
278           }
279
280         });
281       }
282     }
283
284     MenuItem selectContaining = new MenuItem(
285             MessageManager.getString("label.select_columns_containing"));
286     selectContaining.addActionListener(new ActionListener()
287     {
288       @Override
289       public void actionPerformed(ActionEvent e)
290       {
291         me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
292                 false, type);
293       }
294     });
295     men.add(selectContaining);
296
297     MenuItem selectNotContaining = new MenuItem(MessageManager
298             .getString("label.select_columns_not_containing"));
299     selectNotContaining.addActionListener(new ActionListener()
300     {
301       @Override
302       public void actionPerformed(ActionEvent e)
303       {
304         me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
305                 false, type);
306       }
307     });
308     men.add(selectNotContaining);
309
310     MenuItem hideContaining = new MenuItem(
311             MessageManager.getString("label.hide_columns_containing"));
312     hideContaining.addActionListener(new ActionListener()
313     {
314       @Override
315       public void actionPerformed(ActionEvent e)
316       {
317         hideFeatureColumns(type, true);
318       }
319     });
320     men.add(hideContaining);
321
322     MenuItem hideNotContaining = new MenuItem(
323             MessageManager.getString("label.hide_columns_not_containing"));
324     hideNotContaining.addActionListener(new ActionListener()
325     {
326       @Override
327       public void actionPerformed(ActionEvent e)
328       {
329         hideFeatureColumns(type, false);
330       }
331     });
332     men.add(hideNotContaining);
333
334     this.featurePanel.add(men);
335     men.show(this.featurePanel, x, y);
336   }
337
338   @Override
339   public void discoverAllFeatureData()
340   {
341     if (fr.getAllFeatureColours() != null
342             && fr.getAllFeatureColours().size() > 0)
343     {
344       // rebuildGroups();
345
346     }
347     resetTable(false);
348   }
349
350   /**
351    * Answers the visibility of the given group, and adds a checkbox for it if
352    * there is not one already
353    */
354   public boolean checkGroupState(String group)
355   {
356     boolean visible = fr.checkGroupVisibility(group, true);
357
358     /*
359      * is there already a checkbox for this group?
360      */
361     for (int g = 0; g < groupPanel.getComponentCount(); g++)
362     {
363       if (((Checkbox) groupPanel.getComponent(g)).getLabel().equals(group))
364       {
365         ((Checkbox) groupPanel.getComponent(g)).setState(visible);
366         return visible;
367       }
368     }
369
370     /*
371      * add a new checkbox
372      */
373     Checkbox check = new MyCheckbox(group, visible, false);
374     check.addMouseListener(this);
375     check.setFont(new Font("Serif", Font.BOLD, 12));
376     check.addItemListener(groupItemListener);
377     groupPanel.add(check);
378
379     groupPanel.validate();
380     return visible;
381   }
382
383   // This routine adds and removes checkboxes depending on
384   // Group selection states
385   void resetTable(boolean groupsChanged)
386   {
387     List<String> displayableTypes = new ArrayList<>();
388     Set<String> foundGroups = new HashSet<>();
389
390     AlignmentI alignment = av.getAlignment();
391
392     for (int i = 0; i < alignment.getHeight(); i++)
393     {
394       SequenceI seq = alignment.getSequenceAt(i);
395
396       /*
397        * get the sequence's groups for positional features
398        * and keep track of which groups are visible
399        */
400       Set<String> groups = seq.getFeatures().getFeatureGroups(true);
401       Set<String> visibleGroups = new HashSet<>();
402       for (String group : groups)
403       {
404         // if (group == null || fr.checkGroupVisibility(group, true))
405         if (group == null || checkGroupState(group))
406         {
407           visibleGroups.add(group);
408         }
409       }
410       foundGroups.addAll(groups);
411
412       /*
413        * get distinct feature types for visible groups
414        * record distinct visible types
415        */
416       Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
417               visibleGroups.toArray(new String[visibleGroups.size()]));
418       displayableTypes.addAll(types);
419     }
420
421     /*
422      * remove any checkboxes for groups not present
423      */
424     pruneGroups(foundGroups);
425
426     Component[] comps;
427     int cSize = featurePanel.getComponentCount();
428     MyCheckbox check;
429     // This will remove any checkboxes which shouldn't be
430     // visible
431     for (int i = 0; i < cSize; i++)
432     {
433       comps = featurePanel.getComponents();
434       check = (MyCheckbox) comps[i];
435       if (!displayableTypes.contains(check.type))
436       {
437         featurePanel.remove(i);
438         cSize--;
439         i--;
440       }
441     }
442
443     if (fr.getRenderOrder() != null)
444     {
445       // First add the checks in the previous render order,
446       // in case the window has been closed and reopened
447       List<String> rol = fr.getRenderOrder();
448       for (int ro = rol.size() - 1; ro > -1; ro--)
449       {
450         String item = rol.get(ro);
451
452         if (!displayableTypes.contains(item))
453         {
454           continue;
455         }
456
457         displayableTypes.remove(item);
458
459         addCheck(false, item);
460       }
461     }
462
463     /*
464      * now add checkboxes which should be visible,
465      * if they have not already been added
466      */
467     for (String type : displayableTypes)
468     {
469       addCheck(groupsChanged, type);
470     }
471
472     featurePanel.setLayout(
473             new GridLayout(featurePanel.getComponentCount(), 1, 10, 5));
474     featurePanel.validate();
475
476     if (scrollPane != null)
477     {
478       scrollPane.validate();
479     }
480
481     itemStateChanged(null);
482   }
483
484   /**
485    * Remove from the groups panel any checkboxes for groups that are not in the
486    * foundGroups set. This enables removing a group from the display when the
487    * last feature in that group is deleted.
488    * 
489    * @param foundGroups
490    */
491   protected void pruneGroups(Set<String> foundGroups)
492   {
493     for (int g = 0; g < groupPanel.getComponentCount(); g++)
494     {
495       Checkbox checkbox = (Checkbox) groupPanel.getComponent(g);
496       if (!foundGroups.contains(checkbox.getLabel()))
497       {
498         groupPanel.remove(checkbox);
499       }
500     }
501   }
502
503   /**
504    * update the checklist of feature types with the given type
505    * 
506    * @param groupsChanged
507    *          true means if the type is not in the display list then it will be
508    *          added and displayed
509    * @param type
510    *          feature type to be checked for in the list.
511    */
512   void addCheck(boolean groupsChanged, String type)
513   {
514     boolean addCheck;
515     Component[] comps = featurePanel.getComponents();
516     MyCheckbox check;
517     addCheck = true;
518     for (int i = 0; i < featurePanel.getComponentCount(); i++)
519     {
520       check = (MyCheckbox) comps[i];
521       if (check.type.equals(type))
522       {
523         addCheck = false;
524         break;
525       }
526     }
527
528     if (addCheck)
529     {
530       boolean selected = false;
531       if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
532       {
533         selected = true;
534       }
535
536       check = new MyCheckbox(type, selected, false,
537               fr.getFeatureStyle(type));
538
539       check.addMouseListener(this);
540       check.addMouseMotionListener(this);
541       check.addItemListener(this);
542       if (groupsChanged)
543       {
544         // add at beginning of stack.
545         featurePanel.add(check, 0);
546       }
547       else
548       {
549         // add at end of stack.
550         featurePanel.add(check);
551       }
552     }
553   }
554
555   @Override
556   public void actionPerformed(ActionEvent evt)
557   {
558     for (int i = 0; i < featurePanel.getComponentCount(); i++)
559     {
560       Checkbox check = (Checkbox) featurePanel.getComponent(i);
561       check.setState(!check.getState());
562     }
563     selectionChanged(true);
564   }
565
566   private ItemListener groupItemListener = new ItemListener()
567   {
568     @Override
569     public void itemStateChanged(ItemEvent evt)
570     {
571       Checkbox source = (Checkbox) evt.getSource();
572       fr.setGroupVisibility(source.getLabel(), source.getState());
573       ap.seqPanel.seqCanvas.repaint();
574       if (ap.overviewPanel != null)
575       {
576         ap.overviewPanel.updateOverviewImage();
577       }
578       resetTable(true);
579       return;
580     };
581   };
582
583   @Override
584   public void itemStateChanged(ItemEvent evt)
585   {
586     selectionChanged(true);
587   }
588
589   void selectionChanged(boolean updateOverview)
590   {
591     Component[] comps = featurePanel.getComponents();
592     int cSize = comps.length;
593
594     /*
595      * temporary! leave column[2] empty - used for Filter in
596      * gui.FeatureSettings
597      */
598     int columnCount = 4;
599     Object[][] tmp = new Object[cSize][columnCount];
600     int tmpSize = 0;
601     for (int i = 0; i < cSize; i++)
602     {
603       MyCheckbox check = (MyCheckbox) comps[i];
604       tmp[tmpSize][TYPE_COLUMN /* 0 */] = check.type;
605       tmp[tmpSize][COLOUR_COLUMN /* 1 */] = fr.getFeatureStyle(check.type);
606       tmp[tmpSize][SHOW_COLUMN /* 3 */] = new Boolean(check.getState());
607       tmpSize++;
608     }
609
610     Object[][] data = new Object[tmpSize][columnCount];
611     System.arraycopy(tmp, 0, data, 0, tmpSize);
612
613     fr.setFeaturePriority(getTableAsBeans(data));
614
615     ap.paintAlignment(updateOverview, updateOverview);
616   }
617
618   /**
619    * Converts table data into an array of data beans
620    */
621   private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
622   {
623     FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
624     for (int i = 0; i < data.length; i++)
625     {
626       String type = (String) data[i][TYPE_COLUMN];
627       FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
628       Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
629       // feature filter set to null as not (yet) offered in applet
630       rowData[i] = new FeatureSettingsBean(type, colour, null, isShown);
631     }
632     return rowData;
633   }
634   MyCheckbox selectedCheck;
635
636   boolean dragging = false;
637
638   @Override
639   public void mouseDragged(MouseEvent evt)
640   {
641     if (((Component) evt.getSource()).getParent() != featurePanel)
642     {
643       return;
644     }
645     dragging = true;
646   }
647
648   @Override
649   public void mouseReleased(MouseEvent evt)
650   {
651     if (((Component) evt.getSource()).getParent() != featurePanel)
652     {
653       return;
654     }
655
656     Component comp = null;
657     Checkbox target = null;
658
659     int height = evt.getY() + evt.getComponent().getLocation().y;
660
661     if (height > featurePanel.getSize().height)
662     {
663
664       comp = featurePanel
665               .getComponent(featurePanel.getComponentCount() - 1);
666     }
667     else if (height < 0)
668     {
669       comp = featurePanel.getComponent(0);
670     }
671     else
672     {
673       comp = featurePanel.getComponentAt(evt.getX(),
674               evt.getY() + evt.getComponent().getLocation().y);
675     }
676
677     if (comp != null && comp instanceof Checkbox)
678     {
679       target = (Checkbox) comp;
680     }
681
682     if (selectedCheck != null && target != null && selectedCheck != target)
683     {
684       int targetIndex = -1;
685       for (int i = 0; i < featurePanel.getComponentCount(); i++)
686       {
687         if (target == featurePanel.getComponent(i))
688         {
689           targetIndex = i;
690           break;
691         }
692       }
693
694       featurePanel.remove(selectedCheck);
695       featurePanel.add(selectedCheck, targetIndex);
696       featurePanel.validate();
697       itemStateChanged(null);
698     }
699   }
700
701   public void setUserColour(String feature, FeatureColourI originalColour)
702   {
703     fr.setColour(feature, originalColour);
704     refreshTable();
705   }
706
707   public void refreshTable()
708   {
709     featurePanel.removeAll();
710     resetTable(false);
711     ap.paintAlignment(true, true);
712   }
713
714   @Override
715   public void mouseEntered(MouseEvent evt)
716   {
717   }
718
719   @Override
720   public void mouseExited(MouseEvent evt)
721   {
722   }
723
724   @Override
725   public void mouseClicked(MouseEvent evt)
726   {
727     MyCheckbox check = (MyCheckbox) evt.getSource();
728     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
729     {
730       this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
731     }
732
733     if (check.getParent() != featurePanel)
734     {
735       return;
736     }
737
738     if (evt.getClickCount() > 1)
739     {
740       FeatureColourI fcol = fr.getFeatureStyle(check.type);
741       if (fcol.isSimpleColour())
742       {
743         new UserDefinedColours(this, check.type, fcol.getColour());
744       }
745       else
746       {
747         new FeatureColourChooser(this, check.type);
748         // write back the current colour object to update the table
749         check.updateColor(fr.getFeatureStyle(check.type));
750       }
751     }
752   }
753
754   @Override
755   public void mouseMoved(MouseEvent evt)
756   {
757   }
758
759   @Override
760   public void adjustmentValueChanged(AdjustmentEvent evt)
761   {
762     fr.setTransparency((100 - transparency.getValue()) / 100f);
763     ap.paintAlignment(true, true);
764   }
765
766   class MyCheckbox extends Checkbox
767   {
768     public String type;
769
770     public int stringWidth;
771
772     boolean hasLink;
773
774     FeatureColourI col;
775
776     public void updateColor(FeatureColourI newcol)
777     {
778       col = newcol;
779       if (col.isSimpleColour())
780       {
781         setBackground(col.getColour());
782       }
783       else
784       {
785         String vlabel = type;
786         if (col.isAboveThreshold())
787         {
788           vlabel += " (>)";
789         }
790         else if (col.isBelowThreshold())
791         {
792           vlabel += " (<)";
793         }
794         if (col.isColourByLabel())
795         {
796           setBackground(Color.white);
797           vlabel += " (by Label)";
798         }
799         else
800         {
801           setBackground(col.getMinColour());
802         }
803         this.setLabel(vlabel);
804       }
805       repaint();
806     }
807
808     public MyCheckbox(String label, boolean checked, boolean haslink)
809     {
810       super(label, checked);
811       type = label;
812       FontMetrics fm = av.nullFrame.getFontMetrics(av.nullFrame.getFont());
813       stringWidth = fm.stringWidth(label);
814       this.hasLink = haslink;
815     }
816
817     public MyCheckbox(String type, boolean selected, boolean b,
818             FeatureColourI featureStyle)
819     {
820       this(type, selected, b);
821       updateColor(featureStyle);
822     }
823
824     @Override
825     public void paint(Graphics g)
826     {
827       Dimension d = getSize();
828       if (col != null)
829       {
830         if (col.isColourByLabel())
831         {
832           g.setColor(Color.white);
833           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
834           /*
835            * g.setColor(Color.black); Font f=g.getFont().deriveFont(9);
836            * g.setFont(f);
837            * 
838            * // g.setFont(g.getFont().deriveFont( //
839            * AffineTransform.getScaleInstance( //
840            * width/g.getFontMetrics().stringWidth("Label"), //
841            * height/g.getFontMetrics().getHeight()))); g.drawString("Label",
842            * width/2, 0);
843            */
844
845         }
846         else if (col.isGraduatedColour())
847         {
848           Color maxCol = col.getMaxColour();
849           g.setColor(maxCol);
850           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
851
852         }
853       }
854
855       if (hasLink)
856       {
857         g.drawImage(linkImage, stringWidth + 25,
858                 (getSize().height - linkImage.getHeight(this)) / 2, this);
859       }
860     }
861   }
862
863   /**
864    * Hide columns containing (or not containing) a given feature type
865    * 
866    * @param type
867    * @param columnsContaining
868    */
869   void hideFeatureColumns(final String type, boolean columnsContaining)
870   {
871     if (ap.alignFrame.avc.markColumnsContainingFeatures(columnsContaining,
872             false, false, type))
873     {
874       if (ap.alignFrame.avc.markColumnsContainingFeatures(
875               !columnsContaining, false, false, type))
876       {
877         ap.alignFrame.viewport.hideSelectedColumns();
878       }
879     }
880   }
881
882   @Override
883   public void mousePressed(MouseEvent e)
884   {
885     // TODO Auto-generated method stub
886
887   }
888
889 }