Clover icon

Coverage Report

  1. Project Clover database Wed Nov 6 2024 00:56:24 GMT
  2. Package jalview.gui

File AnnotationLabels.java

 

Coverage histogram

../../img/srcFileCovDistChart4.png
48% of files have more coverage

Code metrics

280
547
42
1
1,637
1,252
228
0.42
13.02
42
5.43

Classes

Class Line # Actions
AnnotationLabels 77 547 228
0.3348676633.5%
 

Contributing tests

This file is covered by 203 tests. .

Source view

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.gui;
22   
23    import java.awt.Canvas;
24    import java.awt.Color;
25    import java.awt.Cursor;
26    import java.awt.Dimension;
27    import java.awt.Font;
28    import java.awt.FontMetrics;
29    import java.awt.Graphics;
30    import java.awt.Graphics2D;
31    import java.awt.RenderingHints;
32    import java.awt.Toolkit;
33    import java.awt.datatransfer.StringSelection;
34    import java.awt.event.ActionEvent;
35    import java.awt.event.ActionListener;
36    import java.awt.event.MouseEvent;
37    import java.awt.event.MouseListener;
38    import java.awt.event.MouseMotionListener;
39    import java.awt.geom.AffineTransform;
40    import java.util.Arrays;
41    import java.util.Collections;
42    import java.util.Iterator;
43    import java.util.Locale;
44   
45    import javax.swing.JCheckBoxMenuItem;
46    import javax.swing.JMenuItem;
47    import javax.swing.JPanel;
48    import javax.swing.JPopupMenu;
49    import javax.swing.SwingUtilities;
50    import javax.swing.ToolTipManager;
51   
52    import jalview.analysis.AlignSeq;
53    import jalview.analysis.AlignmentUtils;
54    import jalview.bin.Cache;
55    import jalview.bin.Jalview;
56    import jalview.datamodel.Alignment;
57    import jalview.datamodel.AlignmentAnnotation;
58    import jalview.datamodel.Annotation;
59    import jalview.datamodel.ContactMatrixI;
60    import jalview.datamodel.GroupSet;
61    import jalview.datamodel.HiddenColumns;
62    import jalview.datamodel.Sequence;
63    import jalview.datamodel.SequenceGroup;
64    import jalview.datamodel.SequenceI;
65    import jalview.io.FileFormat;
66    import jalview.io.FormatAdapter;
67    import jalview.util.Comparison;
68    import jalview.util.MessageManager;
69    import jalview.util.ParseHtmlBodyAndLinks;
70    import jalview.util.Platform;
71   
72    /**
73    * The panel that holds the labels for alignment annotations, providing
74    * tooltips, context menus, drag to reorder rows, and drag to adjust panel
75    * height
76    */
 
77    public class AnnotationLabels extends JPanel
78    implements MouseListener, MouseMotionListener, ActionListener
79    {
80    private static final String HTML_END_TAG = "</html>";
81   
82    private static final String HTML_START_TAG = "<html>";
83   
84    /**
85    * width in pixels within which height adjuster arrows are shown and active
86    */
87    private static final int HEIGHT_ADJUSTER_WIDTH = 50;
88   
89    /**
90    * height in pixels for allowing height adjuster to be active
91    */
92    public static int HEIGHT_ADJUSTER_HEIGHT = 10;
93   
94    private static final Font font = new Font("Arial", Font.PLAIN, 11);
95   
96    private static final String TOGGLE_LABELSCALE = MessageManager
97    .getString("label.scale_label_to_column");
98   
99    private static final String ADDNEW = MessageManager
100    .getString("label.add_new_row");
101   
102    private static final String EDITNAME = MessageManager
103    .getString("label.edit_label_description");
104   
105    private static final String HIDE = MessageManager
106    .getString("label.hide_row");
107   
108    private static final String DELETE = MessageManager
109    .getString("label.delete_row");
110   
111    private static final String SHOWALL = MessageManager
112    .getString("label.show_all_hidden_rows");
113   
114    private static final String OUTPUT_TEXT = MessageManager
115    .getString("label.export_annotation");
116   
117    private static final String COPYCONS_SEQ = MessageManager
118    .getString("label.copy_consensus_sequence");
119   
120    private static final String ADJUST_ANNOTATION_LABELS_WIDTH_PREF = "ADJUST_ANNOTATION_LABELS_WIDTH";
121   
122    private final boolean debugRedraw = false;
123   
124    private AlignmentPanel ap;
125   
126    AlignViewport av;
127   
128    private MouseEvent dragEvent;
129   
130    private int oldY;
131   
132    private int selectedRow;
133   
134    private int scrollOffset = 0;
135   
136    private boolean hasHiddenRows;
137   
138    private boolean resizePanel = false;
139   
140    private int annotationIdWidth = -1;
141   
142    public static final String RESIZE_MARGINS_MARK_PREF = "RESIZE_MARGINS_MARK";
143   
144    /**
145    * Creates a new AnnotationLabels object
146    *
147    * @param ap
148    */
 
149  478 toggle public AnnotationLabels(AlignmentPanel ap)
150    {
151  478 this.ap = ap;
152  478 av = ap.av;
153  478 ToolTipManager.sharedInstance().registerComponent(this);
154   
155  478 addMouseListener(this);
156  478 addMouseMotionListener(this);
157  478 addMouseWheelListener(ap.getAnnotationPanel());
158    }
159   
 
160  220 toggle public AnnotationLabels(AlignViewport av)
161    {
162  220 this.av = av;
163    }
164   
165    /**
166    * DOCUMENT ME!
167    *
168    * @param y
169    * DOCUMENT ME!
170    */
 
171  908 toggle public void setScrollOffset(int y)
172    {
173  908 scrollOffset = y;
174  908 repaint();
175    }
176   
177    /**
178    * sets selectedRow to -2 if no annotation preset, -1 if no visible row is at
179    * y
180    *
181    * @param y
182    * coordinate position to search for a row
183    */
 
184  0 toggle void getSelectedRow(int y)
185    {
186  0 int height = 0;
187  0 AlignmentAnnotation[] aa = ap.av.getAlignment()
188    .getAlignmentAnnotation();
189  0 selectedRow = -2;
190  0 if (aa != null)
191    {
192  0 for (int i = 0; i < aa.length; i++)
193    {
194  0 selectedRow = -1;
195  0 if (!aa[i].isForDisplay())
196    {
197  0 continue;
198    }
199   
200  0 height += aa[i].height;
201   
202  0 if (y < height)
203    {
204  0 selectedRow = i;
205   
206  0 break;
207    }
208    }
209    }
210    }
211   
212    /**
213    * DOCUMENT ME!
214    *
215    * @param evt
216    * DOCUMENT ME!
217    */
 
218  0 toggle @Override
219    public void actionPerformed(ActionEvent evt)
220    {
221  0 AlignmentAnnotation[] aa = ap.av.getAlignment()
222    .getAlignmentAnnotation();
223   
224  0 String action = evt.getActionCommand();
225  0 if (ADDNEW.equals(action))
226    {
227    /*
228    * non-returning dialog
229    */
230  0 AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null,
231    null, new Annotation[ap.av.getAlignment().getWidth()]);
232  0 editLabelDescription(newAnnotation, true);
233    }
234  0 else if (EDITNAME.equals(action))
235    {
236    /*
237    * non-returning dialog
238    */
239  0 editLabelDescription(aa[selectedRow], false);
240    }
241  0 else if (HIDE.equals(action))
242    {
243  0 aa[selectedRow].visible = false;
244    }
245  0 else if (DELETE.equals(action))
246    {
247  0 ap.av.getAlignment().deleteAnnotation(aa[selectedRow]);
248  0 ap.av.getCalcManager().removeWorkerForAnnotation(aa[selectedRow]);
249    }
250  0 else if (SHOWALL.equals(action))
251    {
252  0 for (int i = 0; i < aa.length; i++)
253    {
254  0 if (!aa[i].visible && aa[i].annotations != null)
255    {
256  0 aa[i].visible = true;
257    }
258    }
259    }
260  0 else if (OUTPUT_TEXT.equals(action))
261    {
262  0 new AnnotationExporter(ap).exportAnnotation(aa[selectedRow]);
263    }
264  0 else if (COPYCONS_SEQ.equals(action))
265    {
266  0 SequenceI cons = null;
267  0 if (aa[selectedRow].groupRef != null)
268    {
269  0 cons = aa[selectedRow].groupRef.getConsensusSeq();
270    }
271    else
272    {
273  0 cons = av.getConsensusSeq();
274    }
275  0 if (cons != null)
276    {
277  0 copy_annotseqtoclipboard(cons);
278    }
279    }
280  0 else if (TOGGLE_LABELSCALE.equals(action))
281    {
282  0 aa[selectedRow].scaleColLabel = !aa[selectedRow].scaleColLabel;
283    }
284   
285  0 ap.refresh(true);
286    }
287   
288    /**
289    * Shows a dialog where the annotation name and description may be edited. If
290    * parameter addNew is true, then on confirmation, a new AlignmentAnnotation
291    * is added, else an existing annotation is updated.
292    *
293    * @param annotation
294    * @param addNew
295    */
 
296  0 toggle void editLabelDescription(AlignmentAnnotation annotation, boolean addNew)
297    {
298  0 String name = MessageManager.getString("label.annotation_name");
299  0 String description = MessageManager
300    .getString("label.annotation_description");
301  0 String title = MessageManager
302    .getString("label.edit_annotation_name_description");
303  0 EditNameDialog dialog = new EditNameDialog(annotation.label,
304    annotation.description, name, description);
305   
306  0 dialog.showDialog(ap.alignFrame, title, () -> {
307  0 annotation.label = dialog.getName();
308  0 String text = dialog.getDescription();
309  0 if (text != null && text.length() == 0)
310    {
311  0 text = null;
312    }
313  0 annotation.description = text;
314  0 if (addNew)
315    {
316  0 ap.av.getAlignment().addAnnotation(annotation);
317  0 ap.av.getAlignment().setAnnotationIndex(annotation, 0);
318    }
319  0 ap.refresh(true);
320    });
321    }
322   
 
323  0 toggle @Override
324    public void mousePressed(MouseEvent evt)
325    {
326  0 getSelectedRow(evt.getY() - getScrollOffset());
327  0 oldY = evt.getY();
328  0 if (evt.isPopupTrigger())
329    {
330  0 showPopupMenu(evt);
331    }
332    }
333   
334    /**
335    * Build and show the Pop-up menu at the right-click mouse position
336    *
337    * @param evt
338    */
 
339  0 toggle void showPopupMenu(MouseEvent evt)
340    {
341  0 evt.consume();
342  0 final AlignmentAnnotation[] aa = ap.av.getAlignment()
343    .getAlignmentAnnotation();
344   
345  0 JPopupMenu pop = new JPopupMenu(
346    MessageManager.getString("label.annotations"));
347  0 JMenuItem item = new JMenuItem(ADDNEW);
348  0 item.addActionListener(this);
349  0 pop.add(item);
350  0 if (selectedRow < 0)
351    {
352  0 if (hasHiddenRows)
353    { // let the user make everything visible again
354  0 item = new JMenuItem(SHOWALL);
355  0 item.addActionListener(this);
356  0 pop.add(item);
357    }
358  0 pop.show(this, evt.getX(), evt.getY());
359  0 return;
360    }
361  0 item = new JMenuItem(EDITNAME);
362  0 item.addActionListener(this);
363  0 pop.add(item);
364  0 item = new JMenuItem(HIDE);
365  0 item.addActionListener(this);
366  0 pop.add(item);
367    // JAL-1264 hide all sequence-specific annotations of this type
368  0 if (selectedRow < aa.length)
369    {
370  0 if (aa[selectedRow].sequenceRef != null)
371    {
372  0 final String label = aa[selectedRow].label;
373  0 JMenuItem hideType = new JMenuItem();
374  0 String text = MessageManager.getString("label.hide_all") + " "
375    + label;
376  0 hideType.setText(text);
377  0 hideType.addActionListener(new ActionListener()
378    {
 
379  0 toggle @Override
380    public void actionPerformed(ActionEvent e)
381    {
382  0 AlignmentUtils.showOrHideSequenceAnnotations(
383    ap.av.getAlignment(), Collections.singleton(label),
384    null, false, false);
385  0 ap.refresh(true);
386    }
387    });
388  0 pop.add(hideType);
389    }
390    }
391  0 item = new JMenuItem(DELETE);
392  0 item.addActionListener(this);
393  0 pop.add(item);
394  0 if (hasHiddenRows)
395    {
396  0 item = new JMenuItem(SHOWALL);
397  0 item.addActionListener(this);
398  0 pop.add(item);
399    }
400  0 item = new JMenuItem(OUTPUT_TEXT);
401  0 item.addActionListener(this);
402  0 pop.add(item);
403    // TODO: annotation object should be typed for autocalculated/derived
404    // property methods
405  0 if (selectedRow < aa.length)
406    {
407  0 final String label = aa[selectedRow].label;
408  0 if (!aa[selectedRow].autoCalculated)
409    {
410  0 if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
411    {
412    // display formatting settings for this row.
413  0 pop.addSeparator();
414    // av and sequencegroup need to implement same interface for
415  0 item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE,
416    aa[selectedRow].scaleColLabel);
417  0 item.addActionListener(this);
418  0 pop.add(item);
419    }
420    }
421  0 else if (label.indexOf("Consensus") > -1)
422    {
423  0 addConsensusMenuOptions(ap, aa[selectedRow], pop);
424   
425  0 final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
426  0 consclipbrd.addActionListener(this);
427  0 pop.add(consclipbrd);
428    }
429   
430  0 addColourOrFilterByOptions(ap, aa[selectedRow], pop);
431   
432  0 if (aa[selectedRow].graph == AlignmentAnnotation.CONTACT_MAP)
433    {
434  0 addContactMatrixOptions(ap, aa[selectedRow], pop);
435    // Set/adjust threshold for grouping ?
436    // colour alignment by this [type]
437    // select/hide columns by this row
438   
439    }
440    }
441   
442  0 pop.show(this, evt.getX(), evt.getY());
443    }
444   
 
445  0 toggle static void addColourOrFilterByOptions(final AlignmentPanel ap,
446    final AlignmentAnnotation alignmentAnnotation,
447    final JPopupMenu pop)
448    {
449  0 JMenuItem item;
450  0 item = new JMenuItem(
451    MessageManager.getString("label.colour_by_annotation"));
452  0 item.addActionListener(new ActionListener()
453    {
454   
 
455  0 toggle @Override
456    public void actionPerformed(ActionEvent e)
457    {
458  0 AnnotationColourChooser.displayFor(ap.av, ap, alignmentAnnotation,
459    false);
460    };
461    });
462  0 pop.add(item);
463  0 if (alignmentAnnotation.sequenceRef != null)
464    {
465  0 item = new JMenuItem(
466    MessageManager.getString("label.colour_by_annotation") + " ("
467    + MessageManager.getString("label.per_seq") + ")");
468  0 item.addActionListener(new ActionListener()
469    {
 
470  0 toggle @Override
471    public void actionPerformed(ActionEvent e)
472    {
473  0 AnnotationColourChooser.displayFor(ap.av, ap, alignmentAnnotation,
474    true);
475    };
476    });
477  0 pop.add(item);
478    }
479  0 item = new JMenuItem(
480    MessageManager.getString("action.select_by_annotation"));
481  0 item.addActionListener(new ActionListener()
482    {
483   
 
484  0 toggle @Override
485    public void actionPerformed(ActionEvent e)
486    {
487  0 AnnotationColumnChooser.displayFor(ap.av, ap, alignmentAnnotation);
488    };
489    });
490  0 pop.add(item);
491    }
492   
 
493  0 toggle static void addContactMatrixOptions(final AlignmentPanel ap,
494    final AlignmentAnnotation alignmentAnnotation,
495    final JPopupMenu pop)
496    {
497   
498  0 final ContactMatrixI cm = ap.av.getContactMatrix(alignmentAnnotation);
499  0 JMenuItem item;
500  0 if (cm != null)
501    {
502  0 pop.addSeparator();
503   
504  0 if (cm.hasGroups())
505    {
506  0 JCheckBoxMenuItem chitem = new JCheckBoxMenuItem(
507    MessageManager.getString("action.show_groups_on_matrix"));
508  0 chitem.setToolTipText(MessageManager
509    .getString("action.show_groups_on_matrix_tooltip"));
510  0 boolean showGroups = alignmentAnnotation
511    .isShowGroupsForContactMatrix();
512  0 final AlignmentAnnotation sel_row = alignmentAnnotation;
513  0 chitem.setState(showGroups);
514  0 chitem.addActionListener(new ActionListener()
515    {
516   
 
517  0 toggle @Override
518    public void actionPerformed(ActionEvent e)
519    {
520  0 sel_row.setShowGroupsForContactMatrix(chitem.getState());
521    // so any annotation colour changes are propagated - though they
522    // probably won't be unless the annotation row colours are removed
523    // too!
524  0 ap.alignmentChanged();
525    }
526    });
527  0 pop.add(chitem);
528    }
529  0 if (cm.hasTree())
530    {
531  0 item = new JMenuItem(
532    MessageManager.getString("action.show_tree_for_matrix"));
533  0 item.setToolTipText(MessageManager
534    .getString("action.show_tree_for_matrix_tooltip"));
535  0 item.addActionListener(new ActionListener()
536    {
537   
 
538  0 toggle @Override
539    public void actionPerformed(ActionEvent e)
540    {
541   
542  0 ap.alignFrame.showContactMapTree(alignmentAnnotation, cm);
543   
544    }
545    });
546  0 pop.add(item);
547    }
548    else
549    {
550  0 item = new JMenuItem(
551    MessageManager.getString("action.cluster_matrix"));
552  0 item.setToolTipText(
553    MessageManager.getString("action.cluster_matrix_tooltip"));
554  0 item.addActionListener(new ActionListener()
555    {
 
556  0 toggle @Override
557    public void actionPerformed(ActionEvent e)
558    {
559  0 new Thread(new Runnable()
560    {
 
561  0 toggle @Override
562    public void run()
563    {
564  0 final long progBar;
565  0 ap.alignFrame.setProgressBar(
566    MessageManager.formatMessage(
567    "action.clustering_matrix_for",
568    cm.getAnnotDescr(), 5f),
569    progBar = System.currentTimeMillis());
570  0 cm.setGroupSet(GroupSet.makeGroups(cm, true));
571  0 cm.randomlyReColourGroups();
572  0 cm.transferGroupColorsTo(alignmentAnnotation);
573  0 ap.alignmentChanged();
574  0 ap.alignFrame.showContactMapTree(alignmentAnnotation, cm);
575  0 ap.alignFrame.setProgressBar(null, progBar);
576    }
577    }).start();
578    }
579    });
580  0 pop.add(item);
581    }
582    }
583    }
584   
585    /**
586    * A helper method that adds menu options for calculation and visualisation of
587    * group and/or alignment consensus annotation to a popup menu. This is
588    * designed to be reusable for either unwrapped mode (popup menu is shown on
589    * component AnnotationLabels), or wrapped mode (popup menu is shown on
590    * IdPanel when the mouse is over an annotation label).
591    *
592    * @param ap
593    * @param ann
594    * @param pop
595    */
 
596  0 toggle static void addConsensusMenuOptions(AlignmentPanel ap,
597    AlignmentAnnotation ann, JPopupMenu pop)
598    {
599  0 pop.addSeparator();
600   
601  0 final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
602    MessageManager.getString("label.ignore_gaps_consensus"),
603  0 (ann.groupRef != null) ? ann.groupRef.getIgnoreGapsConsensus()
604    : ap.av.isIgnoreGapsConsensus());
605  0 final AlignmentAnnotation aaa = ann;
606  0 cbmi.addActionListener(new ActionListener()
607    {
 
608  0 toggle @Override
609    public void actionPerformed(ActionEvent e)
610    {
611  0 if (aaa.groupRef != null)
612    {
613  0 aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
614  0 ap.getAnnotationPanel()
615    .paint(ap.getAnnotationPanel().getGraphics());
616    }
617    else
618    {
619  0 ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
620    }
621  0 ap.alignmentChanged();
622    }
623    });
624  0 pop.add(cbmi);
625   
626  0 if (aaa.groupRef != null)
627    {
628    /*
629    * group consensus options
630    */
631  0 final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
632    MessageManager.getString("label.show_group_histogram"),
633    ann.groupRef.isShowConsensusHistogram());
634  0 chist.addActionListener(new ActionListener()
635    {
 
636  0 toggle @Override
637    public void actionPerformed(ActionEvent e)
638    {
639  0 aaa.groupRef.setShowConsensusHistogram(chist.getState());
640  0 ap.repaint();
641    }
642    });
643  0 pop.add(chist);
644  0 final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
645    MessageManager.getString("label.show_group_logo"),
646    ann.groupRef.isShowSequenceLogo());
647  0 cprofl.addActionListener(new ActionListener()
648    {
 
649  0 toggle @Override
650    public void actionPerformed(ActionEvent e)
651    {
652  0 aaa.groupRef.setshowSequenceLogo(cprofl.getState());
653  0 ap.repaint();
654    }
655    });
656  0 pop.add(cprofl);
657  0 final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
658    MessageManager.getString("label.normalise_group_logo"),
659    ann.groupRef.isNormaliseSequenceLogo());
660  0 cproflnorm.addActionListener(new ActionListener()
661    {
 
662  0 toggle @Override
663    public void actionPerformed(ActionEvent e)
664    {
665  0 aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState());
666    // automatically enable logo display if we're clicked
667  0 aaa.groupRef.setshowSequenceLogo(true);
668  0 ap.repaint();
669    }
670    });
671  0 pop.add(cproflnorm);
672    }
673    else
674    {
675    /*
676    * alignment consensus options
677    */
678  0 final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
679    MessageManager.getString("label.show_histogram"),
680    ap.av.isShowConsensusHistogram());
681  0 chist.addActionListener(new ActionListener()
682    {
 
683  0 toggle @Override
684    public void actionPerformed(ActionEvent e)
685    {
686  0 ap.av.setShowConsensusHistogram(chist.getState());
687  0 ap.alignFrame.setMenusForViewport();
688  0 ap.repaint();
689    }
690    });
691  0 pop.add(chist);
692  0 final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
693    MessageManager.getString("label.show_logo"),
694    ap.av.isShowSequenceLogo());
695  0 cprof.addActionListener(new ActionListener()
696    {
 
697  0 toggle @Override
698    public void actionPerformed(ActionEvent e)
699    {
700  0 ap.av.setShowSequenceLogo(cprof.getState());
701  0 ap.alignFrame.setMenusForViewport();
702  0 ap.repaint();
703    }
704    });
705  0 pop.add(cprof);
706  0 final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
707    MessageManager.getString("label.normalise_logo"),
708    ap.av.isNormaliseSequenceLogo());
709  0 cprofnorm.addActionListener(new ActionListener()
710    {
 
711  0 toggle @Override
712    public void actionPerformed(ActionEvent e)
713    {
714  0 ap.av.setShowSequenceLogo(true);
715  0 ap.av.setNormaliseSequenceLogo(cprofnorm.getState());
716  0 ap.alignFrame.setMenusForViewport();
717  0 ap.repaint();
718    }
719    });
720  0 pop.add(cprofnorm);
721    }
722    }
723   
724    /**
725    * Reorders annotation rows after a drag of a label
726    *
727    * @param evt
728    */
 
729  0 toggle @Override
730    public void mouseReleased(MouseEvent evt)
731    {
732  0 if (evt.isPopupTrigger())
733    {
734  0 showPopupMenu(evt);
735  0 return;
736    }
737   
738  0 int start = selectedRow;
739  0 getSelectedRow(evt.getY() - getScrollOffset());
740  0 int end = selectedRow;
741   
742    /*
743    * if dragging to resize instead, start == end
744    */
745  0 if (start != end)
746    {
747    // Swap these annotations
748  0 AlignmentAnnotation startAA = ap.av.getAlignment()
749    .getAlignmentAnnotation()[start];
750  0 if (end == -1)
751    {
752  0 end = ap.av.getAlignment().getAlignmentAnnotation().length - 1;
753    }
754  0 AlignmentAnnotation endAA = ap.av.getAlignment()
755    .getAlignmentAnnotation()[end];
756   
757  0 ap.av.getAlignment().getAlignmentAnnotation()[end] = startAA;
758  0 ap.av.getAlignment().getAlignmentAnnotation()[start] = endAA;
759    }
760   
761  0 resizePanel = false;
762  0 dragEvent = null;
763  0 repaint();
764  0 ap.getAnnotationPanel().repaint();
765    }
766   
767    /**
768    * Removes the height adjuster image on leaving the panel, unless currently
769    * dragging it
770    */
 
771  0 toggle @Override
772    public void mouseExited(MouseEvent evt)
773    {
774  0 if (resizePanel && dragEvent == null)
775    {
776  0 resizePanel = false;
777  0 repaint();
778    }
779    }
780   
781    /**
782    * A mouse drag may be either an adjustment of the panel height (if flag
783    * resizePanel is set on), or a reordering of the annotation rows. The former
784    * is dealt with by this method, the latter in mouseReleased.
785    *
786    * @param evt
787    */
 
788  0 toggle @Override
789    public void mouseDragged(MouseEvent evt)
790    {
791  0 dragEvent = evt;
792   
793  0 if (resizePanel)
794    {
795  0 Dimension d = ap.annotationScroller.getPreferredSize();
796  0 int dif = evt.getY() - oldY;
797  0 dif -= dif % ap.av.getCharHeight();
798   
799    // don't allow setting an annotation panel height larger than visible
800    // (otherwise you can't get back)
801  0 if (d.height - dif > ap.idPanelHolder.getHeight()
802    - ap.getIdSpaceFillerPanel1().getHeight())
803    {
804  0 return;
805    }
806   
807  0 if ((d.height - dif) > 20)
808    {
809  0 ap.annotationScroller
810    .setPreferredSize(new Dimension(d.width, d.height - dif));
811  0 d = ap.annotationSpaceFillerHolder.getPreferredSize();
812  0 ap.annotationSpaceFillerHolder
813    .setPreferredSize(new Dimension(d.width, d.height - dif));
814  0 ap.paintAlignment(true, false);
815    }
816   
817  0 ap.addNotify();
818    }
819    else
820    {
821  0 repaint();
822    }
823    }
824   
825    /**
826    * Updates the tooltip as the mouse moves over the labels
827    *
828    * @param evt
829    */
 
830  0 toggle @Override
831    public void mouseMoved(MouseEvent evt)
832    {
833  0 showOrHideAdjuster(evt);
834   
835  0 getSelectedRow(evt.getY() - getScrollOffset());
836   
837  0 if (selectedRow > -1 && ap.av.getAlignment()
838    .getAlignmentAnnotation().length > selectedRow)
839    {
840  0 AlignmentAnnotation[] anns = ap.av.getAlignment()
841    .getAlignmentAnnotation();
842  0 AlignmentAnnotation aa = anns[selectedRow];
843   
844  0 String desc = getTooltip(aa);
845  0 this.setToolTipText(desc);
846  0 String msg = getStatusMessage(aa, anns);
847  0 ap.alignFrame.setStatus(msg);
848    }
849    }
850   
851    /**
852    * Constructs suitable text to show in the status bar when over an annotation
853    * label, containing the associated sequence name (if any), and the annotation
854    * labels (or all labels for a graph group annotation)
855    *
856    * @param aa
857    * @param anns
858    * @return
859    */
 
860  6 toggle static String getStatusMessage(AlignmentAnnotation aa,
861    AlignmentAnnotation[] anns)
862    {
863  6 if (aa == null)
864    {
865  1 return null;
866    }
867   
868  5 StringBuilder msg = new StringBuilder(32);
869  5 if (aa.sequenceRef != null)
870    {
871  3 msg.append(aa.sequenceRef.getName()).append(" : ");
872    }
873   
874  5 if (aa.graphGroup == -1)
875    {
876  2 msg.append(aa.label);
877    }
878  3 else if (anns != null)
879    {
880  3 boolean first = true;
881  9 for (int i = anns.length - 1; i >= 0; i--)
882    {
883  6 if (anns[i].graphGroup == aa.graphGroup)
884    {
885  5 if (!first)
886    {
887  2 msg.append(", ");
888    }
889  5 msg.append(anns[i].label);
890  5 first = false;
891    }
892    }
893    }
894   
895  5 return msg.toString();
896    }
897   
898    /**
899    * Answers a tooltip, formatted as html, containing the annotation description
900    * (prefixed by associated sequence id if applicable), and the annotation
901    * (non-positional) score if it has one. Answers null if neither description
902    * nor score is found.
903    *
904    * @param aa
905    * @return
906    */
 
907  13 toggle static String getTooltip(AlignmentAnnotation aa)
908    {
909  13 if (aa == null)
910    {
911  1 return null;
912    }
913  12 StringBuilder tooltip = new StringBuilder();
914  12 if (aa.description != null && !aa.description.equals("New description"))
915    {
916    // TODO: we could refactor and merge this code with the code in
917    // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature
918    // tooltips
919  10 String desc = aa.getDescription(true).trim();
920  10 if (!desc.toLowerCase(Locale.ROOT).startsWith(HTML_START_TAG))
921    {
922  7 tooltip.append(HTML_START_TAG);
923  7 desc = desc.replace("<", "&lt;");
924    }
925  3 else if (desc.toLowerCase(Locale.ROOT).endsWith(HTML_END_TAG))
926    {
927  3 desc = desc.substring(0, desc.length() - HTML_END_TAG.length());
928    }
929  10 tooltip.append(desc);
930    }
931    else
932    {
933    // begin the tooltip's html fragment
934  2 tooltip.append(HTML_START_TAG);
935    }
936  12 if (aa.hasScore())
937    {
938  5 if (tooltip.length() > HTML_START_TAG.length())
939    {
940  3 tooltip.append("<br/>");
941    }
942    // TODO: limit precision of score to avoid noise from imprecise
943    // doubles
944    // (64.7 becomes 64.7+/some tiny value).
945  5 tooltip.append(" Score: ").append(String.valueOf(aa.score));
946    }
947   
948  12 if (tooltip.length() > HTML_START_TAG.length())
949    {
950  10 return tooltip.append(HTML_END_TAG).toString();
951    }
952   
953    /*
954    * nothing in the tooltip (except "<html>")
955    */
956  2 return null;
957    }
958   
959    /**
960    * Shows the height adjuster image if the mouse moves into the top left
961    * region, or hides it if the mouse leaves the regio
962    *
963    * @param evt
964    */
 
965  0 toggle protected void showOrHideAdjuster(MouseEvent evt)
966    {
967  0 boolean was = resizePanel;
968  0 resizePanel = evt.getY() < HEIGHT_ADJUSTER_HEIGHT
969    && evt.getX() < HEIGHT_ADJUSTER_WIDTH;
970   
971  0 if (resizePanel != was)
972    {
973  0 setCursor(Cursor
974  0 .getPredefinedCursor(resizePanel ? Cursor.S_RESIZE_CURSOR
975    : Cursor.DEFAULT_CURSOR));
976  0 repaint();
977    }
978    }
979   
 
980  0 toggle @Override
981    public void mouseClicked(MouseEvent evt)
982    {
983  0 final AlignmentAnnotation[] aa = ap.av.getAlignment()
984    .getAlignmentAnnotation();
985  0 if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt))
986    {
987  0 if (selectedRow > -1 && selectedRow < aa.length)
988    {
989  0 if (aa[selectedRow].groupRef != null)
990    {
991  0 if (evt.getClickCount() >= 2)
992    {
993    // todo: make the ap scroll to the selection - not necessary, first
994    // click highlights/scrolls, second selects
995  0 ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null);
996    // process modifiers
997  0 SequenceGroup sg = ap.av.getSelectionGroup();
998  0 if (sg == null || sg == aa[selectedRow].groupRef
999    || !(Platform.isControlDown(evt) || evt.isShiftDown()))
1000    {
1001  0 if (Platform.isControlDown(evt) || evt.isShiftDown())
1002    {
1003    // clone a new selection group from the associated group
1004  0 ap.av.setSelectionGroup(
1005    new SequenceGroup(aa[selectedRow].groupRef));
1006    }
1007    else
1008    {
1009    // set selection to the associated group so it can be edited
1010  0 ap.av.setSelectionGroup(aa[selectedRow].groupRef);
1011    }
1012    }
1013    else
1014    {
1015    // modify current selection with associated group
1016  0 int remainToAdd = aa[selectedRow].groupRef.getSize();
1017  0 for (SequenceI sgs : aa[selectedRow].groupRef.getSequences())
1018    {
1019  0 if (jalview.util.Platform.isControlDown(evt))
1020    {
1021  0 sg.addOrRemove(sgs, --remainToAdd == 0);
1022    }
1023    else
1024    {
1025    // notionally, we should also add intermediate sequences from
1026    // last added sequence ?
1027  0 sg.addSequence(sgs, --remainToAdd == 0);
1028    }
1029    }
1030    }
1031   
1032  0 ap.paintAlignment(false, false);
1033  0 PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
1034  0 ap.av.sendSelection();
1035    }
1036    else
1037    {
1038  0 ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(
1039    aa[selectedRow].groupRef.getSequences(null));
1040    }
1041  0 return;
1042    }
1043  0 else if (aa[selectedRow].sequenceRef != null)
1044    {
1045  0 if (evt.getClickCount() == 1)
1046    {
1047  0 ap.getSeqPanel().ap.getIdPanel()
1048    .highlightSearchResults(Arrays.asList(new SequenceI[]
1049    { aa[selectedRow].sequenceRef }));
1050    }
1051  0 else if (evt.getClickCount() >= 2)
1052    {
1053  0 ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null);
1054  0 SequenceGroup sg = ap.av.getSelectionGroup();
1055  0 if (sg != null)
1056    {
1057    // we make a copy rather than edit the current selection if no
1058    // modifiers pressed
1059    // see Enhancement JAL-1557
1060  0 if (!(Platform.isControlDown(evt) || evt.isShiftDown()))
1061    {
1062  0 sg = new SequenceGroup(sg);
1063  0 sg.clear();
1064  0 sg.addSequence(aa[selectedRow].sequenceRef, false);
1065    }
1066    else
1067    {
1068  0 if (Platform.isControlDown(evt))
1069    {
1070  0 sg.addOrRemove(aa[selectedRow].sequenceRef, true);
1071    }
1072    else
1073    {
1074    // notionally, we should also add intermediate sequences from
1075    // last added sequence ?
1076  0 sg.addSequence(aa[selectedRow].sequenceRef, true);
1077    }
1078    }
1079    }
1080    else
1081    {
1082  0 sg = new SequenceGroup();
1083  0 sg.setStartRes(0);
1084  0 sg.setEndRes(ap.av.getAlignment().getWidth() - 1);
1085  0 sg.addSequence(aa[selectedRow].sequenceRef, false);
1086    }
1087  0 ap.av.setSelectionGroup(sg);
1088  0 ap.paintAlignment(false, false);
1089  0 PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
1090  0 ap.av.sendSelection();
1091    }
1092   
1093    }
1094    }
1095  0 return;
1096    }
1097    }
1098   
1099    /**
1100    * do a single sequence copy to jalview and the system clipboard
1101    *
1102    * @param sq
1103    * sequence to be copied to clipboard
1104    */
 
1105  0 toggle protected void copy_annotseqtoclipboard(SequenceI sq)
1106    {
1107  0 SequenceI[] seqs = new SequenceI[] { sq };
1108  0 String[] omitHidden = null;
1109  0 SequenceI[] dseqs = new SequenceI[] { sq.getDatasetSequence() };
1110  0 if (dseqs[0] == null)
1111    {
1112  0 dseqs[0] = new Sequence(sq);
1113  0 dseqs[0].setSequence(AlignSeq.extractGaps(Comparison.GapChars,
1114    sq.getSequenceAsString()));
1115   
1116  0 sq.setDatasetSequence(dseqs[0]);
1117    }
1118  0 Alignment ds = new Alignment(dseqs);
1119  0 if (av.hasHiddenColumns())
1120    {
1121  0 Iterator<int[]> it = av.getAlignment().getHiddenColumns()
1122    .getVisContigsIterator(0, sq.getLength() + 1, false);
1123  0 omitHidden = new String[] { sq.getSequenceStringFromIterator(it) };
1124    }
1125   
1126  0 int[] alignmentStartEnd = new int[] { 0, ds.getWidth() - 1 };
1127  0 if (av.hasHiddenColumns())
1128    {
1129  0 alignmentStartEnd = av.getAlignment().getHiddenColumns()
1130    .getVisibleStartAndEndIndex(av.getAlignment().getWidth());
1131    }
1132   
1133  0 String output = new FormatAdapter().formatSequences(FileFormat.Fasta,
1134    seqs, omitHidden, alignmentStartEnd);
1135   
1136  0 Toolkit.getDefaultToolkit().getSystemClipboard()
1137    .setContents(new StringSelection(output), Desktop.instance);
1138   
1139  0 HiddenColumns hiddenColumns = null;
1140   
1141  0 if (av.hasHiddenColumns())
1142    {
1143  0 hiddenColumns = new HiddenColumns(
1144    av.getAlignment().getHiddenColumns());
1145    }
1146   
1147  0 Desktop.jalviewClipboard = new Object[] { seqs, ds, // what is the dataset
1148    // of a consensus
1149    // sequence ? need to
1150    // flag
1151    // sequence as special.
1152    hiddenColumns };
1153    }
1154   
1155    /**
1156    * DOCUMENT ME!
1157    *
1158    * @param g1
1159    * DOCUMENT ME!
1160    */
 
1161  2132 toggle @Override
1162    public void paintComponent(Graphics g)
1163    {
1164   
1165  2132 int width = getWidth();
1166  2132 if (width == 0)
1167    {
1168  0 width = ap.calculateIdWidth().width;
1169    }
1170   
1171  2132 Graphics2D g2 = (Graphics2D) g;
1172  2132 if (av.antiAlias)
1173    {
1174  829 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1175    RenderingHints.VALUE_ANTIALIAS_ON);
1176    }
1177   
1178  2132 drawComponent(g2, true, width, true);
1179    }
1180   
1181    /**
1182    * Draw the full set of annotation Labels for the alignment at the given
1183    * cursor
1184    *
1185    * @param g
1186    * Graphics2D instance (needed for font scaling)
1187    * @param width
1188    * Width for scaling labels
1189    *
1190    */
 
1191  0 toggle public void drawComponent(Graphics g, int width)
1192    {
1193  0 drawComponent(g, false, width, true);
1194    }
1195   
1196    /**
1197    * Draw the full set of annotation Labels for the alignment at the given
1198    * cursor
1199    *
1200    * @param g
1201    * Graphics2D instance (needed for font scaling)
1202    * @param clip
1203    * - true indicates that only current visible area needs to be
1204    * rendered
1205    * @param width
1206    * Width for scaling labels
1207    */
 
1208  2383 toggle public void drawComponent(Graphics g, boolean clip, int givenWidth,
1209    boolean forGUI)
1210    {
1211  2383 int width = givenWidth;
1212  2383 IdwidthAdjuster iwa = null;
1213  2383 if (ap != null)
1214    {
1215  2170 iwa = ap.idwidthAdjuster;
1216  2170 if (Cache.getDefault(ADJUST_ANNOTATION_LABELS_WIDTH_PREF, true)
1217    || Jalview.isHeadlessMode())
1218    {
1219  2170 Graphics2D g2d = (Graphics2D) g;
1220  2170 Graphics dummy = g2d.create();
1221  2170 int newAnnotationIdWidth = drawLabels(dummy, clip, width, false,
1222    forGUI, null, false);
1223  2170 dummy.dispose();
1224  2170 Dimension d = ap.calculateDefaultAlignmentIdWidth();
1225  2170 int alignmentIdWidth = d.width;
1226  2170 if (iwa != null && !iwa.manuallyAdjusted())
1227    {
1228    // If no manual adjustment to ID column with has been made then adjust
1229    // width match widest of alignment or annotation id widths
1230  1101 boolean allowShrink = Cache.getDefault("ALLOW_SHRINK_ID_WIDTH",
1231    false);
1232  1101 width = Math.max(alignmentIdWidth, newAnnotationIdWidth);
1233  1101 if (clip && width < givenWidth && !allowShrink)
1234    {
1235  0 width = givenWidth;
1236    }
1237    }
1238  1069 else if (newAnnotationIdWidth != annotationIdWidth
1239    && newAnnotationIdWidth > givenWidth
1240    && newAnnotationIdWidth > alignmentIdWidth)
1241    {
1242    // otherwise if the annotation id width has become larger than the
1243    // current id width, increase
1244  13 width = newAnnotationIdWidth;
1245  13 annotationIdWidth = newAnnotationIdWidth;
1246    }
1247    // set the width if it's changed
1248  2170 if (width != ap.av.getIdWidth())
1249    {
1250  113 iwa.setWidth(width);
1251    }
1252    }
1253    }
1254    else
1255    {
1256  213 int newAnnotationIdWidth = drawLabels(g, clip, width, false, forGUI,
1257    null, false);
1258  213 width = newAnnotationIdWidth < givenWidth ? givenWidth
1259    : Math.min(newAnnotationIdWidth, givenWidth);
1260    }
1261  2383 drawLabels(g, clip, width, true, forGUI, null, false);
1262    }
1263   
1264    /**
1265    * Render the full set of annotation Labels for the alignment at the given
1266    * cursor. If actuallyDraw is false or g is null then no actual drawing will
1267    * occur, but the widest label width will be returned. If g is null then
1268    * fmetrics must be supplied.
1269    *
1270    * @param g
1271    * Graphics2D instance (used for rendering and font scaling if no
1272    * fmetrics supplied)
1273    * @param clip
1274    * - true indicates that only current visible area needs to be
1275    * rendered
1276    * @param width
1277    * Width for scaling labels
1278    * @param actuallyDraw
1279    * - when false, no graphics are rendered to g0
1280    * @param forGUI
1281    * - when false, GUI relevant marks like indicators for dragging
1282    * annotation panel height are not rendered
1283    * @param fmetrics
1284    * FontMetrics if Graphics object g is null
1285    * @param includeHidden
1286    * - when true returned width includes labels in hidden row width
1287    * calculation
1288    * @return the width of the annotation labels.
1289    */
 
1290  4856 toggle public int drawLabels(Graphics g0, boolean clip, int width,
1291    boolean actuallyDraw, boolean forGUI, FontMetrics fmetrics,
1292    boolean includeHidden)
1293    {
1294  4856 if (clip)
1295    {
1296  4264 clip = Cache.getDefault("MOVE_SEQUENCE_ID_WITH_VISIBLE_ANNOTATIONS",
1297    true);
1298    }
1299  4856 Graphics g = null;
1300    // create a dummy Graphics object if not drawing and one is supplied
1301  4856 if (g0 != null)
1302    {
1303  4773 if (!actuallyDraw)
1304    {
1305  2390 Graphics2D g2d = (Graphics2D) g0;
1306  2390 g = g2d.create();
1307    }
1308    else
1309    {
1310  2383 g = g0;
1311    }
1312    }
1313  4856 int actualWidth = 0;
1314  4856 if (g != null)
1315    {
1316  4773 if (av.getFont().getSize() < 10)
1317    {
1318  0 g.setFont(font);
1319    }
1320    else
1321    {
1322  4773 g.setFont(av.getFont());
1323    }
1324    }
1325   
1326  4856 FontMetrics fm = fmetrics == null ? g.getFontMetrics(g.getFont())
1327    : fmetrics;
1328  4856 if (actuallyDraw)
1329    {
1330  2383 g.setColor(Color.white);
1331  2383 g.fillRect(0, 0, getWidth(), getHeight());
1332   
1333  2383 if (!Cache.getDefault(RESIZE_MARGINS_MARK_PREF, false)
1334    && !av.getWrapAlignment() && forGUI)
1335    {
1336  2132 g.setColor(Color.LIGHT_GRAY);
1337  2132 g.drawLine(0, HEIGHT_ADJUSTER_HEIGHT / 4, HEIGHT_ADJUSTER_WIDTH / 4,
1338    HEIGHT_ADJUSTER_HEIGHT / 4);
1339  2132 g.drawLine(0, 3 * HEIGHT_ADJUSTER_HEIGHT / 4,
1340    HEIGHT_ADJUSTER_WIDTH / 4, 3 * HEIGHT_ADJUSTER_HEIGHT / 4);
1341   
1342    }
1343    }
1344   
1345  4856 if (actuallyDraw)
1346    {
1347  2383 g.translate(0, getScrollOffset());
1348  2383 g.setColor(Color.black);
1349    }
1350  4856 SequenceI lastSeqRef = null;
1351  4856 String lastLabel = null;
1352  4856 String lastDescription = null;
1353  4856 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
1354  4856 int fontHeight = g != null ? g.getFont().getSize()
1355    : fm.getFont().getSize();
1356  4856 int y = 0;
1357  4856 int x = 0;
1358  4856 int graphExtras = 0;
1359  4856 int offset = 0;
1360  4856 Font baseFont = g != null ? g.getFont() : fm.getFont();
1361  4856 FontMetrics baseMetrics = fm;
1362  4856 int ofontH = fontHeight;
1363  4856 int sOffset = 0;
1364  4856 int visHeight = 0;
1365  4856 int[] visr = (ap != null && ap.getAnnotationPanel() != null)
1366    ? ap.getAnnotationPanel().getVisibleVRange()
1367    : null;
1368  4856 if (clip && visr != null)
1369    {
1370  4264 sOffset = visr[0];
1371  4264 visHeight = visr[1];
1372    }
1373  4856 boolean visible = true, before = false, after = false;
1374  4856 if (aa != null)
1375    {
1376  4856 hasHiddenRows = false;
1377  4856 int olY = 0;
1378  4856 int nexAA = 0;
1379  30750 for (int i = 0; i < aa.length; i++)
1380    {
1381  25894 visible = true;
1382  25894 if (!aa[i].isForDisplay() && !includeHidden)
1383    {
1384  2206 hasHiddenRows = true;
1385  2206 continue;
1386    }
1387  23688 olY = y;
1388    // look ahead to next annotation
1389  23688 for (nexAA = i + 1; nexAA < aa.length
1390    && (!aa[nexAA].isForDisplay() && includeHidden); nexAA++)
1391  0 ;
1392  23688 y += aa[i].height;
1393  23688 if (clip)
1394    {
1395  20960 if (y < sOffset)
1396    {
1397  0 if (!before)
1398    {
1399  0 if (debugRedraw)
1400    {
1401  0 jalview.bin.Console.outPrintln("before vis: " + i);
1402    }
1403  0 before = true;
1404    }
1405    // don't draw what isn't visible
1406  0 continue;
1407    }
1408  20960 if (olY > visHeight)
1409    {
1410   
1411  1213 if (!after)
1412    {
1413  542 if (debugRedraw)
1414    {
1415  0 jalview.bin.Console.outPrintln(
1416    "Scroll offset: " + sOffset + " after vis: " + i);
1417    }
1418  542 after = true;
1419    }
1420    // don't draw what isn't visible
1421  1213 continue;
1422    }
1423    }
1424  22475 if (actuallyDraw && g != null)
1425    {
1426  11075 g.setColor(Color.black);
1427    }
1428  22475 offset = -aa[i].height / 2;
1429   
1430  22475 if (aa[i].hasText)
1431    {
1432  20777 offset += fm.getHeight() / 2;
1433  20777 offset -= fm.getDescent();
1434    }
1435    else
1436    {
1437  1698 offset += fm.getDescent();
1438    }
1439  22475 String label = aa[i].label;
1440  22475 ParseHtmlBodyAndLinks phbDecription = new ParseHtmlBodyAndLinks(
1441    aa[i].description, true, "\n");
1442  22475 String description = phbDecription.getNonHtmlContent();
1443  22475 boolean vertBar = false;
1444  22475 if (lastLabel != null && lastLabel.equals(label) &&
1445    lastDescription != null && lastDescription.equals(description))
1446    {
1447    //JAL-4427
1448  1 label = aa[i].label; // No change in label
1449    }
1450  22474 else if (lastLabel != null && lastLabel.equals(label))
1451    {
1452    //JAL-4427
1453  5 if(!(lastDescription != null && lastDescription.equals(description))
1454    && (aa[i].sequenceRef == lastSeqRef)) {
1455  4 label = description;
1456  4 lastDescription = description;
1457    }
1458    }
1459    else
1460    {
1461  22469 if (nexAA < aa.length && label.equals(aa[nexAA].label)) // &&
1462    // aa[nexY].sequenceRef==aa[i].sequenceRef)
1463    {
1464  5 lastLabel = label;
1465    // next label is the same as this label
1466    //JAL-4427
1467  5 if((lastDescription != null && !lastDescription.equals(description))) {
1468  0 if(aa[nexAA].sequenceRef == aa[i].sequenceRef) {
1469  0 label = description;
1470    }
1471  0 lastDescription = description;
1472    }
1473    }
1474    else
1475    {
1476  22464 lastLabel = label;
1477  22464 lastDescription = description;
1478    }
1479    }
1480  22475 if (aa[i].sequenceRef != null)
1481    {
1482  1286 if (aa[i].sequenceRef != lastSeqRef)
1483    {
1484  355 label = aa[i].sequenceRef.getName() + " " + label;
1485    // TODO record relationship between sequence and this annotation and
1486    // display it here
1487    }
1488    else
1489    {
1490  931 vertBar = true;
1491    }
1492    }
1493   
1494  22475 int labelWidth = fm.stringWidth(label) + 3;
1495  22475 x = width - labelWidth;
1496   
1497  22475 if (aa[i].graphGroup > -1)
1498    {
1499  0 int groupSize = 0;
1500    // TODO: JAL-1291 revise rendering model so the graphGroup map is
1501    // computed efficiently for all visible labels
1502  0 for (int gg = 0; gg < aa.length; gg++)
1503    {
1504  0 if (aa[gg].graphGroup == aa[i].graphGroup)
1505    {
1506  0 groupSize++;
1507    }
1508    }
1509  0 if (groupSize * (fontHeight + 8) < aa[i].height)
1510    {
1511  0 graphExtras = (aa[i].height - (groupSize * (fontHeight + 8)))
1512    / 2;
1513    }
1514    else
1515    {
1516    // scale font to fit
1517  0 float h = aa[i].height / (float) groupSize, s;
1518  0 if (h < 9)
1519    {
1520  0 visible = false;
1521    }
1522    else
1523    {
1524  0 fontHeight = -8 + (int) h;
1525  0 s = ((float) fontHeight) / (float) ofontH;
1526  0 Font f = baseFont
1527    .deriveFont(AffineTransform.getScaleInstance(s, s));
1528  0 Canvas c = new Canvas();
1529  0 fm = c.getFontMetrics(f);
1530  0 if (actuallyDraw && g != null)
1531    {
1532  0 g.setFont(f);
1533    // fm = g.getFontMetrics();
1534  0 graphExtras = (aa[i].height
1535    - (groupSize * (fontHeight + 8))) / 2;
1536    }
1537    }
1538    }
1539  0 if (visible)
1540    {
1541  0 for (int gg = 0; gg < aa.length; gg++)
1542    {
1543  0 if (aa[gg].graphGroup == aa[i].graphGroup)
1544    {
1545  0 labelWidth = fm.stringWidth(aa[gg].label) + 3;
1546  0 x = width - labelWidth;
1547  0 if (actuallyDraw && g != null)
1548    {
1549  0 g.drawString(aa[gg].label, x, y - graphExtras);
1550   
1551  0 if (aa[gg]._linecolour != null)
1552    {
1553   
1554  0 g.setColor(aa[gg]._linecolour);
1555  0 g.drawLine(x, y - graphExtras + 3,
1556    x + fm.stringWidth(aa[gg].label),
1557    y - graphExtras + 3);
1558    }
1559   
1560  0 g.setColor(Color.black);
1561    }
1562  0 graphExtras += fontHeight + 8;
1563    }
1564    }
1565    }
1566  0 if (actuallyDraw && g != null)
1567    {
1568  0 g.setFont(baseFont);
1569    }
1570  0 fm = baseMetrics;
1571  0 fontHeight = ofontH;
1572    }
1573    else
1574    {
1575  22475 if (actuallyDraw && g != null)
1576    {
1577  11075 if (vertBar)
1578    {
1579  446 g.drawLine(width - 3, y + offset - fontHeight, width - 3,
1580    (int) (y - 1.5 * aa[i].height - offset - fontHeight));
1581    // g.drawLine(20, y + offset, x - 20, y + offset);
1582   
1583    }
1584  11075 g.drawString(label, x, y + offset);
1585    }
1586    }
1587  22475 lastSeqRef = aa[i].sequenceRef;
1588   
1589  22475 if (labelWidth > actualWidth)
1590    {
1591  6963 actualWidth = labelWidth;
1592    }
1593    }
1594    }
1595   
1596  4856 if (!resizePanel && dragEvent != null && aa != null && selectedRow > -1
1597    && selectedRow < aa.length)
1598    {
1599  0 if (actuallyDraw && g != null)
1600    {
1601  0 g.setColor(Color.lightGray);
1602  0 g.drawString(
1603  0 (aa[selectedRow].sequenceRef == null ? ""
1604    : aa[selectedRow].sequenceRef.getName())
1605    + aa[selectedRow].label,
1606    dragEvent.getX(), dragEvent.getY() - getScrollOffset());
1607    }
1608    }
1609   
1610  4856 if (!av.getWrapAlignment() && ((aa == null) || (aa.length < 1)))
1611    {
1612  0 if (actuallyDraw && g != null)
1613    {
1614  0 g.drawString(MessageManager.getString("label.right_click"), 2, 8);
1615  0 g.drawString(MessageManager.getString("label.to_add_annotation"), 2,
1616    18);
1617    }
1618    }
1619   
1620  4856 return actualWidth;
1621    }
1622   
 
1623  8936 toggle public int getScrollOffset()
1624    {
1625  8936 return scrollOffset;
1626    }
1627   
 
1628  0 toggle @Override
1629    public void mouseEntered(MouseEvent e)
1630    {
1631    }
1632   
 
1633  38 toggle public void drawComponentNotGUI(Graphics idGraphics, int idWidth)
1634    {
1635  38 drawComponent(idGraphics, false, idWidth, false);
1636    }
1637    }