69af72bf531b12f502007890c8a33353fd0e3b4c
[jalview.git] / src / jalview / gui / AlignFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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 jalview.analysis.AAFrequency;
24 import jalview.analysis.AlignmentSorter;
25 import jalview.analysis.AlignmentUtils;
26 import jalview.analysis.Conservation;
27 import jalview.analysis.CrossRef;
28 import jalview.analysis.NJTree;
29 import jalview.analysis.ParseProperties;
30 import jalview.analysis.SequenceIdMatcher;
31 import jalview.api.AlignViewControllerGuiI;
32 import jalview.api.AlignViewControllerI;
33 import jalview.api.analysis.ScoreModelI;
34 import jalview.bin.Cache;
35 import jalview.commands.CommandI;
36 import jalview.commands.EditCommand;
37 import jalview.commands.OrderCommand;
38 import jalview.commands.RemoveGapColCommand;
39 import jalview.commands.RemoveGapsCommand;
40 import jalview.commands.SlideSequencesCommand;
41 import jalview.commands.TrimRegionCommand;
42 import jalview.datamodel.AlignedCodonFrame;
43 import jalview.datamodel.Alignment;
44 import jalview.datamodel.AlignmentAnnotation;
45 import jalview.datamodel.AlignmentI;
46 import jalview.datamodel.AlignmentOrder;
47 import jalview.datamodel.AlignmentView;
48 import jalview.datamodel.ColumnSelection;
49 import jalview.datamodel.PDBEntry;
50 import jalview.datamodel.SeqCigar;
51 import jalview.datamodel.Sequence;
52 import jalview.datamodel.SequenceGroup;
53 import jalview.datamodel.SequenceI;
54 import jalview.io.AlignmentProperties;
55 import jalview.io.AnnotationFile;
56 import jalview.io.FeaturesFile;
57 import jalview.io.FileLoader;
58 import jalview.io.FormatAdapter;
59 import jalview.io.HTMLOutput;
60 import jalview.io.IdentifyFile;
61 import jalview.io.JalviewFileChooser;
62 import jalview.io.JalviewFileView;
63 import jalview.io.JnetAnnotationMaker;
64 import jalview.io.NewickFile;
65 import jalview.io.TCoffeeScoreFile;
66 import jalview.jbgui.GAlignFrame;
67 import jalview.schemes.Blosum62ColourScheme;
68 import jalview.schemes.BuriedColourScheme;
69 import jalview.schemes.ClustalxColourScheme;
70 import jalview.schemes.ColourSchemeI;
71 import jalview.schemes.ColourSchemeProperty;
72 import jalview.schemes.HelixColourScheme;
73 import jalview.schemes.HydrophobicColourScheme;
74 import jalview.schemes.NucleotideColourScheme;
75 import jalview.schemes.PIDColourScheme;
76 import jalview.schemes.PurinePyrimidineColourScheme;
77 import jalview.schemes.RNAHelicesColourChooser;
78 import jalview.schemes.ResidueProperties;
79 import jalview.schemes.StrandColourScheme;
80 import jalview.schemes.TCoffeeColourScheme;
81 import jalview.schemes.TaylorColourScheme;
82 import jalview.schemes.TurnColourScheme;
83 import jalview.schemes.UserColourScheme;
84 import jalview.schemes.ZappoColourScheme;
85 import jalview.util.MessageManager;
86 import jalview.ws.jws1.Discoverer;
87 import jalview.ws.jws2.Jws2Discoverer;
88 import jalview.ws.jws2.jabaws2.Jws2Instance;
89 import jalview.ws.seqfetcher.DbSourceProxy;
90
91 import java.awt.BorderLayout;
92 import java.awt.Component;
93 import java.awt.GridLayout;
94 import java.awt.Rectangle;
95 import java.awt.Toolkit;
96 import java.awt.datatransfer.Clipboard;
97 import java.awt.datatransfer.DataFlavor;
98 import java.awt.datatransfer.StringSelection;
99 import java.awt.datatransfer.Transferable;
100 import java.awt.dnd.DnDConstants;
101 import java.awt.dnd.DropTargetDragEvent;
102 import java.awt.dnd.DropTargetDropEvent;
103 import java.awt.dnd.DropTargetEvent;
104 import java.awt.dnd.DropTargetListener;
105 import java.awt.event.ActionEvent;
106 import java.awt.event.ActionListener;
107 import java.awt.event.KeyAdapter;
108 import java.awt.event.KeyEvent;
109 import java.awt.event.MouseAdapter;
110 import java.awt.event.MouseEvent;
111 import java.awt.print.PageFormat;
112 import java.awt.print.PrinterJob;
113 import java.beans.PropertyChangeEvent;
114 import java.io.File;
115 import java.net.URL;
116 import java.util.ArrayList;
117 import java.util.Enumeration;
118 import java.util.Hashtable;
119 import java.util.List;
120 import java.util.Vector;
121
122 import javax.swing.JButton;
123 import javax.swing.JCheckBoxMenuItem;
124 import javax.swing.JEditorPane;
125 import javax.swing.JInternalFrame;
126 import javax.swing.JLabel;
127 import javax.swing.JLayeredPane;
128 import javax.swing.JMenu;
129 import javax.swing.JMenuItem;
130 import javax.swing.JOptionPane;
131 import javax.swing.JPanel;
132 import javax.swing.JProgressBar;
133 import javax.swing.JRadioButtonMenuItem;
134 import javax.swing.JScrollPane;
135 import javax.swing.SwingUtilities;
136
137 /**
138  * DOCUMENT ME!
139  * 
140  * @author $author$
141  * @version $Revision$
142  */
143 public class AlignFrame extends GAlignFrame implements DropTargetListener,
144         IProgressIndicator, AlignViewControllerGuiI
145 {
146
147   /** DOCUMENT ME!! */
148   public static final int DEFAULT_WIDTH = 700;
149
150   /** DOCUMENT ME!! */
151   public static final int DEFAULT_HEIGHT = 500;
152
153   public AlignmentPanel alignPanel;
154
155   AlignViewport viewport;
156
157   public AlignViewControllerI avc;
158
159   Vector alignPanels = new Vector();
160
161   /**
162    * Last format used to load or save alignments in this window
163    */
164   String currentFileFormat = null;
165
166   /**
167    * Current filename for this alignment
168    */
169   String fileName = null;
170
171   /**
172    * Creates a new AlignFrame object with specific width and height.
173    * 
174    * @param al
175    * @param width
176    * @param height
177    */
178   public AlignFrame(AlignmentI al, int width, int height)
179   {
180     this(al, null, width, height);
181   }
182
183   /**
184    * Creates a new AlignFrame object with specific width, height and
185    * sequenceSetId
186    * 
187    * @param al
188    * @param width
189    * @param height
190    * @param sequenceSetId
191    */
192   public AlignFrame(AlignmentI al, int width, int height,
193           String sequenceSetId)
194   {
195     this(al, null, width, height, sequenceSetId);
196   }
197
198   /**
199    * Creates a new AlignFrame object with specific width, height and
200    * sequenceSetId
201    * 
202    * @param al
203    * @param width
204    * @param height
205    * @param sequenceSetId
206    * @param viewId
207    */
208   public AlignFrame(AlignmentI al, int width, int height,
209           String sequenceSetId, String viewId)
210   {
211     this(al, null, width, height, sequenceSetId, viewId);
212   }
213
214   /**
215    * new alignment window with hidden columns
216    * 
217    * @param al
218    *          AlignmentI
219    * @param hiddenColumns
220    *          ColumnSelection or null
221    * @param width
222    *          Width of alignment frame
223    * @param height
224    *          height of frame.
225    */
226   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
227           int width, int height)
228   {
229     this(al, hiddenColumns, width, height, null);
230   }
231
232   /**
233    * Create alignment frame for al with hiddenColumns, a specific width and
234    * height, and specific sequenceId
235    * 
236    * @param al
237    * @param hiddenColumns
238    * @param width
239    * @param height
240    * @param sequenceSetId
241    *          (may be null)
242    */
243   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
244           int width, int height, String sequenceSetId)
245   {
246     this(al, hiddenColumns, width, height, sequenceSetId, null);
247   }
248
249   /**
250    * Create alignment frame for al with hiddenColumns, a specific width and
251    * height, and specific sequenceId
252    * 
253    * @param al
254    * @param hiddenColumns
255    * @param width
256    * @param height
257    * @param sequenceSetId
258    *          (may be null)
259    * @param viewId
260    *          (may be null)
261    */
262   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
263           int width, int height, String sequenceSetId, String viewId)
264   {
265     setSize(width, height);
266
267     if (al.getDataset() == null)
268     {
269       al.setDataset(null);
270     }
271
272     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
273
274     alignPanel = new AlignmentPanel(this, viewport);
275
276
277     addAlignmentPanel(alignPanel, true);
278     init();
279   }
280
281   /**
282    * Make a new AlignFrame from exisiting alignmentPanels
283    * 
284    * @param ap
285    *          AlignmentPanel
286    * @param av
287    *          AlignViewport
288    */
289   public AlignFrame(AlignmentPanel ap)
290   {
291     viewport = ap.av;
292     alignPanel = ap;
293     addAlignmentPanel(ap, false);
294     init();
295   }
296
297   /**
298    * initalise the alignframe from the underlying viewport data and the
299    * configurations
300    */
301   void init()
302   {
303     avc = new jalview.controller.AlignViewController(this, viewport,
304             alignPanel);
305     if (viewport.getAlignmentConservationAnnotation() == null)
306     {
307       BLOSUM62Colour.setEnabled(false);
308       conservationMenuItem.setEnabled(false);
309       modifyConservation.setEnabled(false);
310       // PIDColour.setEnabled(false);
311       // abovePIDThreshold.setEnabled(false);
312       // modifyPID.setEnabled(false);
313     }
314
315     String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
316             "No sort");
317
318     if (sortby.equals("Id"))
319     {
320       sortIDMenuItem_actionPerformed(null);
321     }
322     else if (sortby.equals("Pairwise Identity"))
323     {
324       sortPairwiseMenuItem_actionPerformed(null);
325     }
326
327     if (Desktop.desktop != null)
328     {
329       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
330       addServiceListeners();
331       setGUINucleotide(viewport.getAlignment().isNucleotide());
332     }
333
334     setMenusFromViewport(viewport);
335     buildSortByAnnotationScoresMenu();
336     buildTreeMenu();
337     
338     if (viewport.wrapAlignment)
339     {
340       wrapMenuItem_actionPerformed(null);
341     }
342
343     if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
344     {
345       this.overviewMenuItem_actionPerformed(null);
346     }
347
348     addKeyListener();
349
350   }
351
352   /**
353    * Change the filename and format for the alignment, and enable the 'reload'
354    * button functionality.
355    * 
356    * @param file
357    *          valid filename
358    * @param format
359    *          format of file
360    */
361   public void setFileName(String file, String format)
362   {
363     fileName = file;
364     currentFileFormat = format;
365     reload.setEnabled(true);
366   }
367
368   void addKeyListener()
369   {
370     addKeyListener(new KeyAdapter()
371     {
372       @Override
373       public void keyPressed(KeyEvent evt)
374       {
375         if (viewport.cursorMode
376                 && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
377                         .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
378                         .getKeyCode() <= KeyEvent.VK_NUMPAD9))
379                 && Character.isDigit(evt.getKeyChar()))
380           alignPanel.seqPanel.numberPressed(evt.getKeyChar());
381
382         switch (evt.getKeyCode())
383         {
384
385         case 27: // escape key
386           deselectAllSequenceMenuItem_actionPerformed(null);
387
388           break;
389
390         case KeyEvent.VK_DOWN:
391           if (evt.isAltDown() || !viewport.cursorMode)
392             moveSelectedSequences(false);
393           if (viewport.cursorMode)
394             alignPanel.seqPanel.moveCursor(0, 1);
395           break;
396
397         case KeyEvent.VK_UP:
398           if (evt.isAltDown() || !viewport.cursorMode)
399             moveSelectedSequences(true);
400           if (viewport.cursorMode)
401             alignPanel.seqPanel.moveCursor(0, -1);
402
403           break;
404
405         case KeyEvent.VK_LEFT:
406           if (evt.isAltDown() || !viewport.cursorMode)
407             slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
408           else
409             alignPanel.seqPanel.moveCursor(-1, 0);
410
411           break;
412
413         case KeyEvent.VK_RIGHT:
414           if (evt.isAltDown() || !viewport.cursorMode)
415             slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
416           else
417             alignPanel.seqPanel.moveCursor(1, 0);
418           break;
419
420         case KeyEvent.VK_SPACE:
421           if (viewport.cursorMode)
422           {
423             alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
424                     || evt.isShiftDown() || evt.isAltDown());
425           }
426           break;
427
428         // case KeyEvent.VK_A:
429         // if (viewport.cursorMode)
430         // {
431         // alignPanel.seqPanel.insertNucAtCursor(false,"A");
432         // //System.out.println("A");
433         // }
434         // break;
435         /*
436          * case KeyEvent.VK_CLOSE_BRACKET: if (viewport.cursorMode) {
437          * System.out.println("closing bracket"); } break;
438          */
439         case KeyEvent.VK_DELETE:
440         case KeyEvent.VK_BACK_SPACE:
441           if (!viewport.cursorMode)
442           {
443             cut_actionPerformed(null);
444           }
445           else
446           {
447             alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
448                     || evt.isShiftDown() || evt.isAltDown());
449           }
450
451           break;
452
453         case KeyEvent.VK_S:
454           if (viewport.cursorMode)
455           {
456             alignPanel.seqPanel.setCursorRow();
457           }
458           break;
459         case KeyEvent.VK_C:
460           if (viewport.cursorMode && !evt.isControlDown())
461           {
462             alignPanel.seqPanel.setCursorColumn();
463           }
464           break;
465         case KeyEvent.VK_P:
466           if (viewport.cursorMode)
467           {
468             alignPanel.seqPanel.setCursorPosition();
469           }
470           break;
471
472         case KeyEvent.VK_ENTER:
473         case KeyEvent.VK_COMMA:
474           if (viewport.cursorMode)
475           {
476             alignPanel.seqPanel.setCursorRowAndColumn();
477           }
478           break;
479
480         case KeyEvent.VK_Q:
481           if (viewport.cursorMode)
482           {
483             alignPanel.seqPanel.setSelectionAreaAtCursor(true);
484           }
485           break;
486         case KeyEvent.VK_M:
487           if (viewport.cursorMode)
488           {
489             alignPanel.seqPanel.setSelectionAreaAtCursor(false);
490           }
491           break;
492
493         case KeyEvent.VK_F2:
494           viewport.cursorMode = !viewport.cursorMode;
495           statusBar.setText(MessageManager.formatMessage(
496                   "label.keyboard_editing_mode", new String[]
497                   { (viewport.cursorMode ? "on" : "off") }));
498           if (viewport.cursorMode)
499           {
500             alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
501             alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
502           }
503           alignPanel.seqPanel.seqCanvas.repaint();
504           break;
505
506         case KeyEvent.VK_F1:
507           try
508           {
509             Help.showHelpWindow();
510           } catch (Exception ex)
511           {
512             ex.printStackTrace();
513           }
514           break;
515         case KeyEvent.VK_H:
516         {
517           boolean toggleSeqs = !evt.isControlDown();
518           boolean toggleCols = !evt.isShiftDown();
519           toggleHiddenRegions(toggleSeqs, toggleCols);
520           break;
521         }
522         case KeyEvent.VK_PAGE_UP:
523           if (viewport.wrapAlignment)
524           {
525             alignPanel.scrollUp(true);
526           }
527           else
528           {
529             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
530                     - viewport.endSeq + viewport.startSeq);
531           }
532           break;
533         case KeyEvent.VK_PAGE_DOWN:
534           if (viewport.wrapAlignment)
535           {
536             alignPanel.scrollUp(false);
537           }
538           else
539           {
540             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
541                     + viewport.endSeq - viewport.startSeq);
542           }
543           break;
544         }
545       }
546
547       @Override
548       public void keyReleased(KeyEvent evt)
549       {
550         switch (evt.getKeyCode())
551         {
552         case KeyEvent.VK_LEFT:
553           if (evt.isAltDown() || !viewport.cursorMode)
554             viewport.firePropertyChange("alignment", null, viewport
555                     .getAlignment().getSequences());
556           break;
557
558         case KeyEvent.VK_RIGHT:
559           if (evt.isAltDown() || !viewport.cursorMode)
560             viewport.firePropertyChange("alignment", null, viewport
561                     .getAlignment().getSequences());
562           break;
563         }
564       }
565     });
566   }
567
568   public void addAlignmentPanel(final AlignmentPanel ap, boolean newPanel)
569   {
570     ap.alignFrame = this;
571     avc = new jalview.controller.AlignViewController(this, viewport,
572             alignPanel);
573
574     alignPanels.addElement(ap);
575
576     PaintRefresher.Register(ap, ap.av.getSequenceSetId());
577
578     int aSize = alignPanels.size();
579
580     tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
581
582     if (aSize == 1 && ap.av.viewName == null)
583     {
584       this.getContentPane().add(ap, BorderLayout.CENTER);
585     }
586     else
587     {
588       if (aSize == 2)
589       {
590         setInitialTabVisible();
591       }
592
593       expandViews.setEnabled(true);
594       gatherViews.setEnabled(true);
595       tabbedPane.addTab(ap.av.viewName, ap);
596
597       ap.setVisible(false);
598     }
599
600     if (newPanel)
601     {
602       if (ap.av.isPadGaps())
603       {
604         ap.av.getAlignment().padGaps();
605       }
606       ap.av.updateConservation(ap);
607       ap.av.updateConsensus(ap);
608       ap.av.updateStrucConsensus(ap);
609     }
610   }
611
612   public void setInitialTabVisible()
613   {
614     expandViews.setEnabled(true);
615     gatherViews.setEnabled(true);
616     tabbedPane.setVisible(true);
617     AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
618     tabbedPane.addTab(first.av.viewName, first);
619     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
620   }
621
622   public AlignViewport getViewport()
623   {
624     return viewport;
625   }
626
627   /* Set up intrinsic listeners for dynamically generated GUI bits. */
628   private void addServiceListeners()
629   {
630     final java.beans.PropertyChangeListener thisListener;
631     Desktop.instance.addJalviewPropertyChangeListener("services",
632             thisListener = new java.beans.PropertyChangeListener()
633             {
634               @Override
635               public void propertyChange(PropertyChangeEvent evt)
636               {
637                 // // System.out.println("Discoverer property change.");
638                 // if (evt.getPropertyName().equals("services"))
639                 {
640                   SwingUtilities.invokeLater(new Runnable()
641                   {
642
643                     @Override
644                     public void run()
645                     {
646                       System.err
647                               .println("Rebuild WS Menu for service change");
648                       BuildWebServiceMenu();
649                     }
650
651                   });
652                 }
653               }
654             });
655     addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
656     {
657       @Override
658       public void internalFrameClosed(
659               javax.swing.event.InternalFrameEvent evt)
660       {
661         System.out.println("deregistering discoverer listener");
662         Desktop.instance.removeJalviewPropertyChangeListener("services",
663                 thisListener);
664         closeMenuItem_actionPerformed(true);
665       };
666     });
667     // Finally, build the menu once to get current service state
668     new Thread(new Runnable()
669     {
670       @Override
671       public void run()
672       {
673         BuildWebServiceMenu();
674       }
675     }).start();
676   }
677
678   public void setGUINucleotide(boolean nucleotide)
679   {
680     showTranslation.setVisible(nucleotide);
681     conservationMenuItem.setEnabled(!nucleotide);
682     modifyConservation.setEnabled(!nucleotide);
683     showGroupConservation.setEnabled(!nucleotide);
684     rnahelicesColour.setEnabled(nucleotide);
685     purinePyrimidineColour.setEnabled(nucleotide);
686     // Remember AlignFrame always starts as protein
687     // if (!nucleotide)
688     // {
689     // showTr
690     // calculateMenu.remove(calculateMenu.getItemCount() - 2);
691     // }
692   }
693
694   /**
695    * set up menus for the currently viewport. This may be called after any
696    * operation that affects the data in the current view (selection changed,
697    * etc) to update the menus to reflect the new state.
698    */
699   public void setMenusForViewport()
700   {
701     setMenusFromViewport(viewport);
702   }
703
704   /**
705    * Need to call this method when tabs are selected for multiple views, or when
706    * loading from Jalview2XML.java
707    * 
708    * @param av
709    *          AlignViewport
710    */
711   void setMenusFromViewport(AlignViewport av)
712   {
713     padGapsMenuitem.setSelected(av.isPadGaps());
714     colourTextMenuItem.setSelected(av.showColourText);
715     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
716     conservationMenuItem.setSelected(av.getConservationSelected());
717     seqLimits.setSelected(av.getShowJVSuffix());
718     idRightAlign.setSelected(av.rightAlignIds);
719     centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
720     renderGapsMenuItem.setSelected(av.renderGaps);
721     wrapMenuItem.setSelected(av.wrapAlignment);
722     scaleAbove.setVisible(av.wrapAlignment);
723     scaleLeft.setVisible(av.wrapAlignment);
724     scaleRight.setVisible(av.wrapAlignment);
725     annotationPanelMenuItem.setState(av.showAnnotation);
726     /*
727      * Show/hide all annotations only enabled if annotation panel is shown
728      */
729     showAllAnnotations.setEnabled(annotationPanelMenuItem.getState());
730     hideAllAnnotations.setEnabled(annotationPanelMenuItem.getState());
731     viewBoxesMenuItem.setSelected(av.showBoxes);
732     viewTextMenuItem.setSelected(av.showText);
733     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
734     showGroupConsensus.setSelected(av.isShowGroupConsensus());
735     showGroupConservation.setSelected(av.isShowGroupConservation());
736     showConsensusHistogram.setSelected(av.isShowConsensusHistogram());
737     showSequenceLogo.setSelected(av.isShowSequenceLogo());
738     normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
739
740     setColourSelected(ColourSchemeProperty.getColourName(av
741             .getGlobalColourScheme()));
742
743     showSeqFeatures.setSelected(av.showSequenceFeatures);
744     hiddenMarkers.setState(av.showHiddenMarkers);
745     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
746     showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
747     showDbRefsMenuitem.setSelected(av.isShowDbRefs());
748     autoCalculate.setSelected(av.autoCalculateConsensus);
749     sortByTree.setSelected(av.sortByTree);
750     listenToViewSelections.setSelected(av.followSelection);
751     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
752     rnahelicesColour
753             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
754     setShowProductsEnabled();
755     updateEditMenuBar();
756   }
757
758   // methods for implementing IProgressIndicator
759   // need to refactor to a reusable stub class
760   Hashtable progressBars, progressBarHandlers;
761
762   /*
763    * (non-Javadoc)
764    * 
765    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
766    */
767   @Override
768   public void setProgressBar(String message, long id)
769   {
770     if (progressBars == null)
771     {
772       progressBars = new Hashtable();
773       progressBarHandlers = new Hashtable();
774     }
775
776     JPanel progressPanel;
777     Long lId = new Long(id);
778     GridLayout layout = (GridLayout) statusPanel.getLayout();
779     if (progressBars.get(lId) != null)
780     {
781       progressPanel = (JPanel) progressBars.get(new Long(id));
782       statusPanel.remove(progressPanel);
783       progressBars.remove(lId);
784       progressPanel = null;
785       if (message != null)
786       {
787         statusBar.setText(message);
788       }
789       if (progressBarHandlers.contains(lId))
790       {
791         progressBarHandlers.remove(lId);
792       }
793       layout.setRows(layout.getRows() - 1);
794     }
795     else
796     {
797       progressPanel = new JPanel(new BorderLayout(10, 5));
798
799       JProgressBar progressBar = new JProgressBar();
800       progressBar.setIndeterminate(true);
801
802       progressPanel.add(new JLabel(message), BorderLayout.WEST);
803       progressPanel.add(progressBar, BorderLayout.CENTER);
804
805       layout.setRows(layout.getRows() + 1);
806       statusPanel.add(progressPanel);
807
808       progressBars.put(lId, progressPanel);
809     }
810     // update GUI
811     // setMenusForViewport();
812     validate();
813   }
814
815   @Override
816   public void registerHandler(final long id,
817           final IProgressIndicatorHandler handler)
818   {
819     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
820     {
821       throw new Error(
822               "call setProgressBar before registering the progress bar's handler.");
823     }
824     progressBarHandlers.put(new Long(id), handler);
825     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
826     if (handler.canCancel())
827     {
828       JButton cancel = new JButton(
829               MessageManager.getString("action.cancel"));
830       final IProgressIndicator us = this;
831       cancel.addActionListener(new ActionListener()
832       {
833
834         @Override
835         public void actionPerformed(ActionEvent e)
836         {
837           handler.cancelActivity(id);
838           us.setProgressBar(
839                   "Cancelled "
840                           + ((JLabel) progressPanel.getComponent(0))
841                                   .getText(), id);
842         }
843       });
844       progressPanel.add(cancel, BorderLayout.EAST);
845     }
846   }
847
848   /**
849    * 
850    * @return true if any progress bars are still active
851    */
852   @Override
853   public boolean operationInProgress()
854   {
855     if (progressBars != null && progressBars.size() > 0)
856     {
857       return true;
858     }
859     return false;
860   }
861
862   @Override
863   public void setStatus(String text)
864   {
865     statusBar.setText(text);
866   };
867
868   /*
869    * Added so Castor Mapping file can obtain Jalview Version
870    */
871   public String getVersion()
872   {
873     return jalview.bin.Cache.getProperty("VERSION");
874   }
875
876   public FeatureRenderer getFeatureRenderer()
877   {
878     return alignPanel.seqPanel.seqCanvas.getFeatureRenderer();
879   }
880
881   @Override
882   public void fetchSequence_actionPerformed(ActionEvent e)
883   {
884     new SequenceFetcher(this);
885   }
886
887   @Override
888   public void addFromFile_actionPerformed(ActionEvent e)
889   {
890     Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
891   }
892
893   @Override
894   public void reload_actionPerformed(ActionEvent e)
895   {
896     if (fileName != null)
897     {
898       // TODO: JAL-1108 - ensure all associated frames are closed regardless of
899       // originating file's format
900       // TODO: work out how to recover feature settings for correct view(s) when
901       // file is reloaded.
902       if (currentFileFormat.equals("Jalview"))
903       {
904         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
905         for (int i = 0; i < frames.length; i++)
906         {
907           if (frames[i] instanceof AlignFrame && frames[i] != this
908                   && ((AlignFrame) frames[i]).fileName != null
909                   && ((AlignFrame) frames[i]).fileName.equals(fileName))
910           {
911             try
912             {
913               frames[i].setSelected(true);
914               Desktop.instance.closeAssociatedWindows();
915             } catch (java.beans.PropertyVetoException ex)
916             {
917             }
918           }
919
920         }
921         Desktop.instance.closeAssociatedWindows();
922
923         FileLoader loader = new FileLoader();
924         String protocol = fileName.startsWith("http:") ? "URL" : "File";
925         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
926       }
927       else
928       {
929         Rectangle bounds = this.getBounds();
930
931         FileLoader loader = new FileLoader();
932         String protocol = fileName.startsWith("http:") ? "URL" : "File";
933         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
934                 protocol, currentFileFormat);
935
936         newframe.setBounds(bounds);
937         if (featureSettings != null && featureSettings.isShowing())
938         {
939           final Rectangle fspos = featureSettings.frame.getBounds();
940           // TODO: need a 'show feature settings' function that takes bounds -
941           // need to refactor Desktop.addFrame
942           newframe.featureSettings_actionPerformed(null);
943           final FeatureSettings nfs = newframe.featureSettings;
944           SwingUtilities.invokeLater(new Runnable()
945           {
946             @Override
947             public void run()
948             {
949               nfs.frame.setBounds(fspos);
950             }
951           });
952           this.featureSettings.close();
953           this.featureSettings = null;
954         }
955         this.closeMenuItem_actionPerformed(true);
956       }
957     }
958   }
959
960   @Override
961   public void addFromText_actionPerformed(ActionEvent e)
962   {
963     Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport);
964   }
965
966   @Override
967   public void addFromURL_actionPerformed(ActionEvent e)
968   {
969     Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
970   }
971
972   @Override
973   public void save_actionPerformed(ActionEvent e)
974   {
975     if (fileName == null
976             || (currentFileFormat == null || !jalview.io.FormatAdapter
977                     .isValidIOFormat(currentFileFormat, true))
978             || fileName.startsWith("http"))
979     {
980       saveAs_actionPerformed(null);
981     }
982     else
983     {
984       saveAlignment(fileName, currentFileFormat);
985     }
986   }
987
988   /**
989    * DOCUMENT ME!
990    * 
991    * @param e
992    *          DOCUMENT ME!
993    */
994   @Override
995   public void saveAs_actionPerformed(ActionEvent e)
996   {
997     JalviewFileChooser chooser = new JalviewFileChooser(
998             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
999             jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
1000             jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
1001             currentFileFormat, false);
1002
1003     chooser.setFileView(new JalviewFileView());
1004     chooser.setDialogTitle("Save Alignment to file");
1005     chooser.setToolTipText(MessageManager.getString("action.save"));
1006
1007     int value = chooser.showSaveDialog(this);
1008
1009     if (value == JalviewFileChooser.APPROVE_OPTION)
1010     {
1011       currentFileFormat = chooser.getSelectedFormat();
1012       if (currentFileFormat == null)
1013       {
1014         JOptionPane
1015                 .showInternalMessageDialog(
1016                         Desktop.desktop,
1017                         MessageManager
1018                                 .getString("label.select_file_format_before_saving"),
1019                         MessageManager
1020                                 .getString("label.file_format_not_specified"),
1021                         JOptionPane.WARNING_MESSAGE);
1022         value = chooser.showSaveDialog(this);
1023         return;
1024       }
1025
1026       fileName = chooser.getSelectedFile().getPath();
1027
1028       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
1029               currentFileFormat);
1030
1031       jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
1032       if (currentFileFormat.indexOf(" ") > -1)
1033       {
1034         currentFileFormat = currentFileFormat.substring(0,
1035                 currentFileFormat.indexOf(" "));
1036       }
1037       saveAlignment(fileName, currentFileFormat);
1038     }
1039   }
1040
1041   public boolean saveAlignment(String file, String format)
1042   {
1043     boolean success = true;
1044
1045     if (format.equalsIgnoreCase("Jalview"))
1046     {
1047       String shortName = title;
1048
1049       if (shortName.indexOf(java.io.File.separatorChar) > -1)
1050       {
1051         shortName = shortName.substring(shortName
1052                 .lastIndexOf(java.io.File.separatorChar) + 1);
1053       }
1054
1055       success = new Jalview2XML().SaveAlignment(this, file, shortName);
1056
1057       statusBar.setText(MessageManager.formatMessage(
1058               "label.successfully_saved_to_file_in_format", new String[]
1059               { fileName, format }));
1060
1061     }
1062     else
1063     {
1064       if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
1065       {
1066         warningMessage("Cannot save file " + fileName + " using format "
1067                 + format, "Alignment output format not supported");
1068         saveAs_actionPerformed(null);
1069         // JBPNote need to have a raise_gui flag here
1070         return false;
1071       }
1072
1073       String[] omitHidden = null;
1074
1075       if (viewport.hasHiddenColumns())
1076       {
1077         int reply = JOptionPane
1078                 .showInternalConfirmDialog(
1079                         Desktop.desktop,
1080                         MessageManager
1081                                 .getString("label.alignment_contains_hidden_columns"),
1082                         MessageManager
1083                                 .getString("action.save_omit_hidden_columns"),
1084                         JOptionPane.YES_NO_OPTION,
1085                         JOptionPane.QUESTION_MESSAGE);
1086
1087         if (reply == JOptionPane.YES_OPTION)
1088         {
1089           omitHidden = viewport.getViewAsString(false);
1090         }
1091       }
1092       FormatAdapter f = new FormatAdapter();
1093       String output = f.formatSequences(format,
1094               viewport.getAlignment(), // class cast exceptions will
1095               // occur in the distant future
1096               omitHidden, f.getCacheSuffixDefault(format),
1097               viewport.getColumnSelection());
1098
1099       if (output == null)
1100       {
1101         success = false;
1102       }
1103       else
1104       {
1105         try
1106         {
1107           java.io.PrintWriter out = new java.io.PrintWriter(
1108                   new java.io.FileWriter(file));
1109
1110           out.print(output);
1111           out.close();
1112           this.setTitle(file);
1113           statusBar.setText(MessageManager.formatMessage(
1114                   "label.successfully_saved_to_file_in_format",
1115                   new String[]
1116                   { fileName, format }));
1117         } catch (Exception ex)
1118         {
1119           success = false;
1120           ex.printStackTrace();
1121         }
1122       }
1123     }
1124
1125     if (!success)
1126     {
1127       JOptionPane.showInternalMessageDialog(this, MessageManager
1128               .formatMessage("label.couldnt_save_file", new String[]
1129               { fileName }), MessageManager
1130               .getString("label.error_saving_file"),
1131               JOptionPane.WARNING_MESSAGE);
1132     }
1133
1134     return success;
1135   }
1136
1137   private void warningMessage(String warning, String title)
1138   {
1139     if (new jalview.util.Platform().isHeadless())
1140     {
1141       System.err.println("Warning: " + title + "\nWarning: " + warning);
1142
1143     }
1144     else
1145     {
1146       JOptionPane.showInternalMessageDialog(this, warning, title,
1147               JOptionPane.WARNING_MESSAGE);
1148     }
1149     return;
1150   }
1151
1152   /**
1153    * DOCUMENT ME!
1154    * 
1155    * @param e
1156    *          DOCUMENT ME!
1157    */
1158   @Override
1159   protected void outputText_actionPerformed(ActionEvent e)
1160   {
1161     String[] omitHidden = null;
1162
1163     if (viewport.hasHiddenColumns())
1164     {
1165       int reply = JOptionPane
1166               .showInternalConfirmDialog(
1167                       Desktop.desktop,
1168                       MessageManager
1169                               .getString("label.alignment_contains_hidden_columns"),
1170                       MessageManager
1171                               .getString("action.save_omit_hidden_columns"),
1172                       JOptionPane.YES_NO_OPTION,
1173                       JOptionPane.QUESTION_MESSAGE);
1174
1175       if (reply == JOptionPane.YES_OPTION)
1176       {
1177         omitHidden = viewport.getViewAsString(false);
1178       }
1179     }
1180
1181     CutAndPasteTransfer cap = new CutAndPasteTransfer();
1182     cap.setForInput(null);
1183
1184     try
1185     {
1186       cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
1187               viewport.getAlignment(), omitHidden,
1188               viewport.getColumnSelection()));
1189       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
1190               "label.alignment_output_command", new String[]
1191               { e.getActionCommand() }), 600, 500);
1192     } catch (OutOfMemoryError oom)
1193     {
1194       new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
1195       cap.dispose();
1196     }
1197
1198   }
1199
1200   /**
1201    * DOCUMENT ME!
1202    * 
1203    * @param e
1204    *          DOCUMENT ME!
1205    */
1206   @Override
1207   protected void htmlMenuItem_actionPerformed(ActionEvent e)
1208   {
1209     new HTMLOutput(alignPanel,
1210             alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
1211             alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
1212   }
1213
1214   public void createImageMap(File file, String image)
1215   {
1216     alignPanel.makePNGImageMap(file, image);
1217   }
1218
1219   /**
1220    * DOCUMENT ME!
1221    * 
1222    * @param e
1223    *          DOCUMENT ME!
1224    */
1225   @Override
1226   public void createPNG(File f)
1227   {
1228     alignPanel.makePNG(f);
1229   }
1230
1231   /**
1232    * DOCUMENT ME!
1233    * 
1234    * @param e
1235    *          DOCUMENT ME!
1236    */
1237   @Override
1238   public void createEPS(File f)
1239   {
1240     alignPanel.makeEPS(f);
1241   }
1242
1243   @Override
1244   public void pageSetup_actionPerformed(ActionEvent e)
1245   {
1246     PrinterJob printJob = PrinterJob.getPrinterJob();
1247     PrintThread.pf = printJob.pageDialog(printJob.defaultPage());
1248   }
1249
1250   /**
1251    * DOCUMENT ME!
1252    * 
1253    * @param e
1254    *          DOCUMENT ME!
1255    */
1256   @Override
1257   public void printMenuItem_actionPerformed(ActionEvent e)
1258   {
1259     // Putting in a thread avoids Swing painting problems
1260     PrintThread thread = new PrintThread(alignPanel);
1261     thread.start();
1262   }
1263
1264   @Override
1265   public void exportFeatures_actionPerformed(ActionEvent e)
1266   {
1267     new AnnotationExporter().exportFeatures(alignPanel);
1268   }
1269
1270   @Override
1271   public void exportAnnotations_actionPerformed(ActionEvent e)
1272   {
1273     new AnnotationExporter().exportAnnotations(alignPanel,
1274             viewport.showAnnotation ? viewport.getAlignment()
1275                     .getAlignmentAnnotation() : null, viewport
1276                     .getAlignment().getGroups(), ((Alignment) viewport
1277                     .getAlignment()).alignmentProperties);
1278   }
1279
1280   @Override
1281   public void associatedData_actionPerformed(ActionEvent e)
1282   {
1283     // Pick the tree file
1284     JalviewFileChooser chooser = new JalviewFileChooser(
1285             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1286     chooser.setFileView(new JalviewFileView());
1287     chooser.setDialogTitle(MessageManager
1288             .getString("label.load_jalview_annotations"));
1289     chooser.setToolTipText(MessageManager
1290             .getString("label.load_jalview_annotations"));
1291
1292     int value = chooser.showOpenDialog(null);
1293
1294     if (value == JalviewFileChooser.APPROVE_OPTION)
1295     {
1296       String choice = chooser.getSelectedFile().getPath();
1297       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
1298       loadJalviewDataFile(choice, null, null, null);
1299     }
1300
1301   }
1302
1303   /**
1304    * Close the current view or all views in the alignment frame. If the frame
1305    * only contains one view then the alignment will be removed from memory.
1306    * 
1307    * @param closeAllTabs
1308    */
1309   @Override
1310   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1311   {
1312     if (alignPanels != null && alignPanels.size() < 2)
1313     {
1314       closeAllTabs = true;
1315     }
1316
1317     try
1318     {
1319       if (alignPanels != null)
1320       {
1321         if (closeAllTabs)
1322         {
1323           if (this.isClosed())
1324           {
1325             // really close all the windows - otherwise wait till
1326             // setClosed(true) is called
1327             for (int i = 0; i < alignPanels.size(); i++)
1328             {
1329               AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
1330               ap.closePanel();
1331             }
1332           }
1333         }
1334         else
1335         {
1336           closeView(alignPanel);
1337         }
1338       }
1339
1340       if (closeAllTabs)
1341       {
1342         this.setClosed(true);
1343       }
1344     } catch (Exception ex)
1345     {
1346       ex.printStackTrace();
1347     }
1348   }
1349
1350   /**
1351    * close alignPanel2 and shuffle tabs appropriately.
1352    * 
1353    * @param alignPanel2
1354    */
1355   public void closeView(AlignmentPanel alignPanel2)
1356   {
1357     int index = tabbedPane.getSelectedIndex();
1358     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
1359     alignPanels.removeElement(alignPanel2);
1360     // Unnecessary
1361     // if (viewport == alignPanel2.av)
1362     // {
1363     // viewport = null;
1364     // }
1365     alignPanel2.closePanel();
1366     alignPanel2 = null;
1367
1368     tabbedPane.removeTabAt(closedindex);
1369     tabbedPane.validate();
1370
1371     if (index > closedindex || index == tabbedPane.getTabCount())
1372     {
1373       // modify currently selected tab index if necessary.
1374       index--;
1375     }
1376
1377     this.tabSelectionChanged(index);
1378   }
1379
1380   /**
1381    * DOCUMENT ME!
1382    */
1383   void updateEditMenuBar()
1384   {
1385
1386     if (viewport.historyList.size() > 0)
1387     {
1388       undoMenuItem.setEnabled(true);
1389       CommandI command = (CommandI) viewport.historyList.peek();
1390       undoMenuItem.setText(MessageManager.formatMessage(
1391               "label.undo_command", new String[]
1392               { command.getDescription() }));
1393     }
1394     else
1395     {
1396       undoMenuItem.setEnabled(false);
1397       undoMenuItem.setText(MessageManager.getString("action.undo"));
1398     }
1399
1400     if (viewport.redoList.size() > 0)
1401     {
1402       redoMenuItem.setEnabled(true);
1403
1404       CommandI command = (CommandI) viewport.redoList.peek();
1405       redoMenuItem.setText(MessageManager.formatMessage(
1406               "label.redo_command", new String[]
1407               { command.getDescription() }));
1408     }
1409     else
1410     {
1411       redoMenuItem.setEnabled(false);
1412       redoMenuItem.setText(MessageManager.getString("action.redo"));
1413     }
1414   }
1415
1416   public void addHistoryItem(CommandI command)
1417   {
1418     if (command.getSize() > 0)
1419     {
1420       viewport.historyList.push(command);
1421       viewport.redoList.clear();
1422       updateEditMenuBar();
1423       viewport.updateHiddenColumns();
1424       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
1425       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1426       // viewport.getColumnSelection()
1427       // .getHiddenColumns().size() > 0);
1428     }
1429   }
1430
1431   /**
1432    * 
1433    * @return alignment objects for all views
1434    */
1435   AlignmentI[] getViewAlignments()
1436   {
1437     if (alignPanels != null)
1438     {
1439       Enumeration e = alignPanels.elements();
1440       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1441       for (int i = 0; e.hasMoreElements(); i++)
1442       {
1443         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1444       }
1445       return als;
1446     }
1447     if (viewport != null)
1448     {
1449       return new AlignmentI[]
1450       { viewport.getAlignment() };
1451     }
1452     return null;
1453   }
1454
1455   /**
1456    * DOCUMENT ME!
1457    * 
1458    * @param e
1459    *          DOCUMENT ME!
1460    */
1461   @Override
1462   protected void undoMenuItem_actionPerformed(ActionEvent e)
1463   {
1464     if (viewport.historyList.empty())
1465       return;
1466     CommandI command = (CommandI) viewport.historyList.pop();
1467     viewport.redoList.push(command);
1468     command.undoCommand(getViewAlignments());
1469
1470     AlignViewport originalSource = getOriginatingSource(command);
1471     updateEditMenuBar();
1472
1473     if (originalSource != null)
1474     {
1475       if (originalSource != viewport)
1476       {
1477         Cache.log
1478                 .warn("Implementation worry: mismatch of viewport origin for undo");
1479       }
1480       originalSource.updateHiddenColumns();
1481       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1482       // null
1483       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1484       // viewport.getColumnSelection()
1485       // .getHiddenColumns().size() > 0);
1486       originalSource.firePropertyChange("alignment", null, originalSource
1487               .getAlignment().getSequences());
1488     }
1489   }
1490
1491   /**
1492    * DOCUMENT ME!
1493    * 
1494    * @param e
1495    *          DOCUMENT ME!
1496    */
1497   @Override
1498   protected void redoMenuItem_actionPerformed(ActionEvent e)
1499   {
1500     if (viewport.redoList.size() < 1)
1501     {
1502       return;
1503     }
1504
1505     CommandI command = (CommandI) viewport.redoList.pop();
1506     viewport.historyList.push(command);
1507     command.doCommand(getViewAlignments());
1508
1509     AlignViewport originalSource = getOriginatingSource(command);
1510     updateEditMenuBar();
1511
1512     if (originalSource != null)
1513     {
1514
1515       if (originalSource != viewport)
1516       {
1517         Cache.log
1518                 .warn("Implementation worry: mismatch of viewport origin for redo");
1519       }
1520       originalSource.updateHiddenColumns();
1521       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1522       // null
1523       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1524       // viewport.getColumnSelection()
1525       // .getHiddenColumns().size() > 0);
1526       originalSource.firePropertyChange("alignment", null, originalSource
1527               .getAlignment().getSequences());
1528     }
1529   }
1530
1531   AlignViewport getOriginatingSource(CommandI command)
1532   {
1533     AlignViewport originalSource = null;
1534     // For sequence removal and addition, we need to fire
1535     // the property change event FROM the viewport where the
1536     // original alignment was altered
1537     AlignmentI al = null;
1538     if (command instanceof EditCommand)
1539     {
1540       EditCommand editCommand = (EditCommand) command;
1541       al = editCommand.getAlignment();
1542       Vector comps = (Vector) PaintRefresher.components.get(viewport
1543               .getSequenceSetId());
1544
1545       for (int i = 0; i < comps.size(); i++)
1546       {
1547         if (comps.elementAt(i) instanceof AlignmentPanel)
1548         {
1549           if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
1550           {
1551             originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
1552             break;
1553           }
1554         }
1555       }
1556     }
1557
1558     if (originalSource == null)
1559     {
1560       // The original view is closed, we must validate
1561       // the current view against the closed view first
1562       if (al != null)
1563       {
1564         PaintRefresher.validateSequences(al, viewport.getAlignment());
1565       }
1566
1567       originalSource = viewport;
1568     }
1569
1570     return originalSource;
1571   }
1572
1573   /**
1574    * DOCUMENT ME!
1575    * 
1576    * @param up
1577    *          DOCUMENT ME!
1578    */
1579   public void moveSelectedSequences(boolean up)
1580   {
1581     SequenceGroup sg = viewport.getSelectionGroup();
1582
1583     if (sg == null)
1584     {
1585       return;
1586     }
1587     viewport.getAlignment().moveSelectedSequencesByOne(sg,
1588             viewport.getHiddenRepSequences(), up);
1589     alignPanel.paintAlignment(true);
1590   }
1591
1592   synchronized void slideSequences(boolean right, int size)
1593   {
1594     List<SequenceI> sg = new Vector();
1595     if (viewport.cursorMode)
1596     {
1597       sg.add(viewport.getAlignment().getSequenceAt(
1598               alignPanel.seqPanel.seqCanvas.cursorY));
1599     }
1600     else if (viewport.getSelectionGroup() != null
1601             && viewport.getSelectionGroup().getSize() != viewport
1602                     .getAlignment().getHeight())
1603     {
1604       sg = viewport.getSelectionGroup().getSequences(
1605               viewport.getHiddenRepSequences());
1606     }
1607
1608     if (sg.size() < 1)
1609     {
1610       return;
1611     }
1612
1613     Vector invertGroup = new Vector();
1614
1615     for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
1616     {
1617       if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
1618         invertGroup.add(viewport.getAlignment().getSequenceAt(i));
1619     }
1620
1621     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
1622
1623     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1624     for (int i = 0; i < invertGroup.size(); i++)
1625       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1626
1627     SlideSequencesCommand ssc;
1628     if (right)
1629       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
1630               size, viewport.getGapCharacter());
1631     else
1632       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
1633               size, viewport.getGapCharacter());
1634
1635     int groupAdjustment = 0;
1636     if (ssc.getGapsInsertedBegin() && right)
1637     {
1638       if (viewport.cursorMode)
1639         alignPanel.seqPanel.moveCursor(size, 0);
1640       else
1641         groupAdjustment = size;
1642     }
1643     else if (!ssc.getGapsInsertedBegin() && !right)
1644     {
1645       if (viewport.cursorMode)
1646         alignPanel.seqPanel.moveCursor(-size, 0);
1647       else
1648         groupAdjustment = -size;
1649     }
1650
1651     if (groupAdjustment != 0)
1652     {
1653       viewport.getSelectionGroup().setStartRes(
1654               viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1655       viewport.getSelectionGroup().setEndRes(
1656               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1657     }
1658
1659     boolean appendHistoryItem = false;
1660     if (viewport.historyList != null && viewport.historyList.size() > 0
1661             && viewport.historyList.peek() instanceof SlideSequencesCommand)
1662     {
1663       appendHistoryItem = ssc
1664               .appendSlideCommand((SlideSequencesCommand) viewport.historyList
1665                       .peek());
1666     }
1667
1668     if (!appendHistoryItem)
1669       addHistoryItem(ssc);
1670
1671     repaint();
1672   }
1673
1674   /**
1675    * DOCUMENT ME!
1676    * 
1677    * @param e
1678    *          DOCUMENT ME!
1679    */
1680   @Override
1681   protected void copy_actionPerformed(ActionEvent e)
1682   {
1683     System.gc();
1684     if (viewport.getSelectionGroup() == null)
1685     {
1686       return;
1687     }
1688     // TODO: preserve the ordering of displayed alignment annotation in any
1689     // internal paste (particularly sequence associated annotation)
1690     SequenceI[] seqs = viewport.getSelectionAsNewSequence();
1691     String[] omitHidden = null;
1692
1693     if (viewport.hasHiddenColumns())
1694     {
1695       omitHidden = viewport.getViewAsString(true);
1696     }
1697
1698     String output = new FormatAdapter().formatSequences("Fasta", seqs,
1699             omitHidden);
1700
1701     StringSelection ss = new StringSelection(output);
1702
1703     try
1704     {
1705       jalview.gui.Desktop.internalCopy = true;
1706       // Its really worth setting the clipboard contents
1707       // to empty before setting the large StringSelection!!
1708       Toolkit.getDefaultToolkit().getSystemClipboard()
1709               .setContents(new StringSelection(""), null);
1710
1711       Toolkit.getDefaultToolkit().getSystemClipboard()
1712               .setContents(ss, Desktop.instance);
1713     } catch (OutOfMemoryError er)
1714     {
1715       new OOMWarning("copying region", er);
1716       return;
1717     }
1718
1719     Vector hiddenColumns = null;
1720     if (viewport.hasHiddenColumns())
1721     {
1722       hiddenColumns = new Vector();
1723       int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
1724               .getSelectionGroup().getEndRes();
1725       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
1726               .size(); i++)
1727       {
1728         int[] region = (int[]) viewport.getColumnSelection()
1729                 .getHiddenColumns().elementAt(i);
1730         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
1731         {
1732           hiddenColumns.addElement(new int[]
1733           { region[0] - hiddenOffset, region[1] - hiddenOffset });
1734         }
1735       }
1736     }
1737
1738     Desktop.jalviewClipboard = new Object[]
1739     { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
1740     statusBar.setText(MessageManager.formatMessage(
1741             "label.copied_sequences_to_clipboard", new String[]
1742             { Integer.valueOf(seqs.length).toString() }));
1743   }
1744
1745   /**
1746    * DOCUMENT ME!
1747    * 
1748    * @param e
1749    *          DOCUMENT ME!
1750    */
1751   @Override
1752   protected void pasteNew_actionPerformed(ActionEvent e)
1753   {
1754     paste(true);
1755   }
1756
1757   /**
1758    * DOCUMENT ME!
1759    * 
1760    * @param e
1761    *          DOCUMENT ME!
1762    */
1763   @Override
1764   protected void pasteThis_actionPerformed(ActionEvent e)
1765   {
1766     paste(false);
1767   }
1768
1769   /**
1770    * Paste contents of Jalview clipboard
1771    * 
1772    * @param newAlignment
1773    *          true to paste to a new alignment, otherwise add to this.
1774    */
1775   void paste(boolean newAlignment)
1776   {
1777     boolean externalPaste = true;
1778     try
1779     {
1780       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1781       Transferable contents = c.getContents(this);
1782
1783       if (contents == null)
1784       {
1785         return;
1786       }
1787
1788       String str, format;
1789       try
1790       {
1791         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1792         if (str.length() < 1)
1793         {
1794           return;
1795         }
1796
1797         format = new IdentifyFile().Identify(str, "Paste");
1798
1799       } catch (OutOfMemoryError er)
1800       {
1801         new OOMWarning("Out of memory pasting sequences!!", er);
1802         return;
1803       }
1804
1805       SequenceI[] sequences;
1806       boolean annotationAdded = false;
1807       AlignmentI alignment = null;
1808
1809       if (Desktop.jalviewClipboard != null)
1810       {
1811         // The clipboard was filled from within Jalview, we must use the
1812         // sequences
1813         // And dataset from the copied alignment
1814         SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
1815         // be doubly sure that we create *new* sequence objects.
1816         sequences = new SequenceI[newseq.length];
1817         for (int i = 0; i < newseq.length; i++)
1818         {
1819           sequences[i] = new Sequence(newseq[i]);
1820         }
1821         alignment = new Alignment(sequences);
1822         externalPaste = false;
1823       }
1824       else
1825       {
1826         // parse the clipboard as an alignment.
1827         alignment = new FormatAdapter().readFile(str, "Paste", format);
1828         sequences = alignment.getSequencesArray();
1829       }
1830
1831       int alwidth = 0;
1832       ArrayList<Integer> newGraphGroups = new ArrayList<Integer>();
1833       int fgroup = -1;
1834
1835       if (newAlignment)
1836       {
1837
1838         if (Desktop.jalviewClipboard != null)
1839         {
1840           // dataset is inherited
1841           alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
1842         }
1843         else
1844         {
1845           // new dataset is constructed
1846           alignment.setDataset(null);
1847         }
1848         alwidth = alignment.getWidth() + 1;
1849       }
1850       else
1851       {
1852         AlignmentI pastedal = alignment; // preserve pasted alignment object
1853         // Add pasted sequences and dataset into existing alignment.
1854         alignment = viewport.getAlignment();
1855         alwidth = alignment.getWidth() + 1;
1856         // decide if we need to import sequences from an existing dataset
1857         boolean importDs = Desktop.jalviewClipboard != null
1858                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1859         // importDs==true instructs us to copy over new dataset sequences from
1860         // an existing alignment
1861         Vector newDs = (importDs) ? new Vector() : null; // used to create
1862         // minimum dataset set
1863
1864         for (int i = 0; i < sequences.length; i++)
1865         {
1866           if (importDs)
1867           {
1868             newDs.addElement(null);
1869           }
1870           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1871           // paste
1872           if (importDs && ds != null)
1873           {
1874             if (!newDs.contains(ds))
1875             {
1876               newDs.setElementAt(ds, i);
1877               ds = new Sequence(ds);
1878               // update with new dataset sequence
1879               sequences[i].setDatasetSequence(ds);
1880             }
1881             else
1882             {
1883               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1884             }
1885           }
1886           else
1887           {
1888             // copy and derive new dataset sequence
1889             sequences[i] = sequences[i].deriveSequence();
1890             alignment.getDataset().addSequence(
1891                     sequences[i].getDatasetSequence());
1892             // TODO: avoid creation of duplicate dataset sequences with a
1893             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1894           }
1895           alignment.addSequence(sequences[i]); // merges dataset
1896         }
1897         if (newDs != null)
1898         {
1899           newDs.clear(); // tidy up
1900         }
1901         if (alignment.getAlignmentAnnotation() != null)
1902         {
1903           for (AlignmentAnnotation alan : alignment
1904                   .getAlignmentAnnotation())
1905           {
1906             if (alan.graphGroup > fgroup)
1907             {
1908               fgroup = alan.graphGroup;
1909             }
1910           }
1911         }
1912         if (pastedal.getAlignmentAnnotation() != null)
1913         {
1914           // Add any annotation attached to alignment.
1915           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1916           for (int i = 0; i < alann.length; i++)
1917           {
1918             annotationAdded = true;
1919             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
1920             {
1921               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1922               if (newann.graphGroup > -1)
1923               {
1924                 if (newGraphGroups.size() <= newann.graphGroup
1925                         || newGraphGroups.get(newann.graphGroup) == null)
1926                 {
1927                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
1928                   {
1929                     newGraphGroups.add(q, null);
1930                   }
1931                   newGraphGroups.set(newann.graphGroup, new Integer(
1932                           ++fgroup));
1933                 }
1934                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
1935                         .intValue();
1936               }
1937
1938               newann.padAnnotation(alwidth);
1939               alignment.addAnnotation(newann);
1940             }
1941           }
1942         }
1943       }
1944       if (!newAlignment)
1945       {
1946         // /////
1947         // ADD HISTORY ITEM
1948         //
1949         addHistoryItem(new EditCommand("Add sequences", EditCommand.PASTE,
1950                 sequences, 0, alignment.getWidth(), alignment));
1951       }
1952       // Add any annotations attached to sequences
1953       for (int i = 0; i < sequences.length; i++)
1954       {
1955         if (sequences[i].getAnnotation() != null)
1956         {
1957           AlignmentAnnotation newann;
1958           for (int a = 0; a < sequences[i].getAnnotation().length; a++)
1959           {
1960             annotationAdded = true;
1961             newann = sequences[i].getAnnotation()[a];
1962             newann.adjustForAlignment();
1963             newann.padAnnotation(alwidth);
1964             if (newann.graphGroup > -1)
1965             {
1966               if (newann.graphGroup > -1)
1967               {
1968                 if (newGraphGroups.size() <= newann.graphGroup
1969                         || newGraphGroups.get(newann.graphGroup) == null)
1970                 {
1971                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
1972                   {
1973                     newGraphGroups.add(q, null);
1974                   }
1975                   newGraphGroups.set(newann.graphGroup, new Integer(
1976                           ++fgroup));
1977                 }
1978                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
1979                         .intValue();
1980               }
1981             }
1982             alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
1983             // was
1984             // duplicated
1985             // earlier
1986             alignment
1987                     .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
1988           }
1989         }
1990       }
1991       if (!newAlignment)
1992       {
1993
1994         // propagate alignment changed.
1995         viewport.setEndSeq(alignment.getHeight());
1996         if (annotationAdded)
1997         {
1998           // Duplicate sequence annotation in all views.
1999           AlignmentI[] alview = this.getViewAlignments();
2000           for (int i = 0; i < sequences.length; i++)
2001           {
2002             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
2003             if (sann == null)
2004               continue;
2005             for (int avnum = 0; avnum < alview.length; avnum++)
2006             {
2007               if (alview[avnum] != alignment)
2008               {
2009                 // duplicate in a view other than the one with input focus
2010                 int avwidth = alview[avnum].getWidth() + 1;
2011                 // this relies on sann being preserved after we
2012                 // modify the sequence's annotation array for each duplication
2013                 for (int a = 0; a < sann.length; a++)
2014                 {
2015                   AlignmentAnnotation newann = new AlignmentAnnotation(
2016                           sann[a]);
2017                   sequences[i].addAlignmentAnnotation(newann);
2018                   newann.padAnnotation(avwidth);
2019                   alview[avnum].addAnnotation(newann); // annotation was
2020                   // duplicated earlier
2021                   // TODO JAL-1145 graphGroups are not updated for sequence
2022                   // annotation added to several views. This may cause
2023                   // strangeness
2024                   alview[avnum].setAnnotationIndex(newann, a);
2025                 }
2026               }
2027             }
2028           }
2029           buildSortByAnnotationScoresMenu();
2030         }
2031         viewport.firePropertyChange("alignment", null,
2032                 alignment.getSequences());
2033         if (alignPanels != null)
2034         {
2035           for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
2036           {
2037             ap.validateAnnotationDimensions(false);
2038           }
2039         }
2040         else
2041         {
2042           alignPanel.validateAnnotationDimensions(false);
2043         }
2044
2045       }
2046       else
2047       {
2048         AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2049                 DEFAULT_HEIGHT);
2050         String newtitle = new String("Copied sequences");
2051
2052         if (Desktop.jalviewClipboard != null
2053                 && Desktop.jalviewClipboard[2] != null)
2054         {
2055           Vector hc = (Vector) Desktop.jalviewClipboard[2];
2056           for (int i = 0; i < hc.size(); i++)
2057           {
2058             int[] region = (int[]) hc.elementAt(i);
2059             af.viewport.hideColumns(region[0], region[1]);
2060           }
2061         }
2062
2063         // >>>This is a fix for the moment, until a better solution is
2064         // found!!<<<
2065         af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2066                 .transferSettings(
2067                         alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2068
2069         // TODO: maintain provenance of an alignment, rather than just make the
2070         // title a concatenation of operations.
2071         if (!externalPaste)
2072         {
2073           if (title.startsWith("Copied sequences"))
2074           {
2075             newtitle = title;
2076           }
2077           else
2078           {
2079             newtitle = newtitle.concat("- from " + title);
2080           }
2081         }
2082         else
2083         {
2084           newtitle = new String("Pasted sequences");
2085         }
2086
2087         Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
2088                 DEFAULT_HEIGHT);
2089
2090       }
2091
2092     } catch (Exception ex)
2093     {
2094       ex.printStackTrace();
2095       System.out.println("Exception whilst pasting: " + ex);
2096       // could be anything being pasted in here
2097     }
2098
2099   }
2100
2101   @Override
2102   protected void expand_newalign(ActionEvent e)
2103   {
2104     try
2105     {
2106       AlignmentI alignment = AlignmentUtils.expandContext(getViewport()
2107               .getAlignment(), -1);
2108       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2109               DEFAULT_HEIGHT);
2110       String newtitle = new String("Flanking alignment");
2111
2112       if (Desktop.jalviewClipboard != null
2113               && Desktop.jalviewClipboard[2] != null)
2114       {
2115         Vector hc = (Vector) Desktop.jalviewClipboard[2];
2116         for (int i = 0; i < hc.size(); i++)
2117         {
2118           int[] region = (int[]) hc.elementAt(i);
2119           af.viewport.hideColumns(region[0], region[1]);
2120         }
2121       }
2122
2123       // >>>This is a fix for the moment, until a better solution is
2124       // found!!<<<
2125       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2126               .transferSettings(
2127                       alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2128
2129       // TODO: maintain provenance of an alignment, rather than just make the
2130       // title a concatenation of operations.
2131       {
2132         if (title.startsWith("Copied sequences"))
2133         {
2134           newtitle = title;
2135         }
2136         else
2137         {
2138           newtitle = newtitle.concat("- from " + title);
2139         }
2140       }
2141
2142       Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
2143
2144     } catch (Exception ex)
2145     {
2146       ex.printStackTrace();
2147       System.out.println("Exception whilst pasting: " + ex);
2148       // could be anything being pasted in here
2149     } catch (OutOfMemoryError oom)
2150     {
2151       new OOMWarning("Viewing flanking region of alignment", oom);
2152     }
2153   }
2154
2155   /**
2156    * DOCUMENT ME!
2157    * 
2158    * @param e
2159    *          DOCUMENT ME!
2160    */
2161   @Override
2162   protected void cut_actionPerformed(ActionEvent e)
2163   {
2164     copy_actionPerformed(null);
2165     delete_actionPerformed(null);
2166   }
2167
2168   /**
2169    * DOCUMENT ME!
2170    * 
2171    * @param e
2172    *          DOCUMENT ME!
2173    */
2174   @Override
2175   protected void delete_actionPerformed(ActionEvent evt)
2176   {
2177
2178     SequenceGroup sg = viewport.getSelectionGroup();
2179     if (sg == null)
2180     {
2181       return;
2182     }
2183
2184     List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
2185     SequenceI seq;
2186     for (int i = 0; i < sg.getSize(); i++)
2187     {
2188       seq = sg.getSequenceAt(i);
2189       seqs.add(seq);
2190     }
2191
2192     // If the cut affects all sequences, warn, remove highlighted columns
2193     if (sg.getSize() == viewport.getAlignment().getHeight())
2194     {
2195       int confirm = JOptionPane.showConfirmDialog(this,
2196               MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
2197               MessageManager.getString("label.delete_all"), // $NON-NLS-1$
2198               JOptionPane.OK_CANCEL_OPTION);
2199
2200       if (confirm == JOptionPane.CANCEL_OPTION
2201               || confirm == JOptionPane.CLOSED_OPTION)
2202       {
2203         return;
2204       }
2205       viewport.getColumnSelection().removeElements(sg.getStartRes(),
2206               sg.getEndRes() + 1);
2207     }
2208
2209     SequenceI[] cut = new SequenceI[seqs.size()];
2210     for (int i = 0; i < seqs.size(); i++)
2211     {
2212       cut[i] = seqs.get(i);
2213     }
2214
2215     /*
2216      * //ADD HISTORY ITEM
2217      */
2218     addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, cut,
2219             sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
2220             viewport.getAlignment()));
2221
2222     viewport.setSelectionGroup(null);
2223     viewport.sendSelection();
2224     viewport.getAlignment().deleteGroup(sg);
2225
2226     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2227             .getSequences());
2228     if (viewport.getAlignment().getHeight() < 1)
2229     {
2230       try
2231       {
2232         this.setClosed(true);
2233       } catch (Exception ex)
2234       {
2235       }
2236     }
2237   }
2238
2239   /**
2240    * DOCUMENT ME!
2241    * 
2242    * @param e
2243    *          DOCUMENT ME!
2244    */
2245   @Override
2246   protected void deleteGroups_actionPerformed(ActionEvent e)
2247   {
2248     if (avc.deleteGroups())
2249     {
2250       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
2251       alignPanel.updateAnnotation();
2252       alignPanel.paintAlignment(true);
2253     }
2254   }
2255
2256   /**
2257    * DOCUMENT ME!
2258    * 
2259    * @param e
2260    *          DOCUMENT ME!
2261    */
2262   @Override
2263   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2264   {
2265     SequenceGroup sg = new SequenceGroup();
2266
2267     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2268     {
2269       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
2270     }
2271
2272     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
2273     viewport.setSelectionGroup(sg);
2274     viewport.sendSelection();
2275     alignPanel.paintAlignment(true);
2276     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2277   }
2278
2279   /**
2280    * DOCUMENT ME!
2281    * 
2282    * @param e
2283    *          DOCUMENT ME!
2284    */
2285   @Override
2286   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2287   {
2288     if (viewport.cursorMode)
2289     {
2290       alignPanel.seqPanel.keyboardNo1 = null;
2291       alignPanel.seqPanel.keyboardNo2 = null;
2292     }
2293     viewport.setSelectionGroup(null);
2294     viewport.getColumnSelection().clear();
2295     viewport.setSelectionGroup(null);
2296     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
2297     alignPanel.idPanel.idCanvas.searchResults = null;
2298     alignPanel.paintAlignment(true);
2299     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2300     viewport.sendSelection();
2301   }
2302
2303   /**
2304    * DOCUMENT ME!
2305    * 
2306    * @param e
2307    *          DOCUMENT ME!
2308    */
2309   @Override
2310   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
2311   {
2312     SequenceGroup sg = viewport.getSelectionGroup();
2313
2314     if (sg == null)
2315     {
2316       selectAllSequenceMenuItem_actionPerformed(null);
2317
2318       return;
2319     }
2320
2321     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2322     {
2323       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
2324     }
2325
2326     alignPanel.paintAlignment(true);
2327     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2328     viewport.sendSelection();
2329   }
2330
2331   @Override
2332   public void invertColSel_actionPerformed(ActionEvent e)
2333   {
2334     viewport.invertColumnSelection();
2335     alignPanel.paintAlignment(true);
2336     viewport.sendSelection();
2337   }
2338
2339   /**
2340    * DOCUMENT ME!
2341    * 
2342    * @param e
2343    *          DOCUMENT ME!
2344    */
2345   @Override
2346   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
2347   {
2348     trimAlignment(true);
2349   }
2350
2351   /**
2352    * DOCUMENT ME!
2353    * 
2354    * @param e
2355    *          DOCUMENT ME!
2356    */
2357   @Override
2358   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
2359   {
2360     trimAlignment(false);
2361   }
2362
2363   void trimAlignment(boolean trimLeft)
2364   {
2365     ColumnSelection colSel = viewport.getColumnSelection();
2366     int column;
2367
2368     if (colSel.size() > 0)
2369     {
2370       if (trimLeft)
2371       {
2372         column = colSel.getMin();
2373       }
2374       else
2375       {
2376         column = colSel.getMax();
2377       }
2378
2379       SequenceI[] seqs;
2380       if (viewport.getSelectionGroup() != null)
2381       {
2382         seqs = viewport.getSelectionGroup().getSequencesAsArray(
2383                 viewport.getHiddenRepSequences());
2384       }
2385       else
2386       {
2387         seqs = viewport.getAlignment().getSequencesArray();
2388       }
2389
2390       TrimRegionCommand trimRegion;
2391       if (trimLeft)
2392       {
2393         trimRegion = new TrimRegionCommand("Remove Left",
2394                 TrimRegionCommand.TRIM_LEFT, seqs, column,
2395                 viewport.getAlignment(), viewport.getColumnSelection(),
2396                 viewport.getSelectionGroup());
2397         viewport.setStartRes(0);
2398       }
2399       else
2400       {
2401         trimRegion = new TrimRegionCommand("Remove Right",
2402                 TrimRegionCommand.TRIM_RIGHT, seqs, column,
2403                 viewport.getAlignment(), viewport.getColumnSelection(),
2404                 viewport.getSelectionGroup());
2405       }
2406
2407       statusBar.setText(MessageManager.formatMessage(
2408               "label.removed_columns", new String[]
2409               { Integer.valueOf(trimRegion.getSize()).toString() }));
2410
2411       addHistoryItem(trimRegion);
2412
2413       for (SequenceGroup sg : viewport.getAlignment().getGroups())
2414       {
2415         if ((trimLeft && !sg.adjustForRemoveLeft(column))
2416                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
2417         {
2418           viewport.getAlignment().deleteGroup(sg);
2419         }
2420       }
2421
2422       viewport.firePropertyChange("alignment", null, viewport
2423               .getAlignment().getSequences());
2424     }
2425   }
2426
2427   /**
2428    * DOCUMENT ME!
2429    * 
2430    * @param e
2431    *          DOCUMENT ME!
2432    */
2433   @Override
2434   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2435   {
2436     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2437
2438     SequenceI[] seqs;
2439     if (viewport.getSelectionGroup() != null)
2440     {
2441       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2442               viewport.getHiddenRepSequences());
2443       start = viewport.getSelectionGroup().getStartRes();
2444       end = viewport.getSelectionGroup().getEndRes();
2445     }
2446     else
2447     {
2448       seqs = viewport.getAlignment().getSequencesArray();
2449     }
2450
2451     RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2452             "Remove Gapped Columns", seqs, start, end,
2453             viewport.getAlignment());
2454
2455     addHistoryItem(removeGapCols);
2456
2457     statusBar.setText(MessageManager.formatMessage(
2458             "label.removed_empty_columns", new String[]
2459             { Integer.valueOf(removeGapCols.getSize()).toString() }));
2460
2461     // This is to maintain viewport position on first residue
2462     // of first sequence
2463     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2464     int startRes = seq.findPosition(viewport.startRes);
2465     // ShiftList shifts;
2466     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2467     // edit.alColumnChanges=shifts.getInverse();
2468     // if (viewport.hasHiddenColumns)
2469     // viewport.getColumnSelection().compensateForEdits(shifts);
2470     viewport.setStartRes(seq.findIndex(startRes) - 1);
2471     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2472             .getSequences());
2473
2474   }
2475
2476   /**
2477    * DOCUMENT ME!
2478    * 
2479    * @param e
2480    *          DOCUMENT ME!
2481    */
2482   @Override
2483   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2484   {
2485     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2486
2487     SequenceI[] seqs;
2488     if (viewport.getSelectionGroup() != null)
2489     {
2490       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2491               viewport.getHiddenRepSequences());
2492       start = viewport.getSelectionGroup().getStartRes();
2493       end = viewport.getSelectionGroup().getEndRes();
2494     }
2495     else
2496     {
2497       seqs = viewport.getAlignment().getSequencesArray();
2498     }
2499
2500     // This is to maintain viewport position on first residue
2501     // of first sequence
2502     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2503     int startRes = seq.findPosition(viewport.startRes);
2504
2505     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
2506             viewport.getAlignment()));
2507
2508     viewport.setStartRes(seq.findIndex(startRes) - 1);
2509
2510     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2511             .getSequences());
2512
2513   }
2514
2515   /**
2516    * DOCUMENT ME!
2517    * 
2518    * @param e
2519    *          DOCUMENT ME!
2520    */
2521   @Override
2522   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2523   {
2524     viewport.setPadGaps(padGapsMenuitem.isSelected());
2525     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2526             .getSequences());
2527   }
2528
2529   // else
2530   {
2531     // if (justifySeqs>0)
2532     {
2533       // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
2534     }
2535   }
2536
2537   // }
2538
2539   /**
2540    * DOCUMENT ME!
2541    * 
2542    * @param e
2543    *          DOCUMENT ME!
2544    */
2545   @Override
2546   public void findMenuItem_actionPerformed(ActionEvent e)
2547   {
2548     new Finder();
2549   }
2550
2551   @Override
2552   public void newView_actionPerformed(ActionEvent e)
2553   {
2554     newView(true);
2555   }
2556
2557   /**
2558    * 
2559    * @param copyAnnotation
2560    *          if true then duplicate all annnotation, groups and settings
2561    * @return new alignment panel, already displayed.
2562    */
2563   public AlignmentPanel newView(boolean copyAnnotation)
2564   {
2565     return newView(null, copyAnnotation);
2566   }
2567
2568   /**
2569    * 
2570    * @param viewTitle
2571    *          title of newly created view
2572    * @return new alignment panel, already displayed.
2573    */
2574   public AlignmentPanel newView(String viewTitle)
2575   {
2576     return newView(viewTitle, true);
2577   }
2578
2579   /**
2580    * 
2581    * @param viewTitle
2582    *          title of newly created view
2583    * @param copyAnnotation
2584    *          if true then duplicate all annnotation, groups and settings
2585    * @return new alignment panel, already displayed.
2586    */
2587   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
2588   {
2589     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
2590             true);
2591     if (!copyAnnotation)
2592     {
2593       // just remove all the current annotation except for the automatic stuff
2594       newap.av.getAlignment().deleteAllGroups();
2595       for (AlignmentAnnotation alan : newap.av.getAlignment()
2596               .getAlignmentAnnotation())
2597       {
2598         if (!alan.autoCalculated)
2599         {
2600           newap.av.getAlignment().deleteAnnotation(alan);
2601         }
2602         ;
2603       }
2604     }
2605
2606     newap.av.gatherViewsHere = false;
2607
2608     if (viewport.viewName == null)
2609     {
2610       viewport.viewName = "Original";
2611     }
2612
2613     newap.av.historyList = viewport.historyList;
2614     newap.av.redoList = viewport.redoList;
2615
2616     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2617     // make sure the new view has a unique name - this is essential for Jalview
2618     // 2 archives
2619     boolean addFirstIndex = false;
2620     if (viewTitle == null || viewTitle.trim().length() == 0)
2621     {
2622       viewTitle = "View";
2623       addFirstIndex = true;
2624     }
2625     else
2626     {
2627       index = 1;// we count from 1 if given a specific name
2628     }
2629     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
2630     Vector comps = (Vector) PaintRefresher.components.get(viewport
2631             .getSequenceSetId());
2632     Vector existingNames = new Vector();
2633     for (int i = 0; i < comps.size(); i++)
2634     {
2635       if (comps.elementAt(i) instanceof AlignmentPanel)
2636       {
2637         AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
2638         if (!existingNames.contains(ap.av.viewName))
2639         {
2640           existingNames.addElement(ap.av.viewName);
2641         }
2642       }
2643     }
2644
2645     while (existingNames.contains(newViewName))
2646     {
2647       newViewName = viewTitle + " " + (++index);
2648     }
2649
2650     newap.av.viewName = newViewName;
2651
2652     addAlignmentPanel(newap, true);
2653     newap.alignmentChanged();
2654
2655     if (alignPanels.size() == 2)
2656     {
2657       viewport.gatherViewsHere = true;
2658     }
2659     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2660     return newap;
2661   }
2662
2663   @Override
2664   public void expandViews_actionPerformed(ActionEvent e)
2665   {
2666     Desktop.instance.explodeViews(this);
2667   }
2668
2669   @Override
2670   public void gatherViews_actionPerformed(ActionEvent e)
2671   {
2672     Desktop.instance.gatherViews(this);
2673   }
2674
2675   /**
2676    * DOCUMENT ME!
2677    * 
2678    * @param e
2679    *          DOCUMENT ME!
2680    */
2681   @Override
2682   public void font_actionPerformed(ActionEvent e)
2683   {
2684     new FontChooser(alignPanel);
2685   }
2686
2687   /**
2688    * DOCUMENT ME!
2689    * 
2690    * @param e
2691    *          DOCUMENT ME!
2692    */
2693   @Override
2694   protected void seqLimit_actionPerformed(ActionEvent e)
2695   {
2696     viewport.setShowJVSuffix(seqLimits.isSelected());
2697
2698     alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel
2699             .calculateIdWidth());
2700     alignPanel.paintAlignment(true);
2701   }
2702
2703   @Override
2704   public void idRightAlign_actionPerformed(ActionEvent e)
2705   {
2706     viewport.rightAlignIds = idRightAlign.isSelected();
2707     alignPanel.paintAlignment(true);
2708   }
2709
2710   @Override
2711   public void centreColumnLabels_actionPerformed(ActionEvent e)
2712   {
2713     viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
2714     alignPanel.paintAlignment(true);
2715   }
2716
2717   /*
2718    * (non-Javadoc)
2719    * 
2720    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
2721    */
2722   @Override
2723   protected void followHighlight_actionPerformed()
2724   {
2725     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
2726     {
2727       alignPanel.scrollToPosition(
2728               alignPanel.seqPanel.seqCanvas.searchResults, false);
2729     }
2730   }
2731
2732   /**
2733    * DOCUMENT ME!
2734    * 
2735    * @param e
2736    *          DOCUMENT ME!
2737    */
2738   @Override
2739   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2740   {
2741     viewport.setColourText(colourTextMenuItem.isSelected());
2742     alignPanel.paintAlignment(true);
2743   }
2744
2745   /**
2746    * DOCUMENT ME!
2747    * 
2748    * @param e
2749    *          DOCUMENT ME!
2750    */
2751   @Override
2752   public void wrapMenuItem_actionPerformed(ActionEvent e)
2753   {
2754     scaleAbove.setVisible(wrapMenuItem.isSelected());
2755     scaleLeft.setVisible(wrapMenuItem.isSelected());
2756     scaleRight.setVisible(wrapMenuItem.isSelected());
2757     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2758     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
2759   }
2760
2761   @Override
2762   public void showAllSeqs_actionPerformed(ActionEvent e)
2763   {
2764     viewport.showAllHiddenSeqs();
2765   }
2766
2767   @Override
2768   public void showAllColumns_actionPerformed(ActionEvent e)
2769   {
2770     viewport.showAllHiddenColumns();
2771     repaint();
2772   }
2773
2774   @Override
2775   public void hideSelSequences_actionPerformed(ActionEvent e)
2776   {
2777     viewport.hideAllSelectedSeqs();
2778     alignPanel.paintAlignment(true);
2779   }
2780
2781   /**
2782    * called by key handler and the hide all/show all menu items
2783    * 
2784    * @param toggleSeqs
2785    * @param toggleCols
2786    */
2787   private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
2788   {
2789
2790     boolean hide = false;
2791     SequenceGroup sg = viewport.getSelectionGroup();
2792     if (!toggleSeqs && !toggleCols)
2793     {
2794       // Hide everything by the current selection - this is a hack - we do the
2795       // invert and then hide
2796       // first check that there will be visible columns after the invert.
2797       if ((viewport.getColumnSelection() != null
2798               && viewport.getColumnSelection().getSelected() != null && viewport
2799               .getColumnSelection().getSelected().size() > 0)
2800               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
2801                       .getEndRes()))
2802       {
2803         // now invert the sequence set, if required - empty selection implies
2804         // that no hiding is required.
2805         if (sg != null)
2806         {
2807           invertSequenceMenuItem_actionPerformed(null);
2808           sg = viewport.getSelectionGroup();
2809           toggleSeqs = true;
2810
2811         }
2812         viewport.expandColSelection(sg, true);
2813         // finally invert the column selection and get the new sequence
2814         // selection.
2815         invertColSel_actionPerformed(null);
2816         toggleCols = true;
2817       }
2818     }
2819
2820     if (toggleSeqs)
2821     {
2822       if (sg != null && sg.getSize() != viewport.getAlignment().getHeight())
2823       {
2824         hideSelSequences_actionPerformed(null);
2825         hide = true;
2826       }
2827       else if (!(toggleCols && viewport.getColumnSelection().getSelected()
2828               .size() > 0))
2829       {
2830         showAllSeqs_actionPerformed(null);
2831       }
2832     }
2833
2834     if (toggleCols)
2835     {
2836       if (viewport.getColumnSelection().getSelected().size() > 0)
2837       {
2838         hideSelColumns_actionPerformed(null);
2839         if (!toggleSeqs)
2840         {
2841           viewport.setSelectionGroup(sg);
2842         }
2843       }
2844       else if (!hide)
2845       {
2846         showAllColumns_actionPerformed(null);
2847       }
2848     }
2849   }
2850
2851   /*
2852    * (non-Javadoc)
2853    * 
2854    * @see
2855    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
2856    * event.ActionEvent)
2857    */
2858   @Override
2859   public void hideAllButSelection_actionPerformed(ActionEvent e)
2860   {
2861     toggleHiddenRegions(false, false);
2862   }
2863
2864   /*
2865    * (non-Javadoc)
2866    * 
2867    * @see
2868    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
2869    * .ActionEvent)
2870    */
2871   @Override
2872   public void hideAllSelection_actionPerformed(ActionEvent e)
2873   {
2874     SequenceGroup sg = viewport.getSelectionGroup();
2875     viewport.expandColSelection(sg, false);
2876     viewport.hideAllSelectedSeqs();
2877     viewport.hideSelectedColumns();
2878     alignPanel.paintAlignment(true);
2879   }
2880
2881   /*
2882    * (non-Javadoc)
2883    * 
2884    * @see
2885    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
2886    * ActionEvent)
2887    */
2888   @Override
2889   public void showAllhidden_actionPerformed(ActionEvent e)
2890   {
2891     viewport.showAllHiddenColumns();
2892     viewport.showAllHiddenSeqs();
2893     alignPanel.paintAlignment(true);
2894   }
2895
2896   @Override
2897   public void hideSelColumns_actionPerformed(ActionEvent e)
2898   {
2899     viewport.hideSelectedColumns();
2900     alignPanel.paintAlignment(true);
2901   }
2902
2903   @Override
2904   public void hiddenMarkers_actionPerformed(ActionEvent e)
2905   {
2906     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
2907     repaint();
2908   }
2909
2910   /**
2911    * DOCUMENT ME!
2912    * 
2913    * @param e
2914    *          DOCUMENT ME!
2915    */
2916   @Override
2917   protected void scaleAbove_actionPerformed(ActionEvent e)
2918   {
2919     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
2920     alignPanel.paintAlignment(true);
2921   }
2922
2923   /**
2924    * DOCUMENT ME!
2925    * 
2926    * @param e
2927    *          DOCUMENT ME!
2928    */
2929   @Override
2930   protected void scaleLeft_actionPerformed(ActionEvent e)
2931   {
2932     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
2933     alignPanel.paintAlignment(true);
2934   }
2935
2936   /**
2937    * DOCUMENT ME!
2938    * 
2939    * @param e
2940    *          DOCUMENT ME!
2941    */
2942   @Override
2943   protected void scaleRight_actionPerformed(ActionEvent e)
2944   {
2945     viewport.setScaleRightWrapped(scaleRight.isSelected());
2946     alignPanel.paintAlignment(true);
2947   }
2948
2949   /**
2950    * DOCUMENT ME!
2951    * 
2952    * @param e
2953    *          DOCUMENT ME!
2954    */
2955   @Override
2956   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
2957   {
2958     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
2959     alignPanel.paintAlignment(true);
2960   }
2961
2962   /**
2963    * DOCUMENT ME!
2964    * 
2965    * @param e
2966    *          DOCUMENT ME!
2967    */
2968   @Override
2969   public void viewTextMenuItem_actionPerformed(ActionEvent e)
2970   {
2971     viewport.setShowText(viewTextMenuItem.isSelected());
2972     alignPanel.paintAlignment(true);
2973   }
2974
2975   /**
2976    * DOCUMENT ME!
2977    * 
2978    * @param e
2979    *          DOCUMENT ME!
2980    */
2981   @Override
2982   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
2983   {
2984     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
2985     alignPanel.paintAlignment(true);
2986   }
2987
2988   public FeatureSettings featureSettings;
2989
2990   @Override
2991   public void featureSettings_actionPerformed(ActionEvent e)
2992   {
2993     if (featureSettings != null)
2994     {
2995       featureSettings.close();
2996       featureSettings = null;
2997     }
2998     if (!showSeqFeatures.isSelected())
2999     {
3000       // make sure features are actually displayed
3001       showSeqFeatures.setSelected(true);
3002       showSeqFeatures_actionPerformed(null);
3003     }
3004     featureSettings = new FeatureSettings(this);
3005   }
3006
3007   /**
3008    * Set or clear 'Show Sequence Features'
3009    * 
3010    * @param evt
3011    *          DOCUMENT ME!
3012    */
3013   @Override
3014   public void showSeqFeatures_actionPerformed(ActionEvent evt)
3015   {
3016     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
3017     alignPanel.paintAlignment(true);
3018     if (alignPanel.getOverviewPanel() != null)
3019     {
3020       alignPanel.getOverviewPanel().updateOverviewImage();
3021     }
3022   }
3023
3024   /**
3025    * Set or clear 'Show Sequence Features'
3026    * 
3027    * @param evt
3028    *          DOCUMENT ME!
3029    */
3030   @Override
3031   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
3032   {
3033     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
3034             .isSelected());
3035     if (viewport.getShowSequenceFeaturesHeight())
3036     {
3037       // ensure we're actually displaying features
3038       viewport.setShowSequenceFeatures(true);
3039       showSeqFeatures.setSelected(true);
3040     }
3041     alignPanel.paintAlignment(true);
3042     if (alignPanel.getOverviewPanel() != null)
3043     {
3044       alignPanel.getOverviewPanel().updateOverviewImage();
3045     }
3046   }
3047
3048   /**
3049    * Action on toggle of the 'Show annotations' menu item. This shows or hides
3050    * the annotations panel as a whole.
3051    * 
3052    * The options to show/hide all annotations should be enabled when the panel
3053    * is shown, and disabled when the panel is hidden.
3054    * 
3055    * @param e
3056    */
3057   @Override
3058   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
3059   {
3060     final boolean setVisible = annotationPanelMenuItem.isSelected();
3061     viewport.setShowAnnotation(setVisible);
3062     alignPanel.setAnnotationVisible(setVisible);
3063     this.showAllAnnotations.setEnabled(setVisible);
3064     this.hideAllAnnotations.setEnabled(setVisible);
3065   }
3066
3067   @Override
3068   public void alignmentProperties()
3069   {
3070     JEditorPane editPane = new JEditorPane("text/html", "");
3071     editPane.setEditable(false);
3072     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
3073             .formatAsHtml();
3074     editPane.setText(MessageManager.formatMessage("label.html_content",
3075             new String[]
3076             { contents.toString() }));
3077     JInternalFrame frame = new JInternalFrame();
3078     frame.getContentPane().add(new JScrollPane(editPane));
3079
3080     Desktop.instance.addInternalFrame(frame, MessageManager.formatMessage(
3081             "label.alignment_properties", new String[]
3082             { getTitle() }), 500, 400);
3083   }
3084
3085   /**
3086    * DOCUMENT ME!
3087    * 
3088    * @param e
3089    *          DOCUMENT ME!
3090    */
3091   @Override
3092   public void overviewMenuItem_actionPerformed(ActionEvent e)
3093   {
3094     if (alignPanel.overviewPanel != null)
3095     {
3096       return;
3097     }
3098
3099     JInternalFrame frame = new JInternalFrame();
3100     OverviewPanel overview = new OverviewPanel(alignPanel);
3101     frame.setContentPane(overview);
3102     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3103             "label.overview_params", new String[]
3104             { this.getTitle() }), frame.getWidth(), frame.getHeight());
3105     frame.pack();
3106     frame.setLayer(JLayeredPane.PALETTE_LAYER);
3107     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
3108     {
3109       @Override
3110       public void internalFrameClosed(
3111               javax.swing.event.InternalFrameEvent evt)
3112       {
3113         alignPanel.setOverviewPanel(null);
3114       };
3115     });
3116
3117     alignPanel.setOverviewPanel(overview);
3118   }
3119
3120   @Override
3121   public void textColour_actionPerformed(ActionEvent e)
3122   {
3123     new TextColourChooser().chooseColour(alignPanel, null);
3124   }
3125
3126   /**
3127    * DOCUMENT ME!
3128    * 
3129    * @param e
3130    *          DOCUMENT ME!
3131    */
3132   @Override
3133   protected void noColourmenuItem_actionPerformed(ActionEvent e)
3134   {
3135     changeColour(null);
3136   }
3137
3138   /**
3139    * DOCUMENT ME!
3140    * 
3141    * @param e
3142    *          DOCUMENT ME!
3143    */
3144   @Override
3145   public void clustalColour_actionPerformed(ActionEvent e)
3146   {
3147     changeColour(new ClustalxColourScheme(viewport.getAlignment(),
3148             viewport.getHiddenRepSequences()));
3149   }
3150
3151   /**
3152    * DOCUMENT ME!
3153    * 
3154    * @param e
3155    *          DOCUMENT ME!
3156    */
3157   @Override
3158   public void zappoColour_actionPerformed(ActionEvent e)
3159   {
3160     changeColour(new ZappoColourScheme());
3161   }
3162
3163   /**
3164    * DOCUMENT ME!
3165    * 
3166    * @param e
3167    *          DOCUMENT ME!
3168    */
3169   @Override
3170   public void taylorColour_actionPerformed(ActionEvent e)
3171   {
3172     changeColour(new TaylorColourScheme());
3173   }
3174
3175   /**
3176    * DOCUMENT ME!
3177    * 
3178    * @param e
3179    *          DOCUMENT ME!
3180    */
3181   @Override
3182   public void hydrophobicityColour_actionPerformed(ActionEvent e)
3183   {
3184     changeColour(new HydrophobicColourScheme());
3185   }
3186
3187   /**
3188    * DOCUMENT ME!
3189    * 
3190    * @param e
3191    *          DOCUMENT ME!
3192    */
3193   @Override
3194   public void helixColour_actionPerformed(ActionEvent e)
3195   {
3196     changeColour(new HelixColourScheme());
3197   }
3198
3199   /**
3200    * DOCUMENT ME!
3201    * 
3202    * @param e
3203    *          DOCUMENT ME!
3204    */
3205   @Override
3206   public void strandColour_actionPerformed(ActionEvent e)
3207   {
3208     changeColour(new StrandColourScheme());
3209   }
3210
3211   /**
3212    * DOCUMENT ME!
3213    * 
3214    * @param e
3215    *          DOCUMENT ME!
3216    */
3217   @Override
3218   public void turnColour_actionPerformed(ActionEvent e)
3219   {
3220     changeColour(new TurnColourScheme());
3221   }
3222
3223   /**
3224    * DOCUMENT ME!
3225    * 
3226    * @param e
3227    *          DOCUMENT ME!
3228    */
3229   @Override
3230   public void buriedColour_actionPerformed(ActionEvent e)
3231   {
3232     changeColour(new BuriedColourScheme());
3233   }
3234
3235   /**
3236    * DOCUMENT ME!
3237    * 
3238    * @param e
3239    *          DOCUMENT ME!
3240    */
3241   @Override
3242   public void nucleotideColour_actionPerformed(ActionEvent e)
3243   {
3244     changeColour(new NucleotideColourScheme());
3245   }
3246
3247   @Override
3248   public void purinePyrimidineColour_actionPerformed(ActionEvent e)
3249   {
3250     changeColour(new PurinePyrimidineColourScheme());
3251   }
3252
3253   /*
3254    * public void covariationColour_actionPerformed(ActionEvent e) {
3255    * changeColour(new
3256    * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
3257    * ()[0])); }
3258    */
3259   @Override
3260   public void annotationColour_actionPerformed(ActionEvent e)
3261   {
3262     new AnnotationColourChooser(viewport, alignPanel);
3263   }
3264
3265   @Override
3266   public void rnahelicesColour_actionPerformed(ActionEvent e)
3267   {
3268     new RNAHelicesColourChooser(viewport, alignPanel);
3269   }
3270
3271   /**
3272    * DOCUMENT ME!
3273    * 
3274    * @param e
3275    *          DOCUMENT ME!
3276    */
3277   @Override
3278   protected void applyToAllGroups_actionPerformed(ActionEvent e)
3279   {
3280     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
3281   }
3282
3283   /**
3284    * DOCUMENT ME!
3285    * 
3286    * @param cs
3287    *          DOCUMENT ME!
3288    */
3289   public void changeColour(ColourSchemeI cs)
3290   {
3291     // TODO: compare with applet and pull up to model method
3292     int threshold = 0;
3293
3294     if (cs != null)
3295     {
3296       if (viewport.getAbovePIDThreshold())
3297       {
3298         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
3299                 "Background");
3300         cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3301       }
3302       else
3303       {
3304         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3305       }
3306
3307       if (viewport.getConservationSelected())
3308       {
3309
3310         Alignment al = (Alignment) viewport.getAlignment();
3311         Conservation c = new Conservation("All",
3312                 ResidueProperties.propHash, 3, al.getSequences(), 0,
3313                 al.getWidth() - 1);
3314
3315         c.calculate();
3316         c.verdict(false, viewport.getConsPercGaps());
3317
3318         cs.setConservation(c);
3319
3320         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
3321                 cs, "Background"));
3322       }
3323       else
3324       {
3325         cs.setConservation(null);
3326       }
3327
3328       cs.setConsensus(viewport.getSequenceConsensusHash());
3329     }
3330
3331     viewport.setGlobalColourScheme(cs);
3332
3333     if (viewport.getColourAppliesToAllGroups())
3334     {
3335
3336       for (SequenceGroup sg : viewport.getAlignment().getGroups())
3337       {
3338         if (cs == null)
3339         {
3340           sg.cs = null;
3341           continue;
3342         }
3343
3344         if (cs instanceof ClustalxColourScheme)
3345         {
3346           sg.cs = new ClustalxColourScheme(sg,
3347                   viewport.getHiddenRepSequences());
3348         }
3349         else if (cs instanceof UserColourScheme)
3350         {
3351           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
3352         }
3353         else
3354         {
3355           try
3356           {
3357             sg.cs = cs.getClass().newInstance();
3358           } catch (Exception ex)
3359           {
3360           }
3361         }
3362
3363         if (viewport.getAbovePIDThreshold()
3364                 || cs instanceof PIDColourScheme
3365                 || cs instanceof Blosum62ColourScheme)
3366         {
3367           sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3368
3369           sg.cs.setConsensus(AAFrequency.calculate(
3370                   sg.getSequences(viewport.getHiddenRepSequences()),
3371                   sg.getStartRes(), sg.getEndRes() + 1));
3372         }
3373         else
3374         {
3375           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3376         }
3377
3378         if (viewport.getConservationSelected())
3379         {
3380           Conservation c = new Conservation("Group",
3381                   ResidueProperties.propHash, 3, sg.getSequences(viewport
3382                           .getHiddenRepSequences()), sg.getStartRes(),
3383                   sg.getEndRes() + 1);
3384           c.calculate();
3385           c.verdict(false, viewport.getConsPercGaps());
3386           sg.cs.setConservation(c);
3387         }
3388         else
3389         {
3390           sg.cs.setConservation(null);
3391         }
3392       }
3393     }
3394
3395     if (alignPanel.getOverviewPanel() != null)
3396     {
3397       alignPanel.getOverviewPanel().updateOverviewImage();
3398     }
3399
3400     alignPanel.paintAlignment(true);
3401   }
3402
3403   /**
3404    * DOCUMENT ME!
3405    * 
3406    * @param e
3407    *          DOCUMENT ME!
3408    */
3409   @Override
3410   protected void modifyPID_actionPerformed(ActionEvent e)
3411   {
3412     if (viewport.getAbovePIDThreshold()
3413             && viewport.getGlobalColourScheme() != null)
3414     {
3415       SliderPanel.setPIDSliderSource(alignPanel,
3416               viewport.getGlobalColourScheme(), "Background");
3417       SliderPanel.showPIDSlider();
3418     }
3419   }
3420
3421   /**
3422    * DOCUMENT ME!
3423    * 
3424    * @param e
3425    *          DOCUMENT ME!
3426    */
3427   @Override
3428   protected void modifyConservation_actionPerformed(ActionEvent e)
3429   {
3430     if (viewport.getConservationSelected()
3431             && viewport.getGlobalColourScheme() != null)
3432     {
3433       SliderPanel.setConservationSlider(alignPanel,
3434               viewport.getGlobalColourScheme(), "Background");
3435       SliderPanel.showConservationSlider();
3436     }
3437   }
3438
3439   /**
3440    * DOCUMENT ME!
3441    * 
3442    * @param e
3443    *          DOCUMENT ME!
3444    */
3445   @Override
3446   protected void conservationMenuItem_actionPerformed(ActionEvent e)
3447   {
3448     viewport.setConservationSelected(conservationMenuItem.isSelected());
3449
3450     viewport.setAbovePIDThreshold(false);
3451     abovePIDThreshold.setSelected(false);
3452
3453     changeColour(viewport.getGlobalColourScheme());
3454
3455     modifyConservation_actionPerformed(null);
3456   }
3457
3458   /**
3459    * DOCUMENT ME!
3460    * 
3461    * @param e
3462    *          DOCUMENT ME!
3463    */
3464   @Override
3465   public void abovePIDThreshold_actionPerformed(ActionEvent e)
3466   {
3467     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
3468
3469     conservationMenuItem.setSelected(false);
3470     viewport.setConservationSelected(false);
3471
3472     changeColour(viewport.getGlobalColourScheme());
3473
3474     modifyPID_actionPerformed(null);
3475   }
3476
3477   /**
3478    * DOCUMENT ME!
3479    * 
3480    * @param e
3481    *          DOCUMENT ME!
3482    */
3483   @Override
3484   public void userDefinedColour_actionPerformed(ActionEvent e)
3485   {
3486     if (e.getActionCommand().equals(
3487             MessageManager.getString("action.user_defined")))
3488     {
3489       new UserDefinedColours(alignPanel, null);
3490     }
3491     else
3492     {
3493       UserColourScheme udc = (UserColourScheme) UserDefinedColours
3494               .getUserColourSchemes().get(e.getActionCommand());
3495
3496       changeColour(udc);
3497     }
3498   }
3499
3500   public void updateUserColourMenu()
3501   {
3502
3503     Component[] menuItems = colourMenu.getMenuComponents();
3504     int i, iSize = menuItems.length;
3505     for (i = 0; i < iSize; i++)
3506     {
3507       if (menuItems[i].getName() != null
3508               && menuItems[i].getName().equals("USER_DEFINED"))
3509       {
3510         colourMenu.remove(menuItems[i]);
3511         iSize--;
3512       }
3513     }
3514     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
3515     {
3516       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
3517               .getUserColourSchemes().keys();
3518
3519       while (userColours.hasMoreElements())
3520       {
3521         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
3522                 userColours.nextElement().toString());
3523         radioItem.setName("USER_DEFINED");
3524         radioItem.addMouseListener(new MouseAdapter()
3525         {
3526           @Override
3527           public void mousePressed(MouseEvent evt)
3528           {
3529             if (evt.isControlDown()
3530                     || SwingUtilities.isRightMouseButton(evt))
3531             {
3532               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
3533
3534               int option = JOptionPane.showInternalConfirmDialog(
3535                       jalview.gui.Desktop.desktop,
3536                       MessageManager
3537                               .getString("label.remove_from_default_list"),
3538                       MessageManager
3539                               .getString("label.remove_user_defined_colour"),
3540                       JOptionPane.YES_NO_OPTION);
3541               if (option == JOptionPane.YES_OPTION)
3542               {
3543                 jalview.gui.UserDefinedColours
3544                         .removeColourFromDefaults(radioItem.getText());
3545                 colourMenu.remove(radioItem);
3546               }
3547               else
3548               {
3549                 radioItem.addActionListener(new ActionListener()
3550                 {
3551                   @Override
3552                   public void actionPerformed(ActionEvent evt)
3553                   {
3554                     userDefinedColour_actionPerformed(evt);
3555                   }
3556                 });
3557               }
3558             }
3559           }
3560         });
3561         radioItem.addActionListener(new ActionListener()
3562         {
3563           @Override
3564           public void actionPerformed(ActionEvent evt)
3565           {
3566             userDefinedColour_actionPerformed(evt);
3567           }
3568         });
3569
3570         colourMenu.insert(radioItem, 15);
3571         colours.add(radioItem);
3572       }
3573     }
3574   }
3575
3576   /**
3577    * DOCUMENT ME!
3578    * 
3579    * @param e
3580    *          DOCUMENT ME!
3581    */
3582   @Override
3583   public void PIDColour_actionPerformed(ActionEvent e)
3584   {
3585     changeColour(new PIDColourScheme());
3586   }
3587
3588   /**
3589    * DOCUMENT ME!
3590    * 
3591    * @param e
3592    *          DOCUMENT ME!
3593    */
3594   @Override
3595   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
3596   {
3597     changeColour(new Blosum62ColourScheme());
3598   }
3599
3600   /**
3601    * DOCUMENT ME!
3602    * 
3603    * @param e
3604    *          DOCUMENT ME!
3605    */
3606   @Override
3607   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3608   {
3609     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3610     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
3611             .getAlignment().getSequenceAt(0), null);
3612     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3613             viewport.getAlignment()));
3614     alignPanel.paintAlignment(true);
3615   }
3616
3617   /**
3618    * DOCUMENT ME!
3619    * 
3620    * @param e
3621    *          DOCUMENT ME!
3622    */
3623   @Override
3624   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3625   {
3626     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3627     AlignmentSorter.sortByID(viewport.getAlignment());
3628     addHistoryItem(new OrderCommand("ID Sort", oldOrder,
3629             viewport.getAlignment()));
3630     alignPanel.paintAlignment(true);
3631   }
3632
3633   /**
3634    * DOCUMENT ME!
3635    * 
3636    * @param e
3637    *          DOCUMENT ME!
3638    */
3639   @Override
3640   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3641   {
3642     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3643     AlignmentSorter.sortByLength(viewport.getAlignment());
3644     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3645             viewport.getAlignment()));
3646     alignPanel.paintAlignment(true);
3647   }
3648
3649   /**
3650    * DOCUMENT ME!
3651    * 
3652    * @param e
3653    *          DOCUMENT ME!
3654    */
3655   @Override
3656   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3657   {
3658     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3659     AlignmentSorter.sortByGroup(viewport.getAlignment());
3660     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3661             viewport.getAlignment()));
3662
3663     alignPanel.paintAlignment(true);
3664   }
3665
3666   /**
3667    * DOCUMENT ME!
3668    * 
3669    * @param e
3670    *          DOCUMENT ME!
3671    */
3672   @Override
3673   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3674   {
3675     new RedundancyPanel(alignPanel, this);
3676   }
3677
3678   /**
3679    * DOCUMENT ME!
3680    * 
3681    * @param e
3682    *          DOCUMENT ME!
3683    */
3684   @Override
3685   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3686   {
3687     if ((viewport.getSelectionGroup() == null)
3688             || (viewport.getSelectionGroup().getSize() < 2))
3689     {
3690       JOptionPane.showInternalMessageDialog(this, MessageManager
3691               .getString("label.you_must_select_least_two_sequences"),
3692               MessageManager.getString("label.invalid_selection"),
3693               JOptionPane.WARNING_MESSAGE);
3694     }
3695     else
3696     {
3697       JInternalFrame frame = new JInternalFrame();
3698       frame.setContentPane(new PairwiseAlignPanel(viewport));
3699       Desktop.addInternalFrame(frame,
3700               MessageManager.getString("action.pairwise_alignment"), 600,
3701               500);
3702     }
3703   }
3704
3705   /**
3706    * DOCUMENT ME!
3707    * 
3708    * @param e
3709    *          DOCUMENT ME!
3710    */
3711   @Override
3712   public void PCAMenuItem_actionPerformed(ActionEvent e)
3713   {
3714     if (((viewport.getSelectionGroup() != null)
3715             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
3716             .getSelectionGroup().getSize() > 0))
3717             || (viewport.getAlignment().getHeight() < 4))
3718     {
3719       JOptionPane
3720               .showInternalMessageDialog(
3721                       this,
3722                       MessageManager
3723                               .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
3724                       MessageManager
3725                               .getString("label.sequence_selection_insufficient"),
3726                       JOptionPane.WARNING_MESSAGE);
3727
3728       return;
3729     }
3730
3731     new PCAPanel(alignPanel);
3732   }
3733
3734   @Override
3735   public void autoCalculate_actionPerformed(ActionEvent e)
3736   {
3737     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3738     if (viewport.autoCalculateConsensus)
3739     {
3740       viewport.firePropertyChange("alignment", null, viewport
3741               .getAlignment().getSequences());
3742     }
3743   }
3744
3745   @Override
3746   public void sortByTreeOption_actionPerformed(ActionEvent e)
3747   {
3748     viewport.sortByTree = sortByTree.isSelected();
3749   }
3750
3751   @Override
3752   protected void listenToViewSelections_actionPerformed(ActionEvent e)
3753   {
3754     viewport.followSelection = listenToViewSelections.isSelected();
3755   }
3756
3757   /**
3758    * DOCUMENT ME!
3759    * 
3760    * @param e
3761    *          DOCUMENT ME!
3762    */
3763   @Override
3764   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3765   {
3766     NewTreePanel("AV", "PID", "Average distance tree using PID");
3767   }
3768
3769   /**
3770    * DOCUMENT ME!
3771    * 
3772    * @param e
3773    *          DOCUMENT ME!
3774    */
3775   @Override
3776   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3777   {
3778     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3779   }
3780
3781   /**
3782    * DOCUMENT ME!
3783    * 
3784    * @param e
3785    *          DOCUMENT ME!
3786    */
3787   @Override
3788   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3789   {
3790     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3791   }
3792
3793   /**
3794    * DOCUMENT ME!
3795    * 
3796    * @param e
3797    *          DOCUMENT ME!
3798    */
3799   @Override
3800   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3801   {
3802     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3803   }
3804
3805   /**
3806    * DOCUMENT ME!
3807    * 
3808    * @param type
3809    *          DOCUMENT ME!
3810    * @param pwType
3811    *          DOCUMENT ME!
3812    * @param title
3813    *          DOCUMENT ME!
3814    */
3815   void NewTreePanel(String type, String pwType, String title)
3816   {
3817     TreePanel tp;
3818
3819     if (viewport.getSelectionGroup() != null
3820             && viewport.getSelectionGroup().getSize() > 0)
3821     {
3822       if (viewport.getSelectionGroup().getSize() < 3)
3823       {
3824         JOptionPane
3825                 .showMessageDialog(
3826                         Desktop.desktop,
3827                         MessageManager
3828                                 .getString("label.you_need_more_two_sequences_selected_build_tree"),
3829                         MessageManager
3830                                 .getString("label.not_enough_sequences"),
3831                         JOptionPane.WARNING_MESSAGE);
3832         return;
3833       }
3834
3835       SequenceGroup sg = viewport.getSelectionGroup();
3836
3837       /* Decide if the selection is a column region */
3838       for (SequenceI _s : sg.getSequences())
3839       {
3840         if (_s.getLength() < sg.getEndRes())
3841         {
3842           JOptionPane
3843                   .showMessageDialog(
3844                           Desktop.desktop,
3845                           MessageManager
3846                                   .getString("label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
3847                           MessageManager
3848                                   .getString("label.sequences_selection_not_aligned"),
3849                           JOptionPane.WARNING_MESSAGE);
3850
3851           return;
3852         }
3853       }
3854
3855       title = title + " on region";
3856       tp = new TreePanel(alignPanel, type, pwType);
3857     }
3858     else
3859     {
3860       // are the visible sequences aligned?
3861       if (!viewport.getAlignment().isAligned(false))
3862       {
3863         JOptionPane
3864                 .showMessageDialog(
3865                         Desktop.desktop,
3866                         MessageManager
3867                                 .getString("label.sequences_must_be_aligned_before_creating_tree"),
3868                         MessageManager
3869                                 .getString("label.sequences_not_aligned"),
3870                         JOptionPane.WARNING_MESSAGE);
3871
3872         return;
3873       }
3874
3875       if (viewport.getAlignment().getHeight() < 2)
3876       {
3877         return;
3878       }
3879
3880       tp = new TreePanel(alignPanel, type, pwType);
3881     }
3882
3883     title += " from ";
3884
3885     if (viewport.viewName != null)
3886     {
3887       title += viewport.viewName + " of ";
3888     }
3889
3890     title += this.title;
3891
3892     Desktop.addInternalFrame(tp, title, 600, 500);
3893   }
3894
3895   /**
3896    * DOCUMENT ME!
3897    * 
3898    * @param title
3899    *          DOCUMENT ME!
3900    * @param order
3901    *          DOCUMENT ME!
3902    */
3903   public void addSortByOrderMenuItem(String title,
3904           final AlignmentOrder order)
3905   {
3906     final JMenuItem item = new JMenuItem("by " + title);
3907     sort.add(item);
3908     item.addActionListener(new java.awt.event.ActionListener()
3909     {
3910       @Override
3911       public void actionPerformed(ActionEvent e)
3912       {
3913         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3914
3915         // TODO: JBPNote - have to map order entries to curent SequenceI
3916         // pointers
3917         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3918
3919         addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
3920                 .getAlignment()));
3921
3922         alignPanel.paintAlignment(true);
3923       }
3924     });
3925   }
3926
3927   /**
3928    * Add a new sort by annotation score menu item
3929    * 
3930    * @param sort
3931    *          the menu to add the option to
3932    * @param scoreLabel
3933    *          the label used to retrieve scores for each sequence on the
3934    *          alignment
3935    */
3936   public void addSortByAnnotScoreMenuItem(JMenu sort,
3937           final String scoreLabel)
3938   {
3939     final JMenuItem item = new JMenuItem(scoreLabel);
3940     sort.add(item);
3941     item.addActionListener(new java.awt.event.ActionListener()
3942     {
3943       @Override
3944       public void actionPerformed(ActionEvent e)
3945       {
3946         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3947         AlignmentSorter.sortByAnnotationScore(scoreLabel,
3948                 viewport.getAlignment());// ,viewport.getSelectionGroup());
3949         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
3950                 viewport.getAlignment()));
3951         alignPanel.paintAlignment(true);
3952       }
3953     });
3954   }
3955
3956   /**
3957    * last hash for alignment's annotation array - used to minimise cost of
3958    * rebuild.
3959    */
3960   protected int _annotationScoreVectorHash;
3961
3962   /**
3963    * search the alignment and rebuild the sort by annotation score submenu the
3964    * last alignment annotation vector hash is stored to minimize cost of
3965    * rebuilding in subsequence calls.
3966    * 
3967    */
3968   @Override
3969   public void buildSortByAnnotationScoresMenu()
3970   {
3971     if (viewport.getAlignment().getAlignmentAnnotation() == null)
3972     {
3973       return;
3974     }
3975
3976     if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
3977     {
3978       sortByAnnotScore.removeAll();
3979       // almost certainly a quicker way to do this - but we keep it simple
3980       Hashtable scoreSorts = new Hashtable();
3981       AlignmentAnnotation aann[];
3982       for (SequenceI sqa : viewport.getAlignment().getSequences())
3983       {
3984         aann = sqa.getAnnotation();
3985         for (int i = 0; aann != null && i < aann.length; i++)
3986         {
3987           if (aann[i].hasScore() && aann[i].sequenceRef != null)
3988           {
3989             scoreSorts.put(aann[i].label, aann[i].label);
3990           }
3991         }
3992       }
3993       Enumeration labels = scoreSorts.keys();
3994       while (labels.hasMoreElements())
3995       {
3996         addSortByAnnotScoreMenuItem(sortByAnnotScore,
3997                 (String) labels.nextElement());
3998       }
3999       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
4000       scoreSorts.clear();
4001
4002       _annotationScoreVectorHash = viewport.getAlignment()
4003               .getAlignmentAnnotation().hashCode();
4004     }
4005   }
4006
4007   /**
4008    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
4009    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
4010    * call. Listeners are added to remove the menu item when the treePanel is
4011    * closed, and adjust the tree leaf to sequence mapping when the alignment is
4012    * modified.
4013    * 
4014    * @param treePanel
4015    *          Displayed tree window.
4016    * @param title
4017    *          SortBy menu item title.
4018    */
4019   @Override
4020   public void buildTreeMenu()
4021   {
4022     calculateTree.removeAll();
4023     // build the calculate menu
4024
4025     for (final String type : new String[]
4026     { "NJ", "AV" })
4027     {
4028       String treecalcnm = MessageManager.getString("label.tree_calc_"
4029               + type.toLowerCase());
4030       for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
4031       {
4032         JMenuItem tm = new JMenuItem();
4033         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
4034         if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
4035         {
4036           String smn = MessageManager.getStringOrReturn(
4037                   "label.score_model_", sm.getName());
4038           final String title = MessageManager.formatMessage(
4039                   "label.treecalc_title", treecalcnm, smn);
4040           tm.setText(title);//
4041           tm.addActionListener(new java.awt.event.ActionListener()
4042           {
4043             @Override
4044             public void actionPerformed(ActionEvent e)
4045             {
4046               NewTreePanel(type, (String) pwtype, title);
4047             }
4048           });
4049           calculateTree.add(tm);
4050         }
4051
4052       }
4053     }
4054     sortByTreeMenu.removeAll();
4055
4056     Vector comps = (Vector) PaintRefresher.components.get(viewport
4057             .getSequenceSetId());
4058     Vector treePanels = new Vector();
4059     int i, iSize = comps.size();
4060     for (i = 0; i < iSize; i++)
4061     {
4062       if (comps.elementAt(i) instanceof TreePanel)
4063       {
4064         treePanels.add(comps.elementAt(i));
4065       }
4066     }
4067
4068     iSize = treePanels.size();
4069
4070     if (iSize < 1)
4071     {
4072       sortByTreeMenu.setVisible(false);
4073       return;
4074     }
4075
4076     sortByTreeMenu.setVisible(true);
4077
4078     for (i = 0; i < treePanels.size(); i++)
4079     {
4080       final TreePanel tp = (TreePanel) treePanels.elementAt(i);
4081       final JMenuItem item = new JMenuItem(tp.getTitle());
4082       final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
4083       item.addActionListener(new java.awt.event.ActionListener()
4084       {
4085         @Override
4086         public void actionPerformed(ActionEvent e)
4087         {
4088           tp.sortByTree_actionPerformed(null);
4089           addHistoryItem(tp.sortAlignmentIn(alignPanel));
4090
4091         }
4092       });
4093
4094       sortByTreeMenu.add(item);
4095     }
4096   }
4097
4098   public boolean sortBy(AlignmentOrder alorder, String undoname)
4099   {
4100     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4101     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
4102     if (undoname != null)
4103     {
4104       addHistoryItem(new OrderCommand(undoname, oldOrder,
4105               viewport.getAlignment()));
4106     }
4107     alignPanel.paintAlignment(true);
4108     return true;
4109   }
4110
4111   /**
4112    * Work out whether the whole set of sequences or just the selected set will
4113    * be submitted for multiple alignment.
4114    * 
4115    */
4116   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
4117   {
4118     // Now, check we have enough sequences
4119     AlignmentView msa = null;
4120
4121     if ((viewport.getSelectionGroup() != null)
4122             && (viewport.getSelectionGroup().getSize() > 1))
4123     {
4124       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
4125       // some common interface!
4126       /*
4127        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
4128        * SequenceI[sz = seqs.getSize(false)];
4129        * 
4130        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
4131        * seqs.getSequenceAt(i); }
4132        */
4133       msa = viewport.getAlignmentView(true);
4134     }
4135     else
4136     {
4137       /*
4138        * Vector seqs = viewport.getAlignment().getSequences();
4139        * 
4140        * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
4141        * 
4142        * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
4143        * seqs.elementAt(i); } }
4144        */
4145       msa = viewport.getAlignmentView(false);
4146     }
4147     return msa;
4148   }
4149
4150   /**
4151    * Decides what is submitted to a secondary structure prediction service: the
4152    * first sequence in the alignment, or in the current selection, or, if the
4153    * alignment is 'aligned' (ie padded with gaps), then the currently selected
4154    * region or the whole alignment. (where the first sequence in the set is the
4155    * one that the prediction will be for).
4156    */
4157   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4158   {
4159     AlignmentView seqs = null;
4160
4161     if ((viewport.getSelectionGroup() != null)
4162             && (viewport.getSelectionGroup().getSize() > 0))
4163     {
4164       seqs = viewport.getAlignmentView(true);
4165     }
4166     else
4167     {
4168       seqs = viewport.getAlignmentView(false);
4169     }
4170     // limit sequences - JBPNote in future - could spawn multiple prediction
4171     // jobs
4172     // TODO: viewport.getAlignment().isAligned is a global state - the local
4173     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4174     if (!viewport.getAlignment().isAligned(false))
4175     {
4176       seqs.setSequences(new SeqCigar[]
4177       { seqs.getSequences()[0] });
4178       // TODO: if seqs.getSequences().length>1 then should really have warned
4179       // user!
4180
4181     }
4182     return seqs;
4183   }
4184
4185   /**
4186    * DOCUMENT ME!
4187    * 
4188    * @param e
4189    *          DOCUMENT ME!
4190    */
4191   @Override
4192   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
4193   {
4194     // Pick the tree file
4195     JalviewFileChooser chooser = new JalviewFileChooser(
4196             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4197     chooser.setFileView(new JalviewFileView());
4198     chooser.setDialogTitle(MessageManager
4199             .getString("label.select_newick_like_tree_file"));
4200     chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
4201
4202     int value = chooser.showOpenDialog(null);
4203
4204     if (value == JalviewFileChooser.APPROVE_OPTION)
4205     {
4206       String choice = chooser.getSelectedFile().getPath();
4207       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4208       jalview.io.NewickFile fin = null;
4209       try
4210       {
4211         fin = new jalview.io.NewickFile(choice, "File");
4212         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4213       } catch (Exception ex)
4214       {
4215         JOptionPane
4216                 .showMessageDialog(
4217                         Desktop.desktop,
4218                         ex.getMessage(),
4219                         MessageManager
4220                                 .getString("label.problem_reading_tree_file"),
4221                         JOptionPane.WARNING_MESSAGE);
4222         ex.printStackTrace();
4223       }
4224       if (fin != null && fin.hasWarningMessage())
4225       {
4226         JOptionPane.showMessageDialog(Desktop.desktop, fin
4227                 .getWarningMessage(), MessageManager
4228                 .getString("label.possible_problem_with_tree_file"),
4229                 JOptionPane.WARNING_MESSAGE);
4230       }
4231     }
4232   }
4233
4234   @Override
4235   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4236   {
4237     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4238   }
4239
4240   public TreePanel ShowNewickTree(NewickFile nf, String title)
4241   {
4242     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4243   }
4244
4245   public TreePanel ShowNewickTree(NewickFile nf, String title,
4246           AlignmentView input)
4247   {
4248     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4249   }
4250
4251   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4252           int h, int x, int y)
4253   {
4254     return ShowNewickTree(nf, title, null, w, h, x, y);
4255   }
4256
4257   /**
4258    * Add a treeviewer for the tree extracted from a newick file object to the
4259    * current alignment view
4260    * 
4261    * @param nf
4262    *          the tree
4263    * @param title
4264    *          tree viewer title
4265    * @param input
4266    *          Associated alignment input data (or null)
4267    * @param w
4268    *          width
4269    * @param h
4270    *          height
4271    * @param x
4272    *          position
4273    * @param y
4274    *          position
4275    * @return TreePanel handle
4276    */
4277   public TreePanel ShowNewickTree(NewickFile nf, String title,
4278           AlignmentView input, int w, int h, int x, int y)
4279   {
4280     TreePanel tp = null;
4281
4282     try
4283     {
4284       nf.parse();
4285
4286       if (nf.getTree() != null)
4287       {
4288         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4289
4290         tp.setSize(w, h);
4291
4292         if (x > 0 && y > 0)
4293         {
4294           tp.setLocation(x, y);
4295         }
4296
4297         Desktop.addInternalFrame(tp, title, w, h);
4298       }
4299     } catch (Exception ex)
4300     {
4301       ex.printStackTrace();
4302     }
4303
4304     return tp;
4305   }
4306
4307   private boolean buildingMenu = false;
4308
4309   /**
4310    * Generates menu items and listener event actions for web service clients
4311    * 
4312    */
4313   public void BuildWebServiceMenu()
4314   {
4315     while (buildingMenu)
4316     {
4317       try
4318       {
4319         System.err.println("Waiting for building menu to finish.");
4320         Thread.sleep(10);
4321       } catch (Exception e)
4322       {
4323       }
4324       ;
4325     }
4326     final AlignFrame me = this;
4327     buildingMenu = true;
4328     new Thread(new Runnable()
4329     {
4330       @Override
4331       public void run()
4332       {
4333         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
4334         try
4335         {
4336           System.err.println("Building ws menu again "
4337                   + Thread.currentThread());
4338           // TODO: add support for context dependent disabling of services based
4339           // on
4340           // alignment and current selection
4341           // TODO: add additional serviceHandle parameter to specify abstract
4342           // handler
4343           // class independently of AbstractName
4344           // TODO: add in rediscovery GUI function to restart discoverer
4345           // TODO: group services by location as well as function and/or
4346           // introduce
4347           // object broker mechanism.
4348           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4349           final IProgressIndicator af = me;
4350           final JMenu msawsmenu = new JMenu("Alignment");
4351           final JMenu secstrmenu = new JMenu(
4352                   "Secondary Structure Prediction");
4353           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4354           final JMenu analymenu = new JMenu("Analysis");
4355           final JMenu dismenu = new JMenu("Protein Disorder");
4356           // JAL-940 - only show secondary structure prediction services from
4357           // the legacy server
4358           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4359               // &&
4360           Discoverer.services != null && (Discoverer.services.size() > 0))
4361           {
4362             // TODO: refactor to allow list of AbstractName/Handler bindings to
4363             // be
4364             // stored or retrieved from elsewhere
4365             // No MSAWS used any more:
4366             // Vector msaws = null; // (Vector)
4367             // Discoverer.services.get("MsaWS");
4368             Vector secstrpr = (Vector) Discoverer.services
4369                     .get("SecStrPred");
4370             if (secstrpr != null)
4371             {
4372               // Add any secondary structure prediction services
4373               for (int i = 0, j = secstrpr.size(); i < j; i++)
4374               {
4375                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4376                         .get(i);
4377                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4378                         .getServiceClient(sh);
4379                 int p = secstrmenu.getItemCount();
4380                 impl.attachWSMenuEntry(secstrmenu, me);
4381                 int q = secstrmenu.getItemCount();
4382                 for (int litm = p; litm < q; litm++)
4383                 {
4384                   legacyItems.add(secstrmenu.getItem(litm));
4385                 }
4386               }
4387             }
4388           }
4389
4390           // Add all submenus in the order they should appear on the web
4391           // services menu
4392           wsmenu.add(msawsmenu);
4393           wsmenu.add(secstrmenu);
4394           wsmenu.add(dismenu);
4395           wsmenu.add(analymenu);
4396           // No search services yet
4397           // wsmenu.add(seqsrchmenu);
4398
4399           javax.swing.SwingUtilities.invokeLater(new Runnable()
4400           {
4401             @Override
4402             public void run()
4403             {
4404               try
4405               {
4406                 webService.removeAll();
4407                 // first, add discovered services onto the webservices menu
4408                 if (wsmenu.size() > 0)
4409                 {
4410                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4411                   {
4412                     webService.add(wsmenu.get(i));
4413                   }
4414                 }
4415                 else
4416                 {
4417                   webService.add(me.webServiceNoServices);
4418                 }
4419                 // TODO: move into separate menu builder class.
4420                 boolean new_sspred = false;
4421                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4422                 {
4423                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4424                   if (jws2servs != null)
4425                   {
4426                     if (jws2servs.hasServices())
4427                     {
4428                       jws2servs.attachWSMenuEntry(webService, me);
4429                       for (Jws2Instance sv : jws2servs.getServices())
4430                       {
4431                         if (sv.description.toLowerCase().contains("jpred"))
4432                         {
4433                           for (JMenuItem jmi : legacyItems)
4434                           {
4435                             jmi.setVisible(false);
4436                           }
4437                         }
4438                       }
4439
4440                     }
4441                     if (jws2servs.isRunning())
4442                     {
4443                       JMenuItem tm = new JMenuItem(
4444                               "Still discovering JABA Services");
4445                       tm.setEnabled(false);
4446                       webService.add(tm);
4447                     }
4448                   }
4449                 }
4450                 build_urlServiceMenu(me.webService);
4451                 build_fetchdbmenu(webService);
4452                 for (JMenu item : wsmenu)
4453                 {
4454                   if (item.getItemCount() == 0)
4455                   {
4456                     item.setEnabled(false);
4457                   }
4458                   else
4459                   {
4460                     item.setEnabled(true);
4461                   }
4462                 }
4463               } catch (Exception e)
4464               {
4465                 Cache.log
4466                         .debug("Exception during web service menu building process.",
4467                                 e);
4468               }
4469               ;
4470             }
4471           });
4472         } catch (Exception e)
4473         {
4474         }
4475         ;
4476
4477         buildingMenu = false;
4478       }
4479     }).start();
4480
4481   }
4482
4483   /**
4484    * construct any groupURL type service menu entries.
4485    * 
4486    * @param webService
4487    */
4488   private void build_urlServiceMenu(JMenu webService)
4489   {
4490     // TODO: remove this code when 2.7 is released
4491     // DEBUG - alignmentView
4492     /*
4493      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4494      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4495      * 
4496      * @Override public void actionPerformed(ActionEvent e) {
4497      * jalview.datamodel.AlignmentView
4498      * .testSelectionViews(af.viewport.getAlignment(),
4499      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4500      * 
4501      * }); webService.add(testAlView);
4502      */
4503     // TODO: refactor to RestClient discoverer and merge menu entries for
4504     // rest-style services with other types of analysis/calculation service
4505     // SHmmr test client - still being implemented.
4506     // DEBUG - alignmentView
4507
4508     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4509             .getRestClients())
4510     {
4511       client.attachWSMenuEntry(
4512               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4513               this);
4514     }
4515   }
4516
4517   /*
4518    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4519    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4520    * getProperty("LAST_DIRECTORY"));
4521    * 
4522    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4523    * to Vamsas file"); chooser.setToolTipText("Export");
4524    * 
4525    * int value = chooser.showSaveDialog(this);
4526    * 
4527    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4528    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4529    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4530    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4531    */
4532   /**
4533    * prototype of an automatically enabled/disabled analysis function
4534    * 
4535    */
4536   protected void setShowProductsEnabled()
4537   {
4538     SequenceI[] selection = viewport.getSequenceSelection();
4539     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4540             viewport.getAlignment().getDataset()))
4541     {
4542       showProducts.setEnabled(true);
4543
4544     }
4545     else
4546     {
4547       showProducts.setEnabled(false);
4548     }
4549   }
4550
4551   /**
4552    * search selection for sequence xRef products and build the show products
4553    * menu.
4554    * 
4555    * @param selection
4556    * @param dataset
4557    * @return true if showProducts menu should be enabled.
4558    */
4559   public boolean canShowProducts(SequenceI[] selection,
4560           boolean isRegionSelection, Alignment dataset)
4561   {
4562     boolean showp = false;
4563     try
4564     {
4565       showProducts.removeAll();
4566       final boolean dna = viewport.getAlignment().isNucleotide();
4567       final Alignment ds = dataset;
4568       String[] ptypes = (selection == null || selection.length == 0) ? null
4569               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4570       // Object[] prods =
4571       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4572       // selection, dataset, true);
4573       final SequenceI[] sel = selection;
4574       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4575       {
4576         showp = true;
4577         final boolean isRegSel = isRegionSelection;
4578         final AlignFrame af = this;
4579         final String source = ptypes[t];
4580         JMenuItem xtype = new JMenuItem(ptypes[t]);
4581         xtype.addActionListener(new ActionListener()
4582         {
4583
4584           @Override
4585           public void actionPerformed(ActionEvent e)
4586           {
4587             // TODO: new thread for this call with vis-delay
4588             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4589                     isRegSel, dna, source);
4590           }
4591
4592         });
4593         showProducts.add(xtype);
4594       }
4595       showProducts.setVisible(showp);
4596       showProducts.setEnabled(showp);
4597     } catch (Exception e)
4598     {
4599       jalview.bin.Cache.log
4600               .warn("canTranslate threw an exception - please report to help@jalview.org",
4601                       e);
4602       return false;
4603     }
4604     return showp;
4605   }
4606
4607   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4608           boolean isRegSel, boolean dna, String source)
4609   {
4610     final boolean fisRegSel = isRegSel;
4611     final boolean fdna = dna;
4612     final String fsrc = source;
4613     final AlignFrame ths = this;
4614     final SequenceI[] fsel = sel;
4615     Runnable foo = new Runnable()
4616     {
4617
4618       @Override
4619       public void run()
4620       {
4621         final long sttime = System.currentTimeMillis();
4622         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
4623         try
4624         {
4625           Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
4626           // our local
4627           // dataset
4628           // reference
4629           Alignment prods = CrossRef
4630                   .findXrefSequences(fsel, fdna, fsrc, ds);
4631           if (prods != null)
4632           {
4633             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4634             for (int s = 0; s < sprods.length; s++)
4635             {
4636               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4637               if (ds.getSequences() == null
4638                       || !ds.getSequences().contains(
4639                               sprods[s].getDatasetSequence()))
4640                 ds.addSequence(sprods[s].getDatasetSequence());
4641               sprods[s].updatePDBIds();
4642             }
4643             Alignment al = new Alignment(sprods);
4644             AlignedCodonFrame[] cf = prods.getCodonFrames();
4645             al.setDataset(ds);
4646             for (int s = 0; cf != null && s < cf.length; s++)
4647             {
4648               al.addCodonFrame(cf[s]);
4649               cf[s] = null;
4650             }
4651             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4652                     DEFAULT_HEIGHT);
4653             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4654                     + " for " + ((fisRegSel) ? "selected region of " : "")
4655                     + getTitle();
4656             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4657                     DEFAULT_HEIGHT);
4658           }
4659           else
4660           {
4661             System.err.println("No Sequences generated for xRef type "
4662                     + fsrc);
4663           }
4664         } catch (Exception e)
4665         {
4666           jalview.bin.Cache.log.error(
4667                   "Exception when finding crossreferences", e);
4668         } catch (OutOfMemoryError e)
4669         {
4670           new OOMWarning("whilst fetching crossreferences", e);
4671         } catch (Error e)
4672         {
4673           jalview.bin.Cache.log.error("Error when finding crossreferences",
4674                   e);
4675         }
4676         ths.setProgressBar("Finished searching for sequences from " + fsrc,
4677                 sttime);
4678       }
4679
4680     };
4681     Thread frunner = new Thread(foo);
4682     frunner.start();
4683   }
4684
4685   public boolean canShowTranslationProducts(SequenceI[] selection,
4686           AlignmentI alignment)
4687   {
4688     // old way
4689     try
4690     {
4691       return (jalview.analysis.Dna.canTranslate(selection,
4692               viewport.getViewAsVisibleContigs(true)));
4693     } catch (Exception e)
4694     {
4695       jalview.bin.Cache.log
4696               .warn("canTranslate threw an exception - please report to help@jalview.org",
4697                       e);
4698       return false;
4699     }
4700   }
4701
4702   @Override
4703   public void showProducts_actionPerformed(ActionEvent e)
4704   {
4705     // /////////////////////////////
4706     // Collect Data to be translated/transferred
4707
4708     SequenceI[] selection = viewport.getSequenceSelection();
4709     AlignmentI al = null;
4710     try
4711     {
4712       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4713               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4714               viewport.getAlignment().getDataset());
4715     } catch (Exception ex)
4716     {
4717       al = null;
4718       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4719     }
4720     if (al == null)
4721     {
4722       JOptionPane
4723               .showMessageDialog(
4724                       Desktop.desktop,
4725                       MessageManager
4726                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
4727                       MessageManager.getString("label.translation_failed"),
4728                       JOptionPane.WARNING_MESSAGE);
4729     }
4730     else
4731     {
4732       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4733       Desktop.addInternalFrame(af, MessageManager.formatMessage(
4734               "label.translation_of_params", new String[]
4735               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
4736     }
4737   }
4738
4739   @Override
4740   public void showTranslation_actionPerformed(ActionEvent e)
4741   {
4742     // /////////////////////////////
4743     // Collect Data to be translated/transferred
4744
4745     SequenceI[] selection = viewport.getSequenceSelection();
4746     String[] seqstring = viewport.getViewAsString(true);
4747     AlignmentI al = null;
4748     try
4749     {
4750       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4751               viewport.getViewAsVisibleContigs(true), viewport
4752                       .getGapCharacter(), viewport.getAlignment()
4753                       .getAlignmentAnnotation(), viewport.getAlignment()
4754                       .getWidth(), viewport.getAlignment().getDataset());
4755     } catch (Exception ex)
4756     {
4757       al = null;
4758       jalview.bin.Cache.log.error(
4759               "Exception during translation. Please report this !", ex);
4760       JOptionPane
4761               .showMessageDialog(
4762                       Desktop.desktop,
4763                       MessageManager
4764                               .getString("label.error_when_translating_sequences_submit_bug_report"),
4765                       MessageManager
4766                               .getString("label.implementation_error")
4767                               + MessageManager
4768                                       .getString("translation_failed"),
4769                       JOptionPane.ERROR_MESSAGE);
4770       return;
4771     }
4772     if (al == null)
4773     {
4774       JOptionPane
4775               .showMessageDialog(
4776                       Desktop.desktop,
4777                       MessageManager
4778                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
4779                       MessageManager.getString("label.translation_failed"),
4780                       JOptionPane.WARNING_MESSAGE);
4781     }
4782     else
4783     {
4784       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4785       Desktop.addInternalFrame(af, MessageManager.formatMessage(
4786               "label.translation_of_params", new String[]
4787               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
4788     }
4789   }
4790
4791   /**
4792    * Try to load a features file onto the alignment.
4793    * 
4794    * @param file
4795    *          contents or path to retrieve file
4796    * @param type
4797    *          access mode of file (see jalview.io.AlignFile)
4798    * @return true if features file was parsed corectly.
4799    */
4800   public boolean parseFeaturesFile(String file, String type)
4801   {
4802     boolean featuresFile = false;
4803     try
4804     {
4805       featuresFile = new FeaturesFile(file, type).parse(viewport
4806               .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
4807               .getFeatureRenderer().featureColours, false,
4808               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4809     } catch (Exception ex)
4810     {
4811       ex.printStackTrace();
4812     }
4813
4814     if (featuresFile)
4815     {
4816       viewport.showSequenceFeatures = true;
4817       showSeqFeatures.setSelected(true);
4818       if (alignPanel.seqPanel.seqCanvas.fr != null)
4819       {
4820         // update the min/max ranges where necessary
4821         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4822       }
4823       if (featureSettings != null)
4824       {
4825         featureSettings.setTableData();
4826       }
4827       alignPanel.paintAlignment(true);
4828     }
4829
4830     return featuresFile;
4831   }
4832
4833   @Override
4834   public void dragEnter(DropTargetDragEvent evt)
4835   {
4836   }
4837
4838   @Override
4839   public void dragExit(DropTargetEvent evt)
4840   {
4841   }
4842
4843   @Override
4844   public void dragOver(DropTargetDragEvent evt)
4845   {
4846   }
4847
4848   @Override
4849   public void dropActionChanged(DropTargetDragEvent evt)
4850   {
4851   }
4852
4853   @Override
4854   public void drop(DropTargetDropEvent evt)
4855   {
4856     Transferable t = evt.getTransferable();
4857     java.util.List files = null;
4858
4859     try
4860     {
4861       DataFlavor uriListFlavor = new DataFlavor(
4862               "text/uri-list;class=java.lang.String");
4863       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4864       {
4865         // Works on Windows and MacOSX
4866         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4867         files = (java.util.List) t
4868                 .getTransferData(DataFlavor.javaFileListFlavor);
4869       }
4870       else if (t.isDataFlavorSupported(uriListFlavor))
4871       {
4872         // This is used by Unix drag system
4873         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4874         String data = (String) t.getTransferData(uriListFlavor);
4875         files = new java.util.ArrayList(1);
4876         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4877                 data, "\r\n"); st.hasMoreTokens();)
4878         {
4879           String s = st.nextToken();
4880           if (s.startsWith("#"))
4881           {
4882             // the line is a comment (as per the RFC 2483)
4883             continue;
4884           }
4885
4886           java.net.URI uri = new java.net.URI(s);
4887           // check to see if we can handle this kind of URI
4888           if (uri.getScheme().toLowerCase().startsWith("http"))
4889           {
4890             files.add(uri.toString());
4891           }
4892           else
4893           {
4894             // otherwise preserve old behaviour: catch all for file objects
4895             java.io.File file = new java.io.File(uri);
4896             files.add(file.toString());
4897           }
4898         }
4899       }
4900     } catch (Exception e)
4901     {
4902       e.printStackTrace();
4903     }
4904     if (files != null)
4905     {
4906       try
4907       {
4908         // check to see if any of these files have names matching sequences in
4909         // the alignment
4910         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4911                 .getAlignment().getSequencesArray());
4912         /**
4913          * Object[] { String,SequenceI}
4914          */
4915         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4916         ArrayList<String> filesnotmatched = new ArrayList<String>();
4917         for (int i = 0; i < files.size(); i++)
4918         {
4919           String file = files.get(i).toString();
4920           String pdbfn = "";
4921           String protocol = FormatAdapter.checkProtocol(file);
4922           if (protocol == jalview.io.FormatAdapter.FILE)
4923           {
4924             File fl = new File(file);
4925             pdbfn = fl.getName();
4926           }
4927           else if (protocol == jalview.io.FormatAdapter.URL)
4928           {
4929             URL url = new URL(file);
4930             pdbfn = url.getFile();
4931           }
4932           if (pdbfn.length() > 0)
4933           {
4934             // attempt to find a match in the alignment
4935             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
4936             int l = 0, c = pdbfn.indexOf(".");
4937             while (mtch == null && c != -1)
4938             {
4939               do
4940               {
4941                 l = c;
4942               } while ((c = pdbfn.indexOf(".", l)) > l);
4943               if (l > -1)
4944               {
4945                 pdbfn = pdbfn.substring(0, l);
4946               }
4947               mtch = idm.findAllIdMatches(pdbfn);
4948             }
4949             if (mtch != null)
4950             {
4951               String type = null;
4952               try
4953               {
4954                 type = new IdentifyFile().Identify(file, protocol);
4955               } catch (Exception ex)
4956               {
4957                 type = null;
4958               }
4959               if (type != null)
4960               {
4961                 if (type.equalsIgnoreCase("PDB"))
4962                 {
4963                   filesmatched.add(new Object[]
4964                   { file, protocol, mtch });
4965                   continue;
4966                 }
4967               }
4968             }
4969             // File wasn't named like one of the sequences or wasn't a PDB file.
4970             filesnotmatched.add(file);
4971           }
4972         }
4973         int assocfiles = 0;
4974         if (filesmatched.size() > 0)
4975         {
4976           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
4977                   || JOptionPane
4978                           .showConfirmDialog(
4979                                   this,
4980                                   MessageManager
4981                                           .formatMessage(
4982                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
4983                                                   new String[]
4984                                                   { Integer.valueOf(
4985                                                           filesmatched
4986                                                                   .size())
4987                                                           .toString() }),
4988                                   MessageManager
4989                                           .getString("label.automatically_associate_pdb_files_by_name"),
4990                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
4991
4992           {
4993             for (Object[] fm : filesmatched)
4994             {
4995               // try and associate
4996               // TODO: may want to set a standard ID naming formalism for
4997               // associating PDB files which have no IDs.
4998               for (SequenceI toassoc : (SequenceI[]) fm[2])
4999               {
5000                 PDBEntry pe = new AssociatePdbFileWithSeq()
5001                         .associatePdbWithSeq((String) fm[0],
5002                                 (String) fm[1], toassoc, false);
5003                 if (pe != null)
5004                 {
5005                   System.err.println("Associated file : "
5006                           + ((String) fm[0]) + " with "
5007                           + toassoc.getDisplayId(true));
5008                   assocfiles++;
5009                 }
5010               }
5011               alignPanel.paintAlignment(true);
5012             }
5013           }
5014         }
5015         if (filesnotmatched.size() > 0)
5016         {
5017           if (assocfiles > 0
5018                   && (Cache.getDefault(
5019                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
5020                           .showConfirmDialog(
5021                                   this,
5022                                   MessageManager
5023                                           .formatMessage(
5024                                                   "label.ignore_unmatched_dropped_files_info",
5025                                                   new String[]
5026                                                   { Integer.valueOf(
5027                                                           filesnotmatched
5028                                                                   .size())
5029                                                           .toString() }),
5030                                   MessageManager
5031                                           .getString("label.ignore_unmatched_dropped_files"),
5032                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
5033           {
5034             return;
5035           }
5036           for (String fn : filesnotmatched)
5037           {
5038             loadJalviewDataFile(fn, null, null, null);
5039           }
5040
5041         }
5042       } catch (Exception ex)
5043       {
5044         ex.printStackTrace();
5045       }
5046     }
5047   }
5048
5049   /**
5050    * Attempt to load a "dropped" file or URL string: First by testing whether
5051    * it's and Annotation file, then a JNet file, and finally a features file. If
5052    * all are false then the user may have dropped an alignment file onto this
5053    * AlignFrame.
5054    * 
5055    * @param file
5056    *          either a filename or a URL string.
5057    */
5058   public void loadJalviewDataFile(String file, String protocol,
5059           String format, SequenceI assocSeq)
5060   {
5061     try
5062     {
5063       if (protocol == null)
5064       {
5065         protocol = jalview.io.FormatAdapter.checkProtocol(file);
5066       }
5067       // if the file isn't identified, or not positively identified as some
5068       // other filetype (PFAM is default unidentified alignment file type) then
5069       // try to parse as annotation.
5070       boolean isAnnotation = (format == null || format
5071               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
5072               .readAnnotationFile(viewport.getAlignment(), file, protocol)
5073               : false;
5074
5075       if (!isAnnotation)
5076       {
5077         // first see if its a T-COFFEE score file
5078         TCoffeeScoreFile tcf = null;
5079         try
5080         {
5081           tcf = new TCoffeeScoreFile(file, protocol);
5082           if (tcf.isValid())
5083           {
5084             if (tcf.annotateAlignment(viewport.getAlignment(), true))
5085             {
5086               tcoffeeColour.setEnabled(true);
5087               tcoffeeColour.setSelected(true);
5088               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
5089               isAnnotation = true;
5090               statusBar
5091                       .setText(MessageManager
5092                               .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
5093             }
5094             else
5095             {
5096               // some problem - if no warning its probable that the ID matching
5097               // process didn't work
5098               JOptionPane
5099                       .showMessageDialog(
5100                               Desktop.desktop,
5101                               tcf.getWarningMessage() == null ? MessageManager
5102                                       .getString("label.check_file_matches_sequence_ids_alignment")
5103                                       : tcf.getWarningMessage(),
5104                               MessageManager
5105                                       .getString("label.problem_reading_tcoffee_score_file"),
5106                               JOptionPane.WARNING_MESSAGE);
5107             }
5108           }
5109           else
5110           {
5111             tcf = null;
5112           }
5113         } catch (Exception x)
5114         {
5115           Cache.log
5116                   .debug("Exception when processing data source as T-COFFEE score file",
5117                           x);
5118           tcf = null;
5119         }
5120         if (tcf == null)
5121         {
5122           // try to see if its a JNet 'concise' style annotation file *before*
5123           // we
5124           // try to parse it as a features file
5125           if (format == null)
5126           {
5127             format = new IdentifyFile().Identify(file, protocol);
5128           }
5129           if (format.equalsIgnoreCase("JnetFile"))
5130           {
5131             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
5132                     file, protocol);
5133             new JnetAnnotationMaker().add_annotation(predictions,
5134                     viewport.getAlignment(), 0, false);
5135             isAnnotation = true;
5136           }
5137           else
5138           {
5139             /*
5140              * if (format.equalsIgnoreCase("PDB")) {
5141              * 
5142              * String pdbfn = ""; // try to match up filename with sequence id
5143              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
5144              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
5145              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
5146              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
5147              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5148              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
5149              * // attempt to find a match in the alignment SequenceI mtch =
5150              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
5151              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
5152              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
5153              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
5154              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
5155              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
5156              * { System.err.println("Associated file : " + file + " with " +
5157              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
5158              * TODO: maybe need to load as normal otherwise return; } }
5159              */
5160             // try to parse it as a features file
5161             boolean isGroupsFile = parseFeaturesFile(file, protocol);
5162             // if it wasn't a features file then we just treat it as a general
5163             // alignment file to load into the current view.
5164             if (!isGroupsFile)
5165             {
5166               new FileLoader().LoadFile(viewport, file, protocol, format);
5167             }
5168             else
5169             {
5170               alignPanel.paintAlignment(true);
5171             }
5172           }
5173         }
5174       }
5175       if (isAnnotation)
5176       {
5177
5178         alignPanel.adjustAnnotationHeight();
5179         viewport.updateSequenceIdColours();
5180         buildSortByAnnotationScoresMenu();
5181         alignPanel.paintAlignment(true);
5182       }
5183     } catch (Exception ex)
5184     {
5185       ex.printStackTrace();
5186     } catch (OutOfMemoryError oom)
5187     {
5188       try
5189       {
5190         System.gc();
5191       } catch (Exception x)
5192       {
5193       }
5194       ;
5195       new OOMWarning(
5196               "loading data "
5197                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5198                               : "using " + protocol + " from " + file)
5199                               : ".")
5200                       + (format != null ? "(parsing as '" + format
5201                               + "' file)" : ""), oom, Desktop.desktop);
5202     }
5203   }
5204
5205   @Override
5206   public void tabSelectionChanged(int index)
5207   {
5208     if (index > -1)
5209     {
5210       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
5211       viewport = alignPanel.av;
5212       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
5213       setMenusFromViewport(viewport);
5214     }
5215   }
5216
5217   @Override
5218   public void tabbedPane_mousePressed(MouseEvent e)
5219   {
5220     if (SwingUtilities.isRightMouseButton(e))
5221     {
5222       String reply = JOptionPane.showInternalInputDialog(this,
5223               MessageManager.getString("label.enter_view_name"),
5224               MessageManager.getString("label.enter_view_name"),
5225               JOptionPane.QUESTION_MESSAGE);
5226
5227       if (reply != null)
5228       {
5229         viewport.viewName = reply;
5230         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5231       }
5232     }
5233   }
5234
5235   public AlignViewport getCurrentView()
5236   {
5237     return viewport;
5238   }
5239
5240   /**
5241    * Open the dialog for regex description parsing.
5242    */
5243   @Override
5244   protected void extractScores_actionPerformed(ActionEvent e)
5245   {
5246     ParseProperties pp = new jalview.analysis.ParseProperties(
5247             viewport.getAlignment());
5248     // TODO: verify regex and introduce GUI dialog for version 2.5
5249     // if (pp.getScoresFromDescription("col", "score column ",
5250     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5251     // true)>0)
5252     if (pp.getScoresFromDescription("description column",
5253             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5254     {
5255       buildSortByAnnotationScoresMenu();
5256     }
5257   }
5258
5259   /*
5260    * (non-Javadoc)
5261    * 
5262    * @see
5263    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5264    * )
5265    */
5266   @Override
5267   protected void showDbRefs_actionPerformed(ActionEvent e)
5268   {
5269     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
5270   }
5271
5272   /*
5273    * (non-Javadoc)
5274    * 
5275    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5276    * ActionEvent)
5277    */
5278   @Override
5279   protected void showNpFeats_actionPerformed(ActionEvent e)
5280   {
5281     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
5282   }
5283
5284   /**
5285    * find the viewport amongst the tabs in this alignment frame and close that
5286    * tab
5287    * 
5288    * @param av
5289    */
5290   public boolean closeView(AlignViewport av)
5291   {
5292     if (viewport == av)
5293     {
5294       this.closeMenuItem_actionPerformed(false);
5295       return true;
5296     }
5297     Component[] comp = tabbedPane.getComponents();
5298     for (int i = 0; comp != null && i < comp.length; i++)
5299     {
5300       if (comp[i] instanceof AlignmentPanel)
5301       {
5302         if (((AlignmentPanel) comp[i]).av == av)
5303         {
5304           // close the view.
5305           closeView((AlignmentPanel) comp[i]);
5306           return true;
5307         }
5308       }
5309     }
5310     return false;
5311   }
5312
5313   protected void build_fetchdbmenu(JMenu webService)
5314   {
5315     // Temporary hack - DBRef Fetcher always top level ws entry.
5316     // TODO We probably want to store a sequence database checklist in
5317     // preferences and have checkboxes.. rather than individual sources selected
5318     // here
5319     final JMenu rfetch = new JMenu(
5320             MessageManager.getString("action.fetch_db_references"));
5321     rfetch.setToolTipText(MessageManager
5322             .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
5323     webService.add(rfetch);
5324
5325     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
5326             MessageManager.getString("option.trim_retrieved_seqs"));
5327     trimrs.setToolTipText(MessageManager
5328             .getString("label.trim_retrieved_sequences"));
5329     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
5330     trimrs.addActionListener(new ActionListener()
5331     {
5332       @Override
5333       public void actionPerformed(ActionEvent e)
5334       {
5335         trimrs.setSelected(trimrs.isSelected());
5336         Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
5337                 Boolean.valueOf(trimrs.isSelected()).toString());
5338       };
5339     });
5340     rfetch.add(trimrs);
5341     JMenuItem fetchr = new JMenuItem(
5342             MessageManager.getString("label.standard_databases"));
5343     fetchr.setToolTipText(MessageManager
5344             .getString("label.fetch_embl_uniprot"));
5345     fetchr.addActionListener(new ActionListener()
5346     {
5347
5348       @Override
5349       public void actionPerformed(ActionEvent e)
5350       {
5351         new Thread(new Runnable()
5352         {
5353
5354           @Override
5355           public void run()
5356           {
5357             new jalview.ws.DBRefFetcher(alignPanel.av
5358                     .getSequenceSelection(), alignPanel.alignFrame)
5359                     .fetchDBRefs(false);
5360           }
5361         }).start();
5362
5363       }
5364
5365     });
5366     rfetch.add(fetchr);
5367     final AlignFrame me = this;
5368     new Thread(new Runnable()
5369     {
5370       @Override
5371       public void run()
5372       {
5373         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5374                 .getSequenceFetcherSingleton(me);
5375         javax.swing.SwingUtilities.invokeLater(new Runnable()
5376         {
5377           @Override
5378           public void run()
5379           {
5380             String[] dbclasses = sf.getOrderedSupportedSources();
5381             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5382             // jalview.util.QuickSort.sort(otherdb, otherdb);
5383             List<DbSourceProxy> otherdb;
5384             JMenu dfetch = new JMenu();
5385             JMenu ifetch = new JMenu();
5386             JMenuItem fetchr = null;
5387             int comp = 0, icomp = 0, mcomp = 15;
5388             String mname = null;
5389             int dbi = 0;
5390             for (String dbclass : dbclasses)
5391             {
5392               otherdb = sf.getSourceProxy(dbclass);
5393               // add a single entry for this class, or submenu allowing 'fetch
5394               // all' or pick one
5395               if (otherdb == null || otherdb.size() < 1)
5396               {
5397                 continue;
5398               }
5399               // List<DbSourceProxy> dbs=otherdb;
5400               // otherdb=new ArrayList<DbSourceProxy>();
5401               // for (DbSourceProxy db:dbs)
5402               // {
5403               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5404               // }
5405               if (mname == null)
5406               {
5407                 mname = "From " + dbclass;
5408               }
5409               if (otherdb.size() == 1)
5410               {
5411                 final DbSourceProxy[] dassource = otherdb
5412                         .toArray(new DbSourceProxy[0]);
5413                 DbSourceProxy src = otherdb.get(0);
5414                 fetchr = new JMenuItem(src.getDbSource());
5415                 fetchr.addActionListener(new ActionListener()
5416                 {
5417
5418                   @Override
5419                   public void actionPerformed(ActionEvent e)
5420                   {
5421                     new Thread(new Runnable()
5422                     {
5423
5424                       @Override
5425                       public void run()
5426                       {
5427                         new jalview.ws.DBRefFetcher(alignPanel.av
5428                                 .getSequenceSelection(),
5429                                 alignPanel.alignFrame, dassource)
5430                                 .fetchDBRefs(false);
5431                       }
5432                     }).start();
5433                   }
5434
5435                 });
5436                 fetchr.setToolTipText("<html>"
5437                         + JvSwingUtils.wrapTooltip("Retrieve from "
5438                                 + src.getDbName()) + "<html>");
5439                 dfetch.add(fetchr);
5440                 comp++;
5441               }
5442               else
5443               {
5444                 final DbSourceProxy[] dassource = otherdb
5445                         .toArray(new DbSourceProxy[0]);
5446                 // fetch all entry
5447                 DbSourceProxy src = otherdb.get(0);
5448                 fetchr = new JMenuItem(MessageManager.formatMessage(
5449                         "label.fetch_all_param", new String[]
5450                         { src.getDbSource() }));
5451                 fetchr.addActionListener(new ActionListener()
5452                 {
5453                   @Override
5454                   public void actionPerformed(ActionEvent e)
5455                   {
5456                     new Thread(new Runnable()
5457                     {
5458
5459                       @Override
5460                       public void run()
5461                       {
5462                         new jalview.ws.DBRefFetcher(alignPanel.av
5463                                 .getSequenceSelection(),
5464                                 alignPanel.alignFrame, dassource)
5465                                 .fetchDBRefs(false);
5466                       }
5467                     }).start();
5468                   }
5469                 });
5470
5471                 fetchr.setToolTipText("<html>"
5472                         + JvSwingUtils.wrapTooltip("Retrieve from all "
5473                                 + otherdb.size() + " sources in "
5474                                 + src.getDbSource() + "<br>First is :"
5475                                 + src.getDbName()) + "<html>");
5476                 dfetch.add(fetchr);
5477                 comp++;
5478                 // and then build the rest of the individual menus
5479                 ifetch = new JMenu("Sources from " + src.getDbSource());
5480                 icomp = 0;
5481                 String imname = null;
5482                 int i = 0;
5483                 for (DbSourceProxy sproxy : otherdb)
5484                 {
5485                   String dbname = sproxy.getDbName();
5486                   String sname = dbname.length() > 5 ? dbname.substring(0,
5487                           5) + "..." : dbname;
5488                   String msname = dbname.length() > 10 ? dbname.substring(
5489                           0, 10) + "..." : dbname;
5490                   if (imname == null)
5491                   {
5492                     imname = "from '" + sname + "'";
5493                   }
5494                   fetchr = new JMenuItem(msname);
5495                   final DbSourceProxy[] dassrc =
5496                   { sproxy };
5497                   fetchr.addActionListener(new ActionListener()
5498                   {
5499
5500                     @Override
5501                     public void actionPerformed(ActionEvent e)
5502                     {
5503                       new Thread(new Runnable()
5504                       {
5505
5506                         @Override
5507                         public void run()
5508                         {
5509                           new jalview.ws.DBRefFetcher(alignPanel.av
5510                                   .getSequenceSelection(),
5511                                   alignPanel.alignFrame, dassrc)
5512                                   .fetchDBRefs(false);
5513                         }
5514                       }).start();
5515                     }
5516
5517                   });
5518                   fetchr.setToolTipText("<html>"
5519                           + JvSwingUtils.wrapTooltip("Retrieve from "
5520                                   + dbname) + "</html>");
5521                   ifetch.add(fetchr);
5522                   ++i;
5523                   if (++icomp >= mcomp || i == (otherdb.size()))
5524                   {
5525                     ifetch.setText(MessageManager.formatMessage(
5526                             "label.source_to_target", imname, sname));
5527                     dfetch.add(ifetch);
5528                     ifetch = new JMenu();
5529                     imname = null;
5530                     icomp = 0;
5531                     comp++;
5532                   }
5533                 }
5534               }
5535               ++dbi;
5536               if (comp >= mcomp || dbi >= (dbclasses.length))
5537               {
5538                 dfetch.setText(MessageManager.formatMessage(
5539                         "label.source_to_target", mname, dbclass));
5540                 rfetch.add(dfetch);
5541                 dfetch = new JMenu();
5542                 mname = null;
5543                 comp = 0;
5544               }
5545             }
5546           }
5547         });
5548       }
5549     }).start();
5550
5551   }
5552
5553   /**
5554    * Left justify the whole alignment.
5555    */
5556   @Override
5557   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5558   {
5559     AlignmentI al = viewport.getAlignment();
5560     al.justify(false);
5561     viewport.firePropertyChange("alignment", null, al);
5562   }
5563
5564   /**
5565    * Right justify the whole alignment.
5566    */
5567   @Override
5568   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5569   {
5570     AlignmentI al = viewport.getAlignment();
5571     al.justify(true);
5572     viewport.firePropertyChange("alignment", null, al);
5573   }
5574
5575   public void setShowSeqFeatures(boolean b)
5576   {
5577     showSeqFeatures.setSelected(true);
5578     viewport.setShowSequenceFeatures(true);
5579   }
5580
5581   /*
5582    * (non-Javadoc)
5583    * 
5584    * @see
5585    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5586    * awt.event.ActionEvent)
5587    */
5588   @Override
5589   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5590   {
5591     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5592     alignPanel.paintAlignment(true);
5593   }
5594
5595   /*
5596    * (non-Javadoc)
5597    * 
5598    * @see
5599    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5600    * .ActionEvent)
5601    */
5602   @Override
5603   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5604   {
5605     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5606     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5607
5608   }
5609
5610   /*
5611    * (non-Javadoc)
5612    * 
5613    * @see
5614    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5615    * .event.ActionEvent)
5616    */
5617   @Override
5618   protected void showGroupConservation_actionPerformed(ActionEvent e)
5619   {
5620     viewport.setShowGroupConservation(showGroupConservation.getState());
5621     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5622   }
5623
5624   /*
5625    * (non-Javadoc)
5626    * 
5627    * @see
5628    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5629    * .event.ActionEvent)
5630    */
5631   @Override
5632   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5633   {
5634     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5635     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5636   }
5637
5638   /*
5639    * (non-Javadoc)
5640    * 
5641    * @see
5642    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5643    * .event.ActionEvent)
5644    */
5645   @Override
5646   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5647   {
5648     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5649     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5650   }
5651
5652   @Override
5653   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5654   {
5655     showSequenceLogo.setState(true);
5656     viewport.setShowSequenceLogo(true);
5657     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5658     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5659   }
5660
5661   @Override
5662   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5663   {
5664     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5665   }
5666
5667   /*
5668    * (non-Javadoc)
5669    * 
5670    * @see
5671    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5672    * .event.ActionEvent)
5673    */
5674   @Override
5675   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5676   {
5677     if (avc.makeGroupsFromSelection())
5678     {
5679       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5680       alignPanel.updateAnnotation();
5681       alignPanel.paintAlignment(true);
5682     }
5683   }
5684
5685   @Override
5686   protected void createGroup_actionPerformed(ActionEvent e)
5687   {
5688     if (avc.createGroup())
5689     {
5690       alignPanel.alignmentChanged();
5691     }
5692   }
5693
5694   @Override
5695   protected void unGroup_actionPerformed(ActionEvent e)
5696   {
5697     if (avc.unGroup())
5698     {
5699       alignPanel.alignmentChanged();
5700     }
5701   }
5702
5703   /**
5704    * make the given alignmentPanel the currently selected tab
5705    * 
5706    * @param alignmentPanel
5707    */
5708   public void setDisplayedView(AlignmentPanel alignmentPanel)
5709   {
5710     if (!viewport.getSequenceSetId().equals(
5711             alignmentPanel.av.getSequenceSetId()))
5712     {
5713       throw new Error(
5714               "Implementation error: cannot show a view from another alignment in an AlignFrame.");
5715     }
5716     if (tabbedPane != null
5717             & alignPanels.indexOf(alignmentPanel) != tabbedPane
5718                     .getSelectedIndex())
5719     {
5720       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5721     }
5722   }
5723
5724   /**
5725    * Get a list of unique annotation types for the alignment, optionally
5726    * restricted to sequence-specific annotations.
5727    */
5728   protected List<String> getAnnotationTypes(boolean sequenceSpecificOnly)
5729   {
5730     // TODO move this to an AlignmentUtils class or similar
5731     List<String> types = new ArrayList<String>();
5732     for (AlignmentAnnotation aa : alignPanel.getAlignment()
5733             .getAlignmentAnnotation())
5734     {
5735       if (sequenceSpecificOnly && aa.sequenceRef == null)
5736       {
5737         continue;
5738       }
5739       String type = aa.label;
5740       if (!types.contains(type))
5741       {
5742         types.add(type);
5743       }
5744     }
5745     return types;
5746   }
5747
5748   /**
5749    * Action on selection of menu option to Show or Hide all annotations.
5750    * 
5751    * @param visibile
5752    */
5753   @Override
5754   protected void setAllAnnotationsVisibility(boolean visible)
5755   {
5756     for (AlignmentAnnotation aa : alignPanel.getAlignment()
5757             .getAlignmentAnnotation())
5758     {
5759       aa.visible = visible;
5760     }
5761     this.alignPanel.paintAlignment(true);
5762   }
5763 }
5764
5765 class PrintThread extends Thread
5766 {
5767   AlignmentPanel ap;
5768
5769   public PrintThread(AlignmentPanel ap)
5770   {
5771     this.ap = ap;
5772   }
5773
5774   static PageFormat pf;
5775
5776   @Override
5777   public void run()
5778   {
5779     PrinterJob printJob = PrinterJob.getPrinterJob();
5780
5781     if (pf != null)
5782     {
5783       printJob.setPrintable(ap, pf);
5784     }
5785     else
5786     {
5787       printJob.setPrintable(ap);
5788     }
5789
5790     if (printJob.printDialog())
5791     {
5792       try
5793       {
5794         printJob.print();
5795       } catch (Exception PrintException)
5796       {
5797         PrintException.printStackTrace();
5798       }
5799     }
5800   }
5801 }