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