Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.gui

File AnnotationLabels.java

 

Coverage histogram

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

Code metrics

272
536
42
1
1,614
1,232
218
0.41
12.76
42
5.19

Classes

Class Line # Actions
AnnotationLabels 76 536 218
0.3211764732.1%
 

Contributing tests

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