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