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