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