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