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