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