JAL-1691 applet SplitFrame scrolling; pull up of 5 fields to
[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     final boolean state = this.followHighlightMenuItem.getState();
2853     viewport.setFollowHighlight(state);
2854     if (state)
2855     {
2856       alignPanel.scrollToPosition(
2857               alignPanel.getSeqPanel().seqCanvas.searchResults, false);
2858     }
2859   }
2860
2861   /**
2862    * DOCUMENT ME!
2863    * 
2864    * @param e
2865    *          DOCUMENT ME!
2866    */
2867   @Override
2868   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2869   {
2870     viewport.setColourText(colourTextMenuItem.isSelected());
2871     alignPanel.paintAlignment(true);
2872   }
2873
2874   /**
2875    * DOCUMENT ME!
2876    * 
2877    * @param e
2878    *          DOCUMENT ME!
2879    */
2880   @Override
2881   public void wrapMenuItem_actionPerformed(ActionEvent e)
2882   {
2883     scaleAbove.setVisible(wrapMenuItem.isSelected());
2884     scaleLeft.setVisible(wrapMenuItem.isSelected());
2885     scaleRight.setVisible(wrapMenuItem.isSelected());
2886     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2887     alignPanel.updateLayout();
2888   }
2889
2890   @Override
2891   public void showAllSeqs_actionPerformed(ActionEvent e)
2892   {
2893     viewport.showAllHiddenSeqs();
2894   }
2895
2896   @Override
2897   public void showAllColumns_actionPerformed(ActionEvent e)
2898   {
2899     viewport.showAllHiddenColumns();
2900     repaint();
2901   }
2902
2903   @Override
2904   public void hideSelSequences_actionPerformed(ActionEvent e)
2905   {
2906     viewport.hideAllSelectedSeqs();
2907 //    alignPanel.paintAlignment(true);
2908   }
2909
2910   /**
2911    * called by key handler and the hide all/show all menu items
2912    * 
2913    * @param toggleSeqs
2914    * @param toggleCols
2915    */
2916   private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
2917   {
2918
2919     boolean hide = false;
2920     SequenceGroup sg = viewport.getSelectionGroup();
2921     if (!toggleSeqs && !toggleCols)
2922     {
2923       // Hide everything by the current selection - this is a hack - we do the
2924       // invert and then hide
2925       // first check that there will be visible columns after the invert.
2926       if ((viewport.getColumnSelection() != null
2927               && viewport.getColumnSelection().getSelected() != null && viewport
2928               .getColumnSelection().getSelected().size() > 0)
2929               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
2930                       .getEndRes()))
2931       {
2932         // now invert the sequence set, if required - empty selection implies
2933         // that no hiding is required.
2934         if (sg != null)
2935         {
2936           invertSequenceMenuItem_actionPerformed(null);
2937           sg = viewport.getSelectionGroup();
2938           toggleSeqs = true;
2939
2940         }
2941         viewport.expandColSelection(sg, true);
2942         // finally invert the column selection and get the new sequence
2943         // selection.
2944         invertColSel_actionPerformed(null);
2945         toggleCols = true;
2946       }
2947     }
2948
2949     if (toggleSeqs)
2950     {
2951       if (sg != null && sg.getSize() != viewport.getAlignment().getHeight())
2952       {
2953         hideSelSequences_actionPerformed(null);
2954         hide = true;
2955       }
2956       else if (!(toggleCols && viewport.getColumnSelection().getSelected()
2957               .size() > 0))
2958       {
2959         showAllSeqs_actionPerformed(null);
2960       }
2961     }
2962
2963     if (toggleCols)
2964     {
2965       if (viewport.getColumnSelection().getSelected().size() > 0)
2966       {
2967         hideSelColumns_actionPerformed(null);
2968         if (!toggleSeqs)
2969         {
2970           viewport.setSelectionGroup(sg);
2971         }
2972       }
2973       else if (!hide)
2974       {
2975         showAllColumns_actionPerformed(null);
2976       }
2977     }
2978   }
2979
2980   /*
2981    * (non-Javadoc)
2982    * 
2983    * @see
2984    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
2985    * event.ActionEvent)
2986    */
2987   @Override
2988   public void hideAllButSelection_actionPerformed(ActionEvent e)
2989   {
2990     toggleHiddenRegions(false, false);
2991   }
2992
2993   /*
2994    * (non-Javadoc)
2995    * 
2996    * @see
2997    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
2998    * .ActionEvent)
2999    */
3000   @Override
3001   public void hideAllSelection_actionPerformed(ActionEvent e)
3002   {
3003     SequenceGroup sg = viewport.getSelectionGroup();
3004     viewport.expandColSelection(sg, false);
3005     viewport.hideAllSelectedSeqs();
3006     viewport.hideSelectedColumns();
3007     alignPanel.paintAlignment(true);
3008   }
3009
3010   /*
3011    * (non-Javadoc)
3012    * 
3013    * @see
3014    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
3015    * ActionEvent)
3016    */
3017   @Override
3018   public void showAllhidden_actionPerformed(ActionEvent e)
3019   {
3020     viewport.showAllHiddenColumns();
3021     viewport.showAllHiddenSeqs();
3022     alignPanel.paintAlignment(true);
3023   }
3024
3025   @Override
3026   public void hideSelColumns_actionPerformed(ActionEvent e)
3027   {
3028     viewport.hideSelectedColumns();
3029     alignPanel.paintAlignment(true);
3030   }
3031
3032   @Override
3033   public void hiddenMarkers_actionPerformed(ActionEvent e)
3034   {
3035     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
3036     repaint();
3037   }
3038
3039   /**
3040    * DOCUMENT ME!
3041    * 
3042    * @param e
3043    *          DOCUMENT ME!
3044    */
3045   @Override
3046   protected void scaleAbove_actionPerformed(ActionEvent e)
3047   {
3048     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
3049     alignPanel.paintAlignment(true);
3050   }
3051
3052   /**
3053    * DOCUMENT ME!
3054    * 
3055    * @param e
3056    *          DOCUMENT ME!
3057    */
3058   @Override
3059   protected void scaleLeft_actionPerformed(ActionEvent e)
3060   {
3061     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
3062     alignPanel.paintAlignment(true);
3063   }
3064
3065   /**
3066    * DOCUMENT ME!
3067    * 
3068    * @param e
3069    *          DOCUMENT ME!
3070    */
3071   @Override
3072   protected void scaleRight_actionPerformed(ActionEvent e)
3073   {
3074     viewport.setScaleRightWrapped(scaleRight.isSelected());
3075     alignPanel.paintAlignment(true);
3076   }
3077
3078   /**
3079    * DOCUMENT ME!
3080    * 
3081    * @param e
3082    *          DOCUMENT ME!
3083    */
3084   @Override
3085   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
3086   {
3087     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
3088     alignPanel.paintAlignment(true);
3089   }
3090
3091   /**
3092    * DOCUMENT ME!
3093    * 
3094    * @param e
3095    *          DOCUMENT ME!
3096    */
3097   @Override
3098   public void viewTextMenuItem_actionPerformed(ActionEvent e)
3099   {
3100     viewport.setShowText(viewTextMenuItem.isSelected());
3101     alignPanel.paintAlignment(true);
3102   }
3103
3104   /**
3105    * DOCUMENT ME!
3106    * 
3107    * @param e
3108    *          DOCUMENT ME!
3109    */
3110   @Override
3111   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
3112   {
3113     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
3114     alignPanel.paintAlignment(true);
3115   }
3116
3117   public FeatureSettings featureSettings;
3118
3119   @Override
3120   public void featureSettings_actionPerformed(ActionEvent e)
3121   {
3122     if (featureSettings != null)
3123     {
3124       featureSettings.close();
3125       featureSettings = null;
3126     }
3127     if (!showSeqFeatures.isSelected())
3128     {
3129       // make sure features are actually displayed
3130       showSeqFeatures.setSelected(true);
3131       showSeqFeatures_actionPerformed(null);
3132     }
3133     featureSettings = new FeatureSettings(this);
3134   }
3135
3136   /**
3137    * Set or clear 'Show Sequence Features'
3138    * 
3139    * @param evt
3140    *          DOCUMENT ME!
3141    */
3142   @Override
3143   public void showSeqFeatures_actionPerformed(ActionEvent evt)
3144   {
3145     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
3146     alignPanel.paintAlignment(true);
3147     if (alignPanel.getOverviewPanel() != null)
3148     {
3149       alignPanel.getOverviewPanel().updateOverviewImage();
3150     }
3151   }
3152
3153   /**
3154    * Set or clear 'Show Sequence Features'
3155    * 
3156    * @param evt
3157    *          DOCUMENT ME!
3158    */
3159   @Override
3160   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
3161   {
3162     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
3163             .isSelected());
3164     if (viewport.isShowSequenceFeaturesHeight())
3165     {
3166       // ensure we're actually displaying features
3167       viewport.setShowSequenceFeatures(true);
3168       showSeqFeatures.setSelected(true);
3169     }
3170     alignPanel.paintAlignment(true);
3171     if (alignPanel.getOverviewPanel() != null)
3172     {
3173       alignPanel.getOverviewPanel().updateOverviewImage();
3174     }
3175   }
3176
3177   /**
3178    * Action on toggle of the 'Show annotations' menu item. This shows or hides
3179    * the annotations panel as a whole.
3180    * 
3181    * The options to show/hide all annotations should be enabled when the panel
3182    * is shown, and disabled when the panel is hidden.
3183    * 
3184    * @param e
3185    */
3186   @Override
3187   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
3188   {
3189     final boolean setVisible = annotationPanelMenuItem.isSelected();
3190     viewport.setShowAnnotation(setVisible);
3191     this.showAllSeqAnnotations.setEnabled(setVisible);
3192     this.hideAllSeqAnnotations.setEnabled(setVisible);
3193     this.showAllAlAnnotations.setEnabled(setVisible);
3194     this.hideAllAlAnnotations.setEnabled(setVisible);
3195     alignPanel.updateLayout();
3196   }
3197
3198   @Override
3199   public void alignmentProperties()
3200   {
3201     JEditorPane editPane = new JEditorPane("text/html", "");
3202     editPane.setEditable(false);
3203     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
3204             .formatAsHtml();
3205     editPane.setText(MessageManager.formatMessage("label.html_content",
3206             new Object[]
3207             { contents.toString() }));
3208     JInternalFrame frame = new JInternalFrame();
3209     frame.getContentPane().add(new JScrollPane(editPane));
3210
3211     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3212             "label.alignment_properties", new Object[]
3213             { getTitle() }), 500, 400);
3214   }
3215
3216   /**
3217    * DOCUMENT ME!
3218    * 
3219    * @param e
3220    *          DOCUMENT ME!
3221    */
3222   @Override
3223   public void overviewMenuItem_actionPerformed(ActionEvent e)
3224   {
3225     if (alignPanel.overviewPanel != null)
3226     {
3227       return;
3228     }
3229
3230     JInternalFrame frame = new JInternalFrame();
3231     OverviewPanel overview = new OverviewPanel(alignPanel);
3232     frame.setContentPane(overview);
3233     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3234             "label.overview_params", new Object[]
3235             { this.getTitle() }), frame.getWidth(), frame.getHeight());
3236     frame.pack();
3237     frame.setLayer(JLayeredPane.PALETTE_LAYER);
3238     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
3239     {
3240       @Override
3241       public void internalFrameClosed(
3242               javax.swing.event.InternalFrameEvent evt)
3243       {
3244         alignPanel.setOverviewPanel(null);
3245       };
3246     });
3247
3248     alignPanel.setOverviewPanel(overview);
3249   }
3250
3251   @Override
3252   public void textColour_actionPerformed(ActionEvent e)
3253   {
3254     new TextColourChooser().chooseColour(alignPanel, null);
3255   }
3256
3257   /**
3258    * DOCUMENT ME!
3259    * 
3260    * @param e
3261    *          DOCUMENT ME!
3262    */
3263   @Override
3264   protected void noColourmenuItem_actionPerformed(ActionEvent e)
3265   {
3266     changeColour(null);
3267   }
3268
3269   /**
3270    * DOCUMENT ME!
3271    * 
3272    * @param e
3273    *          DOCUMENT ME!
3274    */
3275   @Override
3276   public void clustalColour_actionPerformed(ActionEvent e)
3277   {
3278     changeColour(new ClustalxColourScheme(viewport.getAlignment(),
3279             viewport.getHiddenRepSequences()));
3280   }
3281
3282   /**
3283    * DOCUMENT ME!
3284    * 
3285    * @param e
3286    *          DOCUMENT ME!
3287    */
3288   @Override
3289   public void zappoColour_actionPerformed(ActionEvent e)
3290   {
3291     changeColour(new ZappoColourScheme());
3292   }
3293
3294   /**
3295    * DOCUMENT ME!
3296    * 
3297    * @param e
3298    *          DOCUMENT ME!
3299    */
3300   @Override
3301   public void taylorColour_actionPerformed(ActionEvent e)
3302   {
3303     changeColour(new TaylorColourScheme());
3304   }
3305
3306   /**
3307    * DOCUMENT ME!
3308    * 
3309    * @param e
3310    *          DOCUMENT ME!
3311    */
3312   @Override
3313   public void hydrophobicityColour_actionPerformed(ActionEvent e)
3314   {
3315     changeColour(new HydrophobicColourScheme());
3316   }
3317
3318   /**
3319    * DOCUMENT ME!
3320    * 
3321    * @param e
3322    *          DOCUMENT ME!
3323    */
3324   @Override
3325   public void helixColour_actionPerformed(ActionEvent e)
3326   {
3327     changeColour(new HelixColourScheme());
3328   }
3329
3330   /**
3331    * DOCUMENT ME!
3332    * 
3333    * @param e
3334    *          DOCUMENT ME!
3335    */
3336   @Override
3337   public void strandColour_actionPerformed(ActionEvent e)
3338   {
3339     changeColour(new StrandColourScheme());
3340   }
3341
3342   /**
3343    * DOCUMENT ME!
3344    * 
3345    * @param e
3346    *          DOCUMENT ME!
3347    */
3348   @Override
3349   public void turnColour_actionPerformed(ActionEvent e)
3350   {
3351     changeColour(new TurnColourScheme());
3352   }
3353
3354   /**
3355    * DOCUMENT ME!
3356    * 
3357    * @param e
3358    *          DOCUMENT ME!
3359    */
3360   @Override
3361   public void buriedColour_actionPerformed(ActionEvent e)
3362   {
3363     changeColour(new BuriedColourScheme());
3364   }
3365
3366   /**
3367    * DOCUMENT ME!
3368    * 
3369    * @param e
3370    *          DOCUMENT ME!
3371    */
3372   @Override
3373   public void nucleotideColour_actionPerformed(ActionEvent e)
3374   {
3375     changeColour(new NucleotideColourScheme());
3376   }
3377
3378   @Override
3379   public void purinePyrimidineColour_actionPerformed(ActionEvent e)
3380   {
3381     changeColour(new PurinePyrimidineColourScheme());
3382   }
3383
3384   /*
3385    * public void covariationColour_actionPerformed(ActionEvent e) {
3386    * changeColour(new
3387    * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
3388    * ()[0])); }
3389    */
3390   @Override
3391   public void annotationColour_actionPerformed(ActionEvent e)
3392   {
3393     new AnnotationColourChooser(viewport, alignPanel);
3394   }
3395
3396   @Override
3397   public void annotationColumn_actionPerformed(ActionEvent e)
3398   {
3399     new AnnotationColumnChooser(viewport, alignPanel);
3400   }
3401
3402   @Override
3403   public void rnahelicesColour_actionPerformed(ActionEvent e)
3404   {
3405     new RNAHelicesColourChooser(viewport, alignPanel);
3406   }
3407
3408   /**
3409    * DOCUMENT ME!
3410    * 
3411    * @param e
3412    *          DOCUMENT ME!
3413    */
3414   @Override
3415   protected void applyToAllGroups_actionPerformed(ActionEvent e)
3416   {
3417     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
3418   }
3419
3420   /**
3421    * DOCUMENT ME!
3422    * 
3423    * @param cs
3424    *          DOCUMENT ME!
3425    */
3426   public void changeColour(ColourSchemeI cs)
3427   {
3428     // TODO: compare with applet and pull up to model method
3429     int threshold = 0;
3430
3431     if (cs != null)
3432     {
3433       if (viewport.getAbovePIDThreshold())
3434       {
3435         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
3436                 "Background");
3437         cs.setThreshold(threshold, viewport.isIgnoreGapsConsensus());
3438       }
3439       else
3440       {
3441         cs.setThreshold(0, viewport.isIgnoreGapsConsensus());
3442       }
3443
3444       if (viewport.getConservationSelected())
3445       {
3446
3447         Alignment al = (Alignment) viewport.getAlignment();
3448         Conservation c = new Conservation("All",
3449                 ResidueProperties.propHash, 3, al.getSequences(), 0,
3450                 al.getWidth() - 1);
3451
3452         c.calculate();
3453         c.verdict(false, viewport.getConsPercGaps());
3454
3455         cs.setConservation(c);
3456
3457         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
3458                 cs, "Background"));
3459       }
3460       else
3461       {
3462         cs.setConservation(null);
3463       }
3464
3465       cs.setConsensus(viewport.getSequenceConsensusHash());
3466     }
3467
3468     viewport.setGlobalColourScheme(cs);
3469
3470     if (viewport.getColourAppliesToAllGroups())
3471     {
3472
3473       for (SequenceGroup sg : viewport.getAlignment().getGroups())
3474       {
3475         if (cs == null)
3476         {
3477           sg.cs = null;
3478           continue;
3479         }
3480
3481         if (cs instanceof ClustalxColourScheme)
3482         {
3483           sg.cs = new ClustalxColourScheme(sg,
3484                   viewport.getHiddenRepSequences());
3485         }
3486         else if (cs instanceof UserColourScheme)
3487         {
3488           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
3489         }
3490         else
3491         {
3492           try
3493           {
3494             sg.cs = cs.getClass().newInstance();
3495           } catch (Exception ex)
3496           {
3497           }
3498         }
3499
3500         if (viewport.getAbovePIDThreshold()
3501                 || cs instanceof PIDColourScheme
3502                 || cs instanceof Blosum62ColourScheme)
3503         {
3504           sg.cs.setThreshold(threshold, viewport.isIgnoreGapsConsensus());
3505
3506           sg.cs.setConsensus(AAFrequency.calculate(
3507                   sg.getSequences(viewport.getHiddenRepSequences()),
3508                   sg.getStartRes(), sg.getEndRes() + 1));
3509         }
3510         else
3511         {
3512           sg.cs.setThreshold(0, viewport.isIgnoreGapsConsensus());
3513         }
3514
3515         if (viewport.getConservationSelected())
3516         {
3517           Conservation c = new Conservation("Group",
3518                   ResidueProperties.propHash, 3, sg.getSequences(viewport
3519                           .getHiddenRepSequences()), sg.getStartRes(),
3520                   sg.getEndRes() + 1);
3521           c.calculate();
3522           c.verdict(false, viewport.getConsPercGaps());
3523           sg.cs.setConservation(c);
3524         }
3525         else
3526         {
3527           sg.cs.setConservation(null);
3528         }
3529       }
3530     }
3531
3532     if (alignPanel.getOverviewPanel() != null)
3533     {
3534       alignPanel.getOverviewPanel().updateOverviewImage();
3535     }
3536
3537     alignPanel.paintAlignment(true);
3538   }
3539
3540   /**
3541    * DOCUMENT ME!
3542    * 
3543    * @param e
3544    *          DOCUMENT ME!
3545    */
3546   @Override
3547   protected void modifyPID_actionPerformed(ActionEvent e)
3548   {
3549     if (viewport.getAbovePIDThreshold()
3550             && viewport.getGlobalColourScheme() != null)
3551     {
3552       SliderPanel.setPIDSliderSource(alignPanel,
3553               viewport.getGlobalColourScheme(), "Background");
3554       SliderPanel.showPIDSlider();
3555     }
3556   }
3557
3558   /**
3559    * DOCUMENT ME!
3560    * 
3561    * @param e
3562    *          DOCUMENT ME!
3563    */
3564   @Override
3565   protected void modifyConservation_actionPerformed(ActionEvent e)
3566   {
3567     if (viewport.getConservationSelected()
3568             && viewport.getGlobalColourScheme() != null)
3569     {
3570       SliderPanel.setConservationSlider(alignPanel,
3571               viewport.getGlobalColourScheme(), "Background");
3572       SliderPanel.showConservationSlider();
3573     }
3574   }
3575
3576   /**
3577    * DOCUMENT ME!
3578    * 
3579    * @param e
3580    *          DOCUMENT ME!
3581    */
3582   @Override
3583   protected void conservationMenuItem_actionPerformed(ActionEvent e)
3584   {
3585     viewport.setConservationSelected(conservationMenuItem.isSelected());
3586
3587     viewport.setAbovePIDThreshold(false);
3588     abovePIDThreshold.setSelected(false);
3589
3590     changeColour(viewport.getGlobalColourScheme());
3591
3592     modifyConservation_actionPerformed(null);
3593   }
3594
3595   /**
3596    * DOCUMENT ME!
3597    * 
3598    * @param e
3599    *          DOCUMENT ME!
3600    */
3601   @Override
3602   public void abovePIDThreshold_actionPerformed(ActionEvent e)
3603   {
3604     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
3605
3606     conservationMenuItem.setSelected(false);
3607     viewport.setConservationSelected(false);
3608
3609     changeColour(viewport.getGlobalColourScheme());
3610
3611     modifyPID_actionPerformed(null);
3612   }
3613
3614   /**
3615    * DOCUMENT ME!
3616    * 
3617    * @param e
3618    *          DOCUMENT ME!
3619    */
3620   @Override
3621   public void userDefinedColour_actionPerformed(ActionEvent e)
3622   {
3623     if (e.getActionCommand().equals(
3624             MessageManager.getString("action.user_defined")))
3625     {
3626       new UserDefinedColours(alignPanel, null);
3627     }
3628     else
3629     {
3630       UserColourScheme udc = (UserColourScheme) UserDefinedColours
3631               .getUserColourSchemes().get(e.getActionCommand());
3632
3633       changeColour(udc);
3634     }
3635   }
3636
3637   public void updateUserColourMenu()
3638   {
3639
3640     Component[] menuItems = colourMenu.getMenuComponents();
3641     int iSize = menuItems.length;
3642     for (int i = 0; i < iSize; i++)
3643     {
3644       if (menuItems[i].getName() != null
3645               && menuItems[i].getName().equals("USER_DEFINED"))
3646       {
3647         colourMenu.remove(menuItems[i]);
3648         iSize--;
3649       }
3650     }
3651     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
3652     {
3653       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
3654               .getUserColourSchemes().keys();
3655
3656       while (userColours.hasMoreElements())
3657       {
3658         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
3659                 userColours.nextElement().toString());
3660         radioItem.setName("USER_DEFINED");
3661         radioItem.addMouseListener(new MouseAdapter()
3662         {
3663           @Override
3664           public void mousePressed(MouseEvent evt)
3665           {
3666             if (evt.isControlDown()
3667                     || SwingUtilities.isRightMouseButton(evt))
3668             {
3669               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
3670
3671               int option = JOptionPane.showInternalConfirmDialog(
3672                       jalview.gui.Desktop.desktop,
3673                       MessageManager
3674                               .getString("label.remove_from_default_list"),
3675                       MessageManager
3676                               .getString("label.remove_user_defined_colour"),
3677                       JOptionPane.YES_NO_OPTION);
3678               if (option == JOptionPane.YES_OPTION)
3679               {
3680                 jalview.gui.UserDefinedColours
3681                         .removeColourFromDefaults(radioItem.getText());
3682                 colourMenu.remove(radioItem);
3683               }
3684               else
3685               {
3686                 radioItem.addActionListener(new ActionListener()
3687                 {
3688                   @Override
3689                   public void actionPerformed(ActionEvent evt)
3690                   {
3691                     userDefinedColour_actionPerformed(evt);
3692                   }
3693                 });
3694               }
3695             }
3696           }
3697         });
3698         radioItem.addActionListener(new ActionListener()
3699         {
3700           @Override
3701           public void actionPerformed(ActionEvent evt)
3702           {
3703             userDefinedColour_actionPerformed(evt);
3704           }
3705         });
3706
3707         colourMenu.insert(radioItem, 15);
3708         colours.add(radioItem);
3709       }
3710     }
3711   }
3712
3713   /**
3714    * DOCUMENT ME!
3715    * 
3716    * @param e
3717    *          DOCUMENT ME!
3718    */
3719   @Override
3720   public void PIDColour_actionPerformed(ActionEvent e)
3721   {
3722     changeColour(new PIDColourScheme());
3723   }
3724
3725   /**
3726    * DOCUMENT ME!
3727    * 
3728    * @param e
3729    *          DOCUMENT ME!
3730    */
3731   @Override
3732   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
3733   {
3734     changeColour(new Blosum62ColourScheme());
3735   }
3736
3737   /**
3738    * DOCUMENT ME!
3739    * 
3740    * @param e
3741    *          DOCUMENT ME!
3742    */
3743   @Override
3744   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3745   {
3746     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3747     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
3748             .getAlignment().getSequenceAt(0), null);
3749     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3750             viewport.getAlignment()));
3751     alignPanel.paintAlignment(true);
3752   }
3753
3754   /**
3755    * DOCUMENT ME!
3756    * 
3757    * @param e
3758    *          DOCUMENT ME!
3759    */
3760   @Override
3761   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3762   {
3763     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3764     AlignmentSorter.sortByID(viewport.getAlignment());
3765     addHistoryItem(new OrderCommand("ID Sort", oldOrder,
3766             viewport.getAlignment()));
3767     alignPanel.paintAlignment(true);
3768   }
3769
3770   /**
3771    * DOCUMENT ME!
3772    * 
3773    * @param e
3774    *          DOCUMENT ME!
3775    */
3776   @Override
3777   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3778   {
3779     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3780     AlignmentSorter.sortByLength(viewport.getAlignment());
3781     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3782             viewport.getAlignment()));
3783     alignPanel.paintAlignment(true);
3784   }
3785
3786   /**
3787    * DOCUMENT ME!
3788    * 
3789    * @param e
3790    *          DOCUMENT ME!
3791    */
3792   @Override
3793   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3794   {
3795     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3796     AlignmentSorter.sortByGroup(viewport.getAlignment());
3797     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3798             viewport.getAlignment()));
3799
3800     alignPanel.paintAlignment(true);
3801   }
3802
3803   /**
3804    * DOCUMENT ME!
3805    * 
3806    * @param e
3807    *          DOCUMENT ME!
3808    */
3809   @Override
3810   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3811   {
3812     new RedundancyPanel(alignPanel, this);
3813   }
3814
3815   /**
3816    * DOCUMENT ME!
3817    * 
3818    * @param e
3819    *          DOCUMENT ME!
3820    */
3821   @Override
3822   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3823   {
3824     if ((viewport.getSelectionGroup() == null)
3825             || (viewport.getSelectionGroup().getSize() < 2))
3826     {
3827       JOptionPane.showInternalMessageDialog(this, MessageManager
3828               .getString("label.you_must_select_least_two_sequences"),
3829               MessageManager.getString("label.invalid_selection"),
3830               JOptionPane.WARNING_MESSAGE);
3831     }
3832     else
3833     {
3834       JInternalFrame frame = new JInternalFrame();
3835       frame.setContentPane(new PairwiseAlignPanel(viewport));
3836       Desktop.addInternalFrame(frame,
3837               MessageManager.getString("action.pairwise_alignment"), 600,
3838               500);
3839     }
3840   }
3841
3842   /**
3843    * DOCUMENT ME!
3844    * 
3845    * @param e
3846    *          DOCUMENT ME!
3847    */
3848   @Override
3849   public void PCAMenuItem_actionPerformed(ActionEvent e)
3850   {
3851     if (((viewport.getSelectionGroup() != null)
3852             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
3853             .getSelectionGroup().getSize() > 0))
3854             || (viewport.getAlignment().getHeight() < 4))
3855     {
3856       JOptionPane
3857               .showInternalMessageDialog(
3858                       this,
3859                       MessageManager
3860                               .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
3861                       MessageManager
3862                               .getString("label.sequence_selection_insufficient"),
3863                       JOptionPane.WARNING_MESSAGE);
3864
3865       return;
3866     }
3867
3868     new PCAPanel(alignPanel);
3869   }
3870
3871   @Override
3872   public void autoCalculate_actionPerformed(ActionEvent e)
3873   {
3874     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3875     if (viewport.autoCalculateConsensus)
3876     {
3877       viewport.firePropertyChange("alignment", null, viewport
3878               .getAlignment().getSequences());
3879     }
3880   }
3881
3882   @Override
3883   public void sortByTreeOption_actionPerformed(ActionEvent e)
3884   {
3885     viewport.sortByTree = sortByTree.isSelected();
3886   }
3887
3888   @Override
3889   protected void listenToViewSelections_actionPerformed(ActionEvent e)
3890   {
3891     viewport.followSelection = listenToViewSelections.isSelected();
3892   }
3893
3894   /**
3895    * DOCUMENT ME!
3896    * 
3897    * @param e
3898    *          DOCUMENT ME!
3899    */
3900   @Override
3901   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3902   {
3903     newTreePanel("AV", "PID", "Average distance tree using PID");
3904   }
3905
3906   /**
3907    * DOCUMENT ME!
3908    * 
3909    * @param e
3910    *          DOCUMENT ME!
3911    */
3912   @Override
3913   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3914   {
3915     newTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3916   }
3917
3918   /**
3919    * DOCUMENT ME!
3920    * 
3921    * @param e
3922    *          DOCUMENT ME!
3923    */
3924   @Override
3925   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3926   {
3927     newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3928   }
3929
3930   /**
3931    * DOCUMENT ME!
3932    * 
3933    * @param e
3934    *          DOCUMENT ME!
3935    */
3936   @Override
3937   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3938   {
3939     newTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3940   }
3941
3942   /**
3943    * DOCUMENT ME!
3944    * 
3945    * @param type
3946    *          DOCUMENT ME!
3947    * @param pwType
3948    *          DOCUMENT ME!
3949    * @param title
3950    *          DOCUMENT ME!
3951    */
3952   void newTreePanel(String type, String pwType, String title)
3953   {
3954     TreePanel tp;
3955
3956     if (viewport.getSelectionGroup() != null
3957             && viewport.getSelectionGroup().getSize() > 0)
3958     {
3959       if (viewport.getSelectionGroup().getSize() < 3)
3960       {
3961         JOptionPane
3962                 .showMessageDialog(
3963                         Desktop.desktop,
3964                         MessageManager
3965                                 .getString("label.you_need_more_two_sequences_selected_build_tree"),
3966                         MessageManager
3967                                 .getString("label.not_enough_sequences"),
3968                         JOptionPane.WARNING_MESSAGE);
3969         return;
3970       }
3971
3972       SequenceGroup sg = viewport.getSelectionGroup();
3973
3974       /* Decide if the selection is a column region */
3975       for (SequenceI _s : sg.getSequences())
3976       {
3977         if (_s.getLength() < sg.getEndRes())
3978         {
3979           JOptionPane
3980                   .showMessageDialog(
3981                           Desktop.desktop,
3982                           MessageManager
3983                                   .getString("label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
3984                           MessageManager
3985                                   .getString("label.sequences_selection_not_aligned"),
3986                           JOptionPane.WARNING_MESSAGE);
3987
3988           return;
3989         }
3990       }
3991
3992       title = title + " on region";
3993       tp = new TreePanel(alignPanel, type, pwType);
3994     }
3995     else
3996     {
3997       // are the visible sequences aligned?
3998       if (!viewport.getAlignment().isAligned(false))
3999       {
4000         JOptionPane
4001                 .showMessageDialog(
4002                         Desktop.desktop,
4003                         MessageManager
4004                                 .getString("label.sequences_must_be_aligned_before_creating_tree"),
4005                         MessageManager
4006                                 .getString("label.sequences_not_aligned"),
4007                         JOptionPane.WARNING_MESSAGE);
4008
4009         return;
4010       }
4011
4012       if (viewport.getAlignment().getHeight() < 2)
4013       {
4014         return;
4015       }
4016
4017       tp = new TreePanel(alignPanel, type, pwType);
4018     }
4019
4020     title += " from ";
4021
4022     if (viewport.viewName != null)
4023     {
4024       title += viewport.viewName + " of ";
4025     }
4026
4027     title += this.title;
4028
4029     Desktop.addInternalFrame(tp, title, 600, 500);
4030   }
4031
4032   /**
4033    * DOCUMENT ME!
4034    * 
4035    * @param title
4036    *          DOCUMENT ME!
4037    * @param order
4038    *          DOCUMENT ME!
4039    */
4040   public void addSortByOrderMenuItem(String title,
4041           final AlignmentOrder order)
4042   {
4043     final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new Object[]{title}));
4044     sort.add(item);
4045     item.addActionListener(new java.awt.event.ActionListener()
4046     {
4047       @Override
4048       public void actionPerformed(ActionEvent e)
4049       {
4050         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4051
4052         // TODO: JBPNote - have to map order entries to curent SequenceI
4053         // pointers
4054         AlignmentSorter.sortBy(viewport.getAlignment(), order);
4055
4056         addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
4057                 .getAlignment()));
4058
4059         alignPanel.paintAlignment(true);
4060       }
4061     });
4062   }
4063
4064   /**
4065    * Add a new sort by annotation score menu item
4066    * 
4067    * @param sort
4068    *          the menu to add the option to
4069    * @param scoreLabel
4070    *          the label used to retrieve scores for each sequence on the
4071    *          alignment
4072    */
4073   public void addSortByAnnotScoreMenuItem(JMenu sort,
4074           final String scoreLabel)
4075   {
4076     final JMenuItem item = new JMenuItem(scoreLabel);
4077     sort.add(item);
4078     item.addActionListener(new java.awt.event.ActionListener()
4079     {
4080       @Override
4081       public void actionPerformed(ActionEvent e)
4082       {
4083         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4084         AlignmentSorter.sortByAnnotationScore(scoreLabel,
4085                 viewport.getAlignment());// ,viewport.getSelectionGroup());
4086         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
4087                 viewport.getAlignment()));
4088         alignPanel.paintAlignment(true);
4089       }
4090     });
4091   }
4092
4093   /**
4094    * last hash for alignment's annotation array - used to minimise cost of
4095    * rebuild.
4096    */
4097   protected int _annotationScoreVectorHash;
4098
4099   /**
4100    * search the alignment and rebuild the sort by annotation score submenu the
4101    * last alignment annotation vector hash is stored to minimize cost of
4102    * rebuilding in subsequence calls.
4103    * 
4104    */
4105   @Override
4106   public void buildSortByAnnotationScoresMenu()
4107   {
4108     if (viewport.getAlignment().getAlignmentAnnotation() == null)
4109     {
4110       return;
4111     }
4112
4113     if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
4114     {
4115       sortByAnnotScore.removeAll();
4116       // almost certainly a quicker way to do this - but we keep it simple
4117       Hashtable scoreSorts = new Hashtable();
4118       AlignmentAnnotation aann[];
4119       for (SequenceI sqa : viewport.getAlignment().getSequences())
4120       {
4121         aann = sqa.getAnnotation();
4122         for (int i = 0; aann != null && i < aann.length; i++)
4123         {
4124           if (aann[i].hasScore() && aann[i].sequenceRef != null)
4125           {
4126             scoreSorts.put(aann[i].label, aann[i].label);
4127           }
4128         }
4129       }
4130       Enumeration labels = scoreSorts.keys();
4131       while (labels.hasMoreElements())
4132       {
4133         addSortByAnnotScoreMenuItem(sortByAnnotScore,
4134                 (String) labels.nextElement());
4135       }
4136       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
4137       scoreSorts.clear();
4138
4139       _annotationScoreVectorHash = viewport.getAlignment()
4140               .getAlignmentAnnotation().hashCode();
4141     }
4142   }
4143
4144   /**
4145    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
4146    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
4147    * call. Listeners are added to remove the menu item when the treePanel is
4148    * closed, and adjust the tree leaf to sequence mapping when the alignment is
4149    * modified.
4150    * 
4151    * @param treePanel
4152    *          Displayed tree window.
4153    * @param title
4154    *          SortBy menu item title.
4155    */
4156   @Override
4157   public void buildTreeMenu()
4158   {
4159     calculateTree.removeAll();
4160     // build the calculate menu
4161
4162     for (final String type : new String[]
4163     { "NJ", "AV" })
4164     {
4165       String treecalcnm = MessageManager.getString("label.tree_calc_"
4166               + type.toLowerCase());
4167       for (final String pwtype : ResidueProperties.scoreMatrices.keySet())
4168       {
4169         JMenuItem tm = new JMenuItem();
4170         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
4171         if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
4172         {
4173           String smn = MessageManager.getStringOrReturn(
4174                   "label.score_model_", sm.getName());
4175           final String title = MessageManager.formatMessage(
4176                   "label.treecalc_title", treecalcnm, smn);
4177           tm.setText(title);//
4178           tm.addActionListener(new java.awt.event.ActionListener()
4179           {
4180             @Override
4181             public void actionPerformed(ActionEvent e)
4182             {
4183               newTreePanel(type, pwtype, title);
4184             }
4185           });
4186           calculateTree.add(tm);
4187         }
4188
4189       }
4190     }
4191     sortByTreeMenu.removeAll();
4192
4193     List<Component> comps = PaintRefresher.components.get(viewport
4194             .getSequenceSetId());
4195     List<TreePanel> treePanels = new ArrayList<TreePanel>();
4196     for (Component comp : comps)
4197     {
4198       if (comp instanceof TreePanel)
4199       {
4200         treePanels.add((TreePanel) comp);
4201       }
4202     }
4203
4204     if (treePanels.size() < 1)
4205     {
4206       sortByTreeMenu.setVisible(false);
4207       return;
4208     }
4209
4210     sortByTreeMenu.setVisible(true);
4211
4212     for (final TreePanel tp : treePanels)
4213     {
4214       final JMenuItem item = new JMenuItem(tp.getTitle());
4215       item.addActionListener(new java.awt.event.ActionListener()
4216       {
4217         @Override
4218         public void actionPerformed(ActionEvent e)
4219         {
4220           tp.sortByTree_actionPerformed();
4221           addHistoryItem(tp.sortAlignmentIn(alignPanel));
4222
4223         }
4224       });
4225
4226       sortByTreeMenu.add(item);
4227     }
4228   }
4229
4230   public boolean sortBy(AlignmentOrder alorder, String undoname)
4231   {
4232     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4233     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
4234     if (undoname != null)
4235     {
4236       addHistoryItem(new OrderCommand(undoname, oldOrder,
4237               viewport.getAlignment()));
4238     }
4239     alignPanel.paintAlignment(true);
4240     return true;
4241   }
4242
4243   /**
4244    * Work out whether the whole set of sequences or just the selected set will
4245    * be submitted for multiple alignment.
4246    * 
4247    */
4248   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
4249   {
4250     // Now, check we have enough sequences
4251     AlignmentView msa = null;
4252
4253     if ((viewport.getSelectionGroup() != null)
4254             && (viewport.getSelectionGroup().getSize() > 1))
4255     {
4256       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
4257       // some common interface!
4258       /*
4259        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
4260        * SequenceI[sz = seqs.getSize(false)];
4261        * 
4262        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
4263        * seqs.getSequenceAt(i); }
4264        */
4265       msa = viewport.getAlignmentView(true);
4266     }
4267     else if (viewport.getSelectionGroup() != null
4268             && viewport.getSelectionGroup().getSize() == 1)
4269     {
4270       int option = JOptionPane.showConfirmDialog(this,
4271               MessageManager.getString("warn.oneseq_msainput_selection"),
4272               MessageManager.getString("label.invalid_selection"),
4273               JOptionPane.OK_CANCEL_OPTION);
4274       if (option == JOptionPane.OK_OPTION)
4275       {
4276         msa = viewport.getAlignmentView(false);
4277       }
4278     }
4279     else
4280     {
4281       msa = viewport.getAlignmentView(false);
4282     }
4283     return msa;
4284   }
4285
4286   /**
4287    * Decides what is submitted to a secondary structure prediction service: the
4288    * first sequence in the alignment, or in the current selection, or, if the
4289    * alignment is 'aligned' (ie padded with gaps), then the currently selected
4290    * region or the whole alignment. (where the first sequence in the set is the
4291    * one that the prediction will be for).
4292    */
4293   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4294   {
4295     AlignmentView seqs = null;
4296
4297     if ((viewport.getSelectionGroup() != null)
4298             && (viewport.getSelectionGroup().getSize() > 0))
4299     {
4300       seqs = viewport.getAlignmentView(true);
4301     }
4302     else
4303     {
4304       seqs = viewport.getAlignmentView(false);
4305     }
4306     // limit sequences - JBPNote in future - could spawn multiple prediction
4307     // jobs
4308     // TODO: viewport.getAlignment().isAligned is a global state - the local
4309     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4310     if (!viewport.getAlignment().isAligned(false))
4311     {
4312       seqs.setSequences(new SeqCigar[]
4313       { seqs.getSequences()[0] });
4314       // TODO: if seqs.getSequences().length>1 then should really have warned
4315       // user!
4316
4317     }
4318     return seqs;
4319   }
4320
4321   /**
4322    * DOCUMENT ME!
4323    * 
4324    * @param e
4325    *          DOCUMENT ME!
4326    */
4327   @Override
4328   protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
4329   {
4330     // Pick the tree file
4331     JalviewFileChooser chooser = new JalviewFileChooser(
4332             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4333     chooser.setFileView(new JalviewFileView());
4334     chooser.setDialogTitle(MessageManager
4335             .getString("label.select_newick_like_tree_file"));
4336     chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
4337
4338     int value = chooser.showOpenDialog(null);
4339
4340     if (value == JalviewFileChooser.APPROVE_OPTION)
4341     {
4342       String choice = chooser.getSelectedFile().getPath();
4343       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4344       jalview.io.NewickFile fin = null;
4345       try
4346       {
4347         fin = new jalview.io.NewickFile(choice, "File");
4348         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4349       } catch (Exception ex)
4350       {
4351         JOptionPane
4352                 .showMessageDialog(
4353                         Desktop.desktop,
4354                         ex.getMessage(),
4355                         MessageManager
4356                                 .getString("label.problem_reading_tree_file"),
4357                         JOptionPane.WARNING_MESSAGE);
4358         ex.printStackTrace();
4359       }
4360       if (fin != null && fin.hasWarningMessage())
4361       {
4362         JOptionPane.showMessageDialog(Desktop.desktop, fin
4363                 .getWarningMessage(), MessageManager
4364                 .getString("label.possible_problem_with_tree_file"),
4365                 JOptionPane.WARNING_MESSAGE);
4366       }
4367     }
4368   }
4369
4370   @Override
4371   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4372   {
4373     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4374   }
4375
4376   public TreePanel ShowNewickTree(NewickFile nf, String title)
4377   {
4378     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4379   }
4380
4381   public TreePanel ShowNewickTree(NewickFile nf, String title,
4382           AlignmentView input)
4383   {
4384     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4385   }
4386
4387   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4388           int h, int x, int y)
4389   {
4390     return ShowNewickTree(nf, title, null, w, h, x, y);
4391   }
4392
4393   /**
4394    * Add a treeviewer for the tree extracted from a newick file object to the
4395    * current alignment view
4396    * 
4397    * @param nf
4398    *          the tree
4399    * @param title
4400    *          tree viewer title
4401    * @param input
4402    *          Associated alignment input data (or null)
4403    * @param w
4404    *          width
4405    * @param h
4406    *          height
4407    * @param x
4408    *          position
4409    * @param y
4410    *          position
4411    * @return TreePanel handle
4412    */
4413   public TreePanel ShowNewickTree(NewickFile nf, String title,
4414           AlignmentView input, int w, int h, int x, int y)
4415   {
4416     TreePanel tp = null;
4417
4418     try
4419     {
4420       nf.parse();
4421
4422       if (nf.getTree() != null)
4423       {
4424         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4425
4426         tp.setSize(w, h);
4427
4428         if (x > 0 && y > 0)
4429         {
4430           tp.setLocation(x, y);
4431         }
4432
4433         Desktop.addInternalFrame(tp, title, w, h);
4434       }
4435     } catch (Exception ex)
4436     {
4437       ex.printStackTrace();
4438     }
4439
4440     return tp;
4441   }
4442
4443   private boolean buildingMenu = false;
4444
4445   /**
4446    * Generates menu items and listener event actions for web service clients
4447    * 
4448    */
4449   public void BuildWebServiceMenu()
4450   {
4451     while (buildingMenu)
4452     {
4453       try
4454       {
4455         System.err.println("Waiting for building menu to finish.");
4456         Thread.sleep(10);
4457       } catch (Exception e)
4458       {
4459       }
4460     }
4461     final AlignFrame me = this;
4462     buildingMenu = true;
4463     new Thread(new Runnable()
4464     {
4465       @Override
4466       public void run()
4467       {
4468         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
4469         try
4470         {
4471           System.err.println("Building ws menu again "
4472                   + Thread.currentThread());
4473           // TODO: add support for context dependent disabling of services based
4474           // on
4475           // alignment and current selection
4476           // TODO: add additional serviceHandle parameter to specify abstract
4477           // handler
4478           // class independently of AbstractName
4479           // TODO: add in rediscovery GUI function to restart discoverer
4480           // TODO: group services by location as well as function and/or
4481           // introduce
4482           // object broker mechanism.
4483           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4484           final IProgressIndicator af = me;
4485           final JMenu msawsmenu = new JMenu("Alignment");
4486           final JMenu secstrmenu = new JMenu(
4487                   "Secondary Structure Prediction");
4488           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4489           final JMenu analymenu = new JMenu("Analysis");
4490           final JMenu dismenu = new JMenu("Protein Disorder");
4491           // final JMenu msawsmenu = new
4492           // JMenu(MessageManager.getString("label.alignment"));
4493           // final JMenu secstrmenu = new
4494           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
4495           // final JMenu seqsrchmenu = new
4496           // JMenu(MessageManager.getString("label.sequence_database_search"));
4497           // final JMenu analymenu = new
4498           // JMenu(MessageManager.getString("label.analysis"));
4499           // final JMenu dismenu = new
4500           // JMenu(MessageManager.getString("label.protein_disorder"));
4501           // JAL-940 - only show secondary structure prediction services from
4502           // the legacy server
4503           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4504               // &&
4505           Discoverer.services != null && (Discoverer.services.size() > 0))
4506           {
4507             // TODO: refactor to allow list of AbstractName/Handler bindings to
4508             // be
4509             // stored or retrieved from elsewhere
4510             // No MSAWS used any more:
4511             // Vector msaws = null; // (Vector)
4512             // Discoverer.services.get("MsaWS");
4513             Vector secstrpr = (Vector) Discoverer.services
4514                     .get("SecStrPred");
4515             if (secstrpr != null)
4516             {
4517               // Add any secondary structure prediction services
4518               for (int i = 0, j = secstrpr.size(); i < j; i++)
4519               {
4520                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4521                         .get(i);
4522                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4523                         .getServiceClient(sh);
4524                 int p = secstrmenu.getItemCount();
4525                 impl.attachWSMenuEntry(secstrmenu, me);
4526                 int q = secstrmenu.getItemCount();
4527                 for (int litm = p; litm < q; litm++)
4528                 {
4529                   legacyItems.add(secstrmenu.getItem(litm));
4530                 }
4531               }
4532             }
4533           }
4534
4535           // Add all submenus in the order they should appear on the web
4536           // services menu
4537           wsmenu.add(msawsmenu);
4538           wsmenu.add(secstrmenu);
4539           wsmenu.add(dismenu);
4540           wsmenu.add(analymenu);
4541           // No search services yet
4542           // wsmenu.add(seqsrchmenu);
4543
4544           javax.swing.SwingUtilities.invokeLater(new Runnable()
4545           {
4546             @Override
4547             public void run()
4548             {
4549               try
4550               {
4551                 webService.removeAll();
4552                 // first, add discovered services onto the webservices menu
4553                 if (wsmenu.size() > 0)
4554                 {
4555                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4556                   {
4557                     webService.add(wsmenu.get(i));
4558                   }
4559                 }
4560                 else
4561                 {
4562                   webService.add(me.webServiceNoServices);
4563                 }
4564                 // TODO: move into separate menu builder class.
4565                 boolean new_sspred = false;
4566                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4567                 {
4568                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4569                   if (jws2servs != null)
4570                   {
4571                     if (jws2servs.hasServices())
4572                     {
4573                       jws2servs.attachWSMenuEntry(webService, me);
4574                       for (Jws2Instance sv : jws2servs.getServices())
4575                       {
4576                         if (sv.description.toLowerCase().contains("jpred"))
4577                         {
4578                           for (JMenuItem jmi : legacyItems)
4579                           {
4580                             jmi.setVisible(false);
4581                           }
4582                         }
4583                       }
4584
4585                     }
4586                     if (jws2servs.isRunning())
4587                     {
4588                       JMenuItem tm = new JMenuItem(
4589                               "Still discovering JABA Services");
4590                       tm.setEnabled(false);
4591                       webService.add(tm);
4592                     }
4593                   }
4594                 }
4595                 build_urlServiceMenu(me.webService);
4596                 build_fetchdbmenu(webService);
4597                 for (JMenu item : wsmenu)
4598                 {
4599                   if (item.getItemCount() == 0)
4600                   {
4601                     item.setEnabled(false);
4602                   }
4603                   else
4604                   {
4605                     item.setEnabled(true);
4606                   }
4607                 }
4608               } catch (Exception e)
4609               {
4610                 Cache.log
4611                         .debug("Exception during web service menu building process.",
4612                                 e);
4613               }
4614             }
4615           });
4616         } catch (Exception e)
4617         {
4618         }
4619         buildingMenu = false;
4620       }
4621     }).start();
4622
4623   }
4624
4625   /**
4626    * construct any groupURL type service menu entries.
4627    * 
4628    * @param webService
4629    */
4630   private void build_urlServiceMenu(JMenu webService)
4631   {
4632     // TODO: remove this code when 2.7 is released
4633     // DEBUG - alignmentView
4634     /*
4635      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4636      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4637      * 
4638      * @Override public void actionPerformed(ActionEvent e) {
4639      * jalview.datamodel.AlignmentView
4640      * .testSelectionViews(af.viewport.getAlignment(),
4641      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4642      * 
4643      * }); webService.add(testAlView);
4644      */
4645     // TODO: refactor to RestClient discoverer and merge menu entries for
4646     // rest-style services with other types of analysis/calculation service
4647     // SHmmr test client - still being implemented.
4648     // DEBUG - alignmentView
4649
4650     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4651             .getRestClients())
4652     {
4653       client.attachWSMenuEntry(
4654               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4655               this);
4656     }
4657   }
4658
4659   /*
4660    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4661    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4662    * getProperty("LAST_DIRECTORY"));
4663    * 
4664    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4665    * to Vamsas file"); chooser.setToolTipText("Export");
4666    * 
4667    * int value = chooser.showSaveDialog(this);
4668    * 
4669    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4670    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4671    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4672    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4673    */
4674   /**
4675    * prototype of an automatically enabled/disabled analysis function
4676    * 
4677    */
4678   protected void setShowProductsEnabled()
4679   {
4680     SequenceI[] selection = viewport.getSequenceSelection();
4681     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4682             viewport.getAlignment().getDataset()))
4683     {
4684       showProducts.setEnabled(true);
4685
4686     }
4687     else
4688     {
4689       showProducts.setEnabled(false);
4690     }
4691   }
4692
4693   /**
4694    * search selection for sequence xRef products and build the show products
4695    * menu.
4696    * 
4697    * @param selection
4698    * @param dataset
4699    * @return true if showProducts menu should be enabled.
4700    */
4701   public boolean canShowProducts(SequenceI[] selection,
4702           boolean isRegionSelection, Alignment dataset)
4703   {
4704     boolean showp = false;
4705     try
4706     {
4707       showProducts.removeAll();
4708       final boolean dna = viewport.getAlignment().isNucleotide();
4709       final Alignment ds = dataset;
4710       String[] ptypes = (selection == null || selection.length == 0) ? null
4711               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4712       // Object[] prods =
4713       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4714       // selection, dataset, true);
4715       final SequenceI[] sel = selection;
4716       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4717       {
4718         showp = true;
4719         final boolean isRegSel = isRegionSelection;
4720         final AlignFrame af = this;
4721         final String source = ptypes[t];
4722         JMenuItem xtype = new JMenuItem(ptypes[t]);
4723         xtype.addActionListener(new ActionListener()
4724         {
4725
4726           @Override
4727           public void actionPerformed(ActionEvent e)
4728           {
4729             // TODO: new thread for this call with vis-delay
4730             af.showProductsFor(af.viewport.getSequenceSelection(),
4731                     isRegSel, dna, source);
4732           }
4733
4734         });
4735         showProducts.add(xtype);
4736       }
4737       showProducts.setVisible(showp);
4738       showProducts.setEnabled(showp);
4739     } catch (Exception e)
4740     {
4741       jalview.bin.Cache.log
4742               .warn("canTranslate threw an exception - please report to help@jalview.org",
4743                       e);
4744       return false;
4745     }
4746     return showp;
4747   }
4748
4749   protected void showProductsFor(final SequenceI[] sel,
4750           final boolean isRegSel, final boolean dna, final String source)
4751   {
4752     Runnable foo = new Runnable()
4753     {
4754
4755       @Override
4756       public void run()
4757       {
4758         final long sttime = System.currentTimeMillis();
4759         AlignFrame.this.setProgressBar(MessageManager.formatMessage(
4760                 "status.searching_for_sequences_from", new Object[]
4761                 { source }), sttime);
4762         try
4763         {
4764           // update our local dataset reference
4765           Alignment ds = AlignFrame.this.getViewport().getAlignment()
4766                   .getDataset();
4767           Alignment prods = CrossRef
4768                   .findXrefSequences(sel, dna, source, ds);
4769           if (prods != null)
4770           {
4771             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4772             for (int s = 0; s < sprods.length; s++)
4773             {
4774               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4775               if (ds.getSequences() == null
4776                       || !ds.getSequences().contains(
4777                               sprods[s].getDatasetSequence()))
4778               {
4779                 ds.addSequence(sprods[s].getDatasetSequence());
4780               }
4781               sprods[s].updatePDBIds();
4782             }
4783             Alignment al = new Alignment(sprods);
4784             Set<AlignedCodonFrame> cf = prods.getCodonFrames();
4785             al.setDataset(ds);
4786             for (AlignedCodonFrame acf : cf)
4787             {
4788               al.addCodonFrame(acf);
4789             }
4790             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4791                     DEFAULT_HEIGHT);
4792             String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
4793                     + " for " + ((isRegSel) ? "selected region of " : "")
4794                     + getTitle();
4795             naf.setTitle(newtitle);
4796
4797             // remove this flag once confirmed we want a split view
4798             boolean asSplitFrame = true;
4799             if (asSplitFrame)
4800             {
4801               final Alignment copyAlignment = new Alignment(new Alignment(
4802                       AlignFrame.this.viewport.getSequenceSelection()));
4803               AlignFrame copyThis = new AlignFrame(copyAlignment,
4804                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
4805               copyThis.setTitle(AlignFrame.this.getTitle());
4806               // SplitFrame with dna above, protein below
4807               SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
4808                       dna ? naf : copyThis);
4809               naf.setVisible(true);
4810               copyThis.setVisible(true);
4811               String linkedTitle = MessageManager
4812                       .getString("label.linked_view_title");
4813               Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
4814             }
4815             else
4816             {
4817               Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4818                       DEFAULT_HEIGHT);
4819             }
4820           }
4821           else
4822           {
4823             System.err.println("No Sequences generated for xRef type "
4824                     + source);
4825           }
4826         } catch (Exception e)
4827         {
4828           jalview.bin.Cache.log.error(
4829                   "Exception when finding crossreferences", e);
4830         } catch (OutOfMemoryError e)
4831         {
4832           new OOMWarning("whilst fetching crossreferences", e);
4833         } catch (Error e)
4834         {
4835           jalview.bin.Cache.log.error("Error when finding crossreferences",
4836                   e);
4837         }
4838         AlignFrame.this.setProgressBar(MessageManager.formatMessage(
4839                 "status.finished_searching_for_sequences_from",
4840                 new Object[]
4841                 { source }),
4842                 sttime);
4843       }
4844
4845     };
4846     Thread frunner = new Thread(foo);
4847     frunner.start();
4848   }
4849
4850   public boolean canShowTranslationProducts(SequenceI[] selection,
4851           AlignmentI alignment)
4852   {
4853     // old way
4854     try
4855     {
4856       return (jalview.analysis.Dna.canTranslate(selection,
4857               viewport.getViewAsVisibleContigs(true)));
4858     } catch (Exception e)
4859     {
4860       jalview.bin.Cache.log
4861               .warn("canTranslate threw an exception - please report to help@jalview.org",
4862                       e);
4863       return false;
4864     }
4865   }
4866
4867   /**
4868    * Construct and display a new frame containing the translation of this
4869    * frame's DNA sequences to their aligned protein (amino acid) equivalents.
4870    */
4871   @Override
4872   public void showTranslation_actionPerformed(ActionEvent e)
4873   {
4874     AlignmentI al = null;
4875     try
4876     {
4877       Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
4878
4879       al = dna.translateCdna();
4880     } catch (Exception ex)
4881     {
4882       jalview.bin.Cache.log.error(
4883               "Exception during translation. Please report this !", ex);
4884       final String msg = MessageManager
4885               .getString("label.error_when_translating_sequences_submit_bug_report");
4886       final String title = MessageManager
4887               .getString("label.implementation_error")
4888               + MessageManager.getString("translation_failed");
4889       JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
4890               JOptionPane.ERROR_MESSAGE);
4891       return;
4892     }
4893     if (al == null || al.getHeight() == 0)
4894     {
4895       final String msg = MessageManager
4896               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
4897       final String title = MessageManager
4898               .getString("label.translation_failed");
4899       JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
4900               JOptionPane.WARNING_MESSAGE);
4901     }
4902     else
4903     {
4904       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4905       af.setFileFormat(this.currentFileFormat);
4906       final String newTitle = MessageManager.formatMessage(
4907               "label.translation_of_params", new Object[]
4908               { this.getTitle() });
4909       af.setTitle(newTitle);
4910       final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
4911       viewport.openSplitFrame(af, new Alignment(seqs), al.getCodonFrames());
4912       // Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4913     }
4914   }
4915
4916   /**
4917    * Set the file format
4918    * 
4919    * @param fileFormat
4920    */
4921   public void setFileFormat(String fileFormat)
4922   {
4923     this.currentFileFormat = fileFormat;
4924   }
4925
4926   /**
4927    * Try to load a features file onto the alignment.
4928    * 
4929    * @param file
4930    *          contents or path to retrieve file
4931    * @param type
4932    *          access mode of file (see jalview.io.AlignFile)
4933    * @return true if features file was parsed corectly.
4934    */
4935   public boolean parseFeaturesFile(String file, String type)
4936   {
4937     boolean featuresFile = false;
4938     try
4939     {
4940       featuresFile = new FeaturesFile(file, type).parse(viewport
4941               .getAlignment().getDataset(), alignPanel.getSeqPanel().seqCanvas
4942               .getFeatureRenderer().getFeatureColours(), false,
4943               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4944     } catch (Exception ex)
4945     {
4946       ex.printStackTrace();
4947     }
4948
4949     if (featuresFile)
4950     {
4951       viewport.setShowSequenceFeatures(true);
4952       showSeqFeatures.setSelected(true);
4953       if (alignPanel.getSeqPanel().seqCanvas.fr != null)
4954       {
4955         // update the min/max ranges where necessary
4956         alignPanel.getSeqPanel().seqCanvas.fr.findAllFeatures(true);
4957       }
4958       if (featureSettings != null)
4959       {
4960         featureSettings.setTableData();
4961       }
4962       alignPanel.paintAlignment(true);
4963     }
4964
4965     return featuresFile;
4966   }
4967
4968   @Override
4969   public void dragEnter(DropTargetDragEvent evt)
4970   {
4971   }
4972
4973   @Override
4974   public void dragExit(DropTargetEvent evt)
4975   {
4976   }
4977
4978   @Override
4979   public void dragOver(DropTargetDragEvent evt)
4980   {
4981   }
4982
4983   @Override
4984   public void dropActionChanged(DropTargetDragEvent evt)
4985   {
4986   }
4987
4988   @Override
4989   public void drop(DropTargetDropEvent evt)
4990   {
4991     Transferable t = evt.getTransferable();
4992     java.util.List files = null;
4993
4994     try
4995     {
4996       DataFlavor uriListFlavor = new DataFlavor(
4997               "text/uri-list;class=java.lang.String");
4998       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4999       {
5000         // Works on Windows and MacOSX
5001         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5002         files = (java.util.List) t
5003                 .getTransferData(DataFlavor.javaFileListFlavor);
5004       }
5005       else if (t.isDataFlavorSupported(uriListFlavor))
5006       {
5007         // This is used by Unix drag system
5008         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5009         String data = (String) t.getTransferData(uriListFlavor);
5010         files = new java.util.ArrayList(1);
5011         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
5012                 data, "\r\n"); st.hasMoreTokens();)
5013         {
5014           String s = st.nextToken();
5015           if (s.startsWith("#"))
5016           {
5017             // the line is a comment (as per the RFC 2483)
5018             continue;
5019           }
5020
5021           java.net.URI uri = new java.net.URI(s);
5022           // check to see if we can handle this kind of URI
5023           if (uri.getScheme().toLowerCase().startsWith("http"))
5024           {
5025             files.add(uri.toString());
5026           }
5027           else
5028           {
5029             // otherwise preserve old behaviour: catch all for file objects
5030             java.io.File file = new java.io.File(uri);
5031             files.add(file.toString());
5032           }
5033         }
5034       }
5035     } catch (Exception e)
5036     {
5037       e.printStackTrace();
5038     }
5039     if (files != null)
5040     {
5041       try
5042       {
5043         // check to see if any of these files have names matching sequences in
5044         // the alignment
5045         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5046                 .getAlignment().getSequencesArray());
5047         /**
5048          * Object[] { String,SequenceI}
5049          */
5050         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
5051         ArrayList<String> filesnotmatched = new ArrayList<String>();
5052         for (int i = 0; i < files.size(); i++)
5053         {
5054           String file = files.get(i).toString();
5055           String pdbfn = "";
5056           String protocol = FormatAdapter.checkProtocol(file);
5057           if (protocol == jalview.io.FormatAdapter.FILE)
5058           {
5059             File fl = new File(file);
5060             pdbfn = fl.getName();
5061           }
5062           else if (protocol == jalview.io.FormatAdapter.URL)
5063           {
5064             URL url = new URL(file);
5065             pdbfn = url.getFile();
5066           }
5067           if (pdbfn.length() > 0)
5068           {
5069             // attempt to find a match in the alignment
5070             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
5071             int l = 0, c = pdbfn.indexOf(".");
5072             while (mtch == null && c != -1)
5073             {
5074               do
5075               {
5076                 l = c;
5077               } while ((c = pdbfn.indexOf(".", l)) > l);
5078               if (l > -1)
5079               {
5080                 pdbfn = pdbfn.substring(0, l);
5081               }
5082               mtch = idm.findAllIdMatches(pdbfn);
5083             }
5084             if (mtch != null)
5085             {
5086               String type = null;
5087               try
5088               {
5089                 type = new IdentifyFile().Identify(file, protocol);
5090               } catch (Exception ex)
5091               {
5092                 type = null;
5093               }
5094               if (type != null)
5095               {
5096                 if (type.equalsIgnoreCase("PDB"))
5097                 {
5098                   filesmatched.add(new Object[]
5099                   { file, protocol, mtch });
5100                   continue;
5101                 }
5102               }
5103             }
5104             // File wasn't named like one of the sequences or wasn't a PDB file.
5105             filesnotmatched.add(file);
5106           }
5107         }
5108         int assocfiles = 0;
5109         if (filesmatched.size() > 0)
5110         {
5111           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
5112                   || JOptionPane
5113                           .showConfirmDialog(
5114                                   this,
5115                                   MessageManager
5116                                           .formatMessage(
5117                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
5118                                                   new Object[]
5119                                                   { Integer.valueOf(
5120                                                           filesmatched
5121                                                                   .size())
5122                                                           .toString() }),
5123                                   MessageManager
5124                                           .getString("label.automatically_associate_pdb_files_by_name"),
5125                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
5126
5127           {
5128             for (Object[] fm : filesmatched)
5129             {
5130               // try and associate
5131               // TODO: may want to set a standard ID naming formalism for
5132               // associating PDB files which have no IDs.
5133               for (SequenceI toassoc : (SequenceI[]) fm[2])
5134               {
5135                 PDBEntry pe = new AssociatePdbFileWithSeq()
5136                         .associatePdbWithSeq((String) fm[0],
5137                                 (String) fm[1], toassoc, false,
5138                                 Desktop.instance);
5139                 if (pe != null)
5140                 {
5141                   System.err.println("Associated file : "
5142                           + ((String) fm[0]) + " with "
5143                           + toassoc.getDisplayId(true));
5144                   assocfiles++;
5145                 }
5146               }
5147               alignPanel.paintAlignment(true);
5148             }
5149           }
5150         }
5151         if (filesnotmatched.size() > 0)
5152         {
5153           if (assocfiles > 0
5154                   && (Cache.getDefault(
5155                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
5156                           .showConfirmDialog(
5157                                   this,
5158                                   "<html>"+MessageManager
5159                                           .formatMessage(
5160                                                   "label.ignore_unmatched_dropped_files_info",
5161                                                   new Object[]
5162                                                   { Integer.valueOf(
5163                                                           filesnotmatched
5164                                                                   .size())
5165                                                           .toString() })+"</html>",
5166                                   MessageManager
5167                                           .getString("label.ignore_unmatched_dropped_files"),
5168                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
5169           {
5170             return;
5171           }
5172           for (String fn : filesnotmatched)
5173           {
5174             loadJalviewDataFile(fn, null, null, null);
5175           }
5176
5177         }
5178       } catch (Exception ex)
5179       {
5180         ex.printStackTrace();
5181       }
5182     }
5183   }
5184
5185   /**
5186    * Attempt to load a "dropped" file or URL string: First by testing whether
5187    * it's and Annotation file, then a JNet file, and finally a features file. If
5188    * all are false then the user may have dropped an alignment file onto this
5189    * AlignFrame.
5190    * 
5191    * @param file
5192    *          either a filename or a URL string.
5193    */
5194   public void loadJalviewDataFile(String file, String protocol,
5195           String format, SequenceI assocSeq)
5196   {
5197     try
5198     {
5199       if (protocol == null)
5200       {
5201         protocol = jalview.io.FormatAdapter.checkProtocol(file);
5202       }
5203       // if the file isn't identified, or not positively identified as some
5204       // other filetype (PFAM is default unidentified alignment file type) then
5205       // try to parse as annotation.
5206       boolean isAnnotation = (format == null || format
5207               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
5208               .annotateAlignmentView(viewport, file, protocol)
5209               : false;
5210
5211       if (!isAnnotation)
5212       {
5213         // first see if its a T-COFFEE score file
5214         TCoffeeScoreFile tcf = null;
5215         try
5216         {
5217           tcf = new TCoffeeScoreFile(file, protocol);
5218           if (tcf.isValid())
5219           {
5220             if (tcf.annotateAlignment(viewport.getAlignment(), true))
5221             {
5222               tcoffeeColour.setEnabled(true);
5223               tcoffeeColour.setSelected(true);
5224               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
5225               isAnnotation = true;
5226               statusBar
5227                       .setText(MessageManager
5228                               .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
5229             }
5230             else
5231             {
5232               // some problem - if no warning its probable that the ID matching
5233               // process didn't work
5234               JOptionPane
5235                       .showMessageDialog(
5236                               Desktop.desktop,
5237                               tcf.getWarningMessage() == null ? MessageManager
5238                                       .getString("label.check_file_matches_sequence_ids_alignment")
5239                                       : tcf.getWarningMessage(),
5240                               MessageManager
5241                                       .getString("label.problem_reading_tcoffee_score_file"),
5242                               JOptionPane.WARNING_MESSAGE);
5243             }
5244           }
5245           else
5246           {
5247             tcf = null;
5248           }
5249         } catch (Exception x)
5250         {
5251           Cache.log
5252                   .debug("Exception when processing data source as T-COFFEE score file",
5253                           x);
5254           tcf = null;
5255         }
5256         if (tcf == null)
5257         {
5258           // try to see if its a JNet 'concise' style annotation file *before*
5259           // we
5260           // try to parse it as a features file
5261           if (format == null)
5262           {
5263             format = new IdentifyFile().Identify(file, protocol);
5264           }
5265           if (format.equalsIgnoreCase("JnetFile"))
5266           {
5267             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
5268                     file, protocol);
5269             new JnetAnnotationMaker();
5270             JnetAnnotationMaker.add_annotation(predictions,
5271                     viewport.getAlignment(), 0, false);
5272             SequenceI repseq = viewport.getAlignment().getSequenceAt(0);
5273             viewport.getAlignment().setSeqrep(repseq);
5274             ColumnSelection cs = new ColumnSelection();
5275             cs.hideInsertionsFor(repseq);
5276             viewport.setColumnSelection(cs);
5277             isAnnotation = true;
5278           }
5279           else
5280           {
5281             /*
5282              * if (format.equalsIgnoreCase("PDB")) {
5283              * 
5284              * String pdbfn = ""; // try to match up filename with sequence id
5285              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
5286              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
5287              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
5288              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
5289              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5290              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
5291              * // attempt to find a match in the alignment SequenceI mtch =
5292              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
5293              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
5294              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
5295              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
5296              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
5297              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
5298              * { System.err.println("Associated file : " + file + " with " +
5299              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
5300              * TODO: maybe need to load as normal otherwise return; } }
5301              */
5302             // try to parse it as a features file
5303             boolean isGroupsFile = parseFeaturesFile(file, protocol);
5304             // if it wasn't a features file then we just treat it as a general
5305             // alignment file to load into the current view.
5306             if (!isGroupsFile)
5307             {
5308               new FileLoader().LoadFile(viewport, file, protocol, format);
5309             }
5310             else
5311             {
5312               alignPanel.paintAlignment(true);
5313             }
5314           }
5315         }
5316       }
5317       if (isAnnotation)
5318       {
5319
5320         alignPanel.adjustAnnotationHeight();
5321         viewport.updateSequenceIdColours();
5322         buildSortByAnnotationScoresMenu();
5323         alignPanel.paintAlignment(true);
5324       }
5325     } catch (Exception ex)
5326     {
5327       ex.printStackTrace();
5328     } catch (OutOfMemoryError oom)
5329     {
5330       try
5331       {
5332         System.gc();
5333       } catch (Exception x)
5334       {
5335       }
5336       ;
5337       new OOMWarning(
5338               "loading data "
5339                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5340                               : "using " + protocol + " from " + file)
5341                               : ".")
5342                       + (format != null ? "(parsing as '" + format
5343                               + "' file)" : ""), oom, Desktop.desktop);
5344     }
5345   }
5346
5347   /**
5348    * Method invoked by the ChangeListener on the tabbed pane, in other words
5349    * when a different tabbed pane is selected by the user or programmatically.
5350    */
5351   @Override
5352   public void tabSelectionChanged(int index)
5353   {
5354     if (index > -1)
5355     {
5356       alignPanel = alignPanels.get(index);
5357       viewport = alignPanel.av;
5358       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
5359       setMenusFromViewport(viewport);
5360     }
5361
5362     /*
5363      * If there is a frame linked to this one in a SplitPane, switch it to the
5364      * same view tab index. No infinite recursion of calls should happen, since
5365      * tabSelectionChanged() should not get invoked on setting the selected
5366      * index to an unchanged value. Guard against setting an invalid index
5367      * before the new view peer tab has been created.
5368      */
5369     final AlignViewportI peer = viewport.getCodingComplement();
5370     if (peer != null)
5371     {
5372       AlignFrame linkedAlignFrame = ((AlignViewport) peer).getAlignPanel().alignFrame;
5373       if (linkedAlignFrame.tabbedPane.getTabCount() > index)
5374       {
5375         linkedAlignFrame.tabbedPane.setSelectedIndex(index);
5376       }
5377     }
5378   }
5379
5380   /**
5381    * On right mouse click on view tab, prompt for and set new view name.
5382    */
5383   @Override
5384   public void tabbedPane_mousePressed(MouseEvent e)
5385   {
5386     if (SwingUtilities.isRightMouseButton(e))
5387     {
5388       String msg = MessageManager.getString("label.enter_view_name");
5389       String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
5390               JOptionPane.QUESTION_MESSAGE);
5391
5392       if (reply != null)
5393       {
5394         viewport.viewName = reply;
5395         // TODO warn if reply is in getExistingViewNames()?
5396         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5397       }
5398     }
5399   }
5400
5401   public AlignViewport getCurrentView()
5402   {
5403     return viewport;
5404   }
5405
5406   /**
5407    * Open the dialog for regex description parsing.
5408    */
5409   @Override
5410   protected void extractScores_actionPerformed(ActionEvent e)
5411   {
5412     ParseProperties pp = new jalview.analysis.ParseProperties(
5413             viewport.getAlignment());
5414     // TODO: verify regex and introduce GUI dialog for version 2.5
5415     // if (pp.getScoresFromDescription("col", "score column ",
5416     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5417     // true)>0)
5418     if (pp.getScoresFromDescription("description column",
5419             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5420     {
5421       buildSortByAnnotationScoresMenu();
5422     }
5423   }
5424
5425   /*
5426    * (non-Javadoc)
5427    * 
5428    * @see
5429    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5430    * )
5431    */
5432   @Override
5433   protected void showDbRefs_actionPerformed(ActionEvent e)
5434   {
5435     viewport.setShowDBRefs(showDbRefsMenuitem.isSelected());
5436   }
5437
5438   /*
5439    * (non-Javadoc)
5440    * 
5441    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5442    * ActionEvent)
5443    */
5444   @Override
5445   protected void showNpFeats_actionPerformed(ActionEvent e)
5446   {
5447     viewport.setShowNPFeats(showNpFeatsMenuitem.isSelected());
5448   }
5449
5450   /**
5451    * find the viewport amongst the tabs in this alignment frame and close that
5452    * tab
5453    * 
5454    * @param av
5455    */
5456   public boolean closeView(AlignViewportI av)
5457   {
5458     if (viewport == av)
5459     {
5460       this.closeMenuItem_actionPerformed(false);
5461       return true;
5462     }
5463     Component[] comp = tabbedPane.getComponents();
5464     for (int i = 0; comp != null && i < comp.length; i++)
5465     {
5466       if (comp[i] instanceof AlignmentPanel)
5467       {
5468         if (((AlignmentPanel) comp[i]).av == av)
5469         {
5470           // close the view.
5471           closeView((AlignmentPanel) comp[i]);
5472           return true;
5473         }
5474       }
5475     }
5476     return false;
5477   }
5478
5479   protected void build_fetchdbmenu(JMenu webService)
5480   {
5481     // Temporary hack - DBRef Fetcher always top level ws entry.
5482     // TODO We probably want to store a sequence database checklist in
5483     // preferences and have checkboxes.. rather than individual sources selected
5484     // here
5485     final JMenu rfetch = new JMenu(
5486             MessageManager.getString("action.fetch_db_references"));
5487     rfetch.setToolTipText(MessageManager
5488             .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
5489     webService.add(rfetch);
5490
5491     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
5492             MessageManager.getString("option.trim_retrieved_seqs"));
5493     trimrs.setToolTipText(MessageManager
5494             .getString("label.trim_retrieved_sequences"));
5495     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
5496     trimrs.addActionListener(new ActionListener()
5497     {
5498       @Override
5499       public void actionPerformed(ActionEvent e)
5500       {
5501         trimrs.setSelected(trimrs.isSelected());
5502         Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
5503                 Boolean.valueOf(trimrs.isSelected()).toString());
5504       };
5505     });
5506     rfetch.add(trimrs);
5507     JMenuItem fetchr = new JMenuItem(
5508             MessageManager.getString("label.standard_databases"));
5509     fetchr.setToolTipText(MessageManager
5510             .getString("label.fetch_embl_uniprot"));
5511     fetchr.addActionListener(new ActionListener()
5512     {
5513
5514       @Override
5515       public void actionPerformed(ActionEvent e)
5516       {
5517         new Thread(new Runnable()
5518         {
5519
5520           @Override
5521           public void run()
5522           {
5523             new jalview.ws.DBRefFetcher(alignPanel.av
5524                     .getSequenceSelection(), alignPanel.alignFrame)
5525                     .fetchDBRefs(false);
5526           }
5527         }).start();
5528
5529       }
5530
5531     });
5532     rfetch.add(fetchr);
5533     final AlignFrame me = this;
5534     new Thread(new Runnable()
5535     {
5536       @Override
5537       public void run()
5538       {
5539         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5540                 .getSequenceFetcherSingleton(me);
5541         javax.swing.SwingUtilities.invokeLater(new Runnable()
5542         {
5543           @Override
5544           public void run()
5545           {
5546             String[] dbclasses = sf.getOrderedSupportedSources();
5547             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5548             // jalview.util.QuickSort.sort(otherdb, otherdb);
5549             List<DbSourceProxy> otherdb;
5550             JMenu dfetch = new JMenu();
5551             JMenu ifetch = new JMenu();
5552             JMenuItem fetchr = null;
5553             int comp = 0, icomp = 0, mcomp = 15;
5554             String mname = null;
5555             int dbi = 0;
5556             for (String dbclass : dbclasses)
5557             {
5558               otherdb = sf.getSourceProxy(dbclass);
5559               // add a single entry for this class, or submenu allowing 'fetch
5560               // all' or pick one
5561               if (otherdb == null || otherdb.size() < 1)
5562               {
5563                 continue;
5564               }
5565               // List<DbSourceProxy> dbs=otherdb;
5566               // otherdb=new ArrayList<DbSourceProxy>();
5567               // for (DbSourceProxy db:dbs)
5568               // {
5569               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5570               // }
5571               if (mname == null)
5572               {
5573                 mname = "From " + dbclass;
5574               }
5575               if (otherdb.size() == 1)
5576               {
5577                 final DbSourceProxy[] dassource = otherdb
5578                         .toArray(new DbSourceProxy[0]);
5579                 DbSourceProxy src = otherdb.get(0);
5580                 fetchr = new JMenuItem(src.getDbSource());
5581                 fetchr.addActionListener(new ActionListener()
5582                 {
5583
5584                   @Override
5585                   public void actionPerformed(ActionEvent e)
5586                   {
5587                     new Thread(new Runnable()
5588                     {
5589
5590                       @Override
5591                       public void run()
5592                       {
5593                         new jalview.ws.DBRefFetcher(alignPanel.av
5594                                 .getSequenceSelection(),
5595                                 alignPanel.alignFrame, dassource)
5596                                 .fetchDBRefs(false);
5597                       }
5598                     }).start();
5599                   }
5600
5601                 });
5602                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{src.getDbName()})));
5603                 dfetch.add(fetchr);
5604                 comp++;
5605               }
5606               else
5607               {
5608                 final DbSourceProxy[] dassource = otherdb
5609                         .toArray(new DbSourceProxy[0]);
5610                 // fetch all entry
5611                 DbSourceProxy src = otherdb.get(0);
5612                 fetchr = new JMenuItem(MessageManager.formatMessage(
5613                         "label.fetch_all_param", new Object[]
5614                         { src.getDbSource() }));
5615                 fetchr.addActionListener(new ActionListener()
5616                 {
5617                   @Override
5618                   public void actionPerformed(ActionEvent e)
5619                   {
5620                     new Thread(new Runnable()
5621                     {
5622
5623                       @Override
5624                       public void run()
5625                       {
5626                         new jalview.ws.DBRefFetcher(alignPanel.av
5627                                 .getSequenceSelection(),
5628                                 alignPanel.alignFrame, dassource)
5629                                 .fetchDBRefs(false);
5630                       }
5631                     }).start();
5632                   }
5633                 });
5634
5635                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new Object[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
5636                 dfetch.add(fetchr);
5637                 comp++;
5638                 // and then build the rest of the individual menus
5639                 ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new Object[]{src.getDbSource()}));
5640                 icomp = 0;
5641                 String imname = null;
5642                 int i = 0;
5643                 for (DbSourceProxy sproxy : otherdb)
5644                 {
5645                   String dbname = sproxy.getDbName();
5646                   String sname = dbname.length() > 5 ? dbname.substring(0,
5647                           5) + "..." : dbname;
5648                   String msname = dbname.length() > 10 ? dbname.substring(
5649                           0, 10) + "..." : dbname;
5650                   if (imname == null)
5651                   {
5652                     imname = MessageManager.formatMessage("label.from_msname", new Object[]{sname});
5653                   }
5654                   fetchr = new JMenuItem(msname);
5655                   final DbSourceProxy[] dassrc =
5656                   { sproxy };
5657                   fetchr.addActionListener(new ActionListener()
5658                   {
5659
5660                     @Override
5661                     public void actionPerformed(ActionEvent e)
5662                     {
5663                       new Thread(new Runnable()
5664                       {
5665
5666                         @Override
5667                         public void run()
5668                         {
5669                           new jalview.ws.DBRefFetcher(alignPanel.av
5670                                   .getSequenceSelection(),
5671                                   alignPanel.alignFrame, dassrc)
5672                                   .fetchDBRefs(false);
5673                         }
5674                       }).start();
5675                     }
5676
5677                   });
5678                   fetchr.setToolTipText("<html>"
5679                           + MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{dbname}));
5680                   ifetch.add(fetchr);
5681                   ++i;
5682                   if (++icomp >= mcomp || i == (otherdb.size()))
5683                   {
5684                     ifetch.setText(MessageManager.formatMessage(
5685                             "label.source_to_target", imname, sname));
5686                     dfetch.add(ifetch);
5687                     ifetch = new JMenu();
5688                     imname = null;
5689                     icomp = 0;
5690                     comp++;
5691                   }
5692                 }
5693               }
5694               ++dbi;
5695               if (comp >= mcomp || dbi >= (dbclasses.length))
5696               {
5697                 dfetch.setText(MessageManager.formatMessage(
5698                         "label.source_to_target", mname, dbclass));
5699                 rfetch.add(dfetch);
5700                 dfetch = new JMenu();
5701                 mname = null;
5702                 comp = 0;
5703               }
5704             }
5705           }
5706         });
5707       }
5708     }).start();
5709
5710   }
5711
5712   /**
5713    * Left justify the whole alignment.
5714    */
5715   @Override
5716   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5717   {
5718     AlignmentI al = viewport.getAlignment();
5719     al.justify(false);
5720     viewport.firePropertyChange("alignment", null, al);
5721   }
5722
5723   /**
5724    * Right justify the whole alignment.
5725    */
5726   @Override
5727   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5728   {
5729     AlignmentI al = viewport.getAlignment();
5730     al.justify(true);
5731     viewport.firePropertyChange("alignment", null, al);
5732   }
5733
5734   public void setShowSeqFeatures(boolean b)
5735   {
5736     showSeqFeatures.setSelected(true);
5737     viewport.setShowSequenceFeatures(true);
5738   }
5739
5740   /*
5741    * (non-Javadoc)
5742    * 
5743    * @see
5744    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5745    * awt.event.ActionEvent)
5746    */
5747   @Override
5748   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5749   {
5750     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5751     alignPanel.paintAlignment(true);
5752   }
5753
5754   /*
5755    * (non-Javadoc)
5756    * 
5757    * @see
5758    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5759    * .ActionEvent)
5760    */
5761   @Override
5762   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5763   {
5764     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5765     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5766
5767   }
5768
5769   /*
5770    * (non-Javadoc)
5771    * 
5772    * @see
5773    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5774    * .event.ActionEvent)
5775    */
5776   @Override
5777   protected void showGroupConservation_actionPerformed(ActionEvent e)
5778   {
5779     viewport.setShowGroupConservation(showGroupConservation.getState());
5780     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5781   }
5782
5783   /*
5784    * (non-Javadoc)
5785    * 
5786    * @see
5787    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5788    * .event.ActionEvent)
5789    */
5790   @Override
5791   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5792   {
5793     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5794     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5795   }
5796
5797   /*
5798    * (non-Javadoc)
5799    * 
5800    * @see
5801    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5802    * .event.ActionEvent)
5803    */
5804   @Override
5805   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5806   {
5807     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5808     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5809   }
5810
5811   @Override
5812   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5813   {
5814     showSequenceLogo.setState(true);
5815     viewport.setShowSequenceLogo(true);
5816     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5817     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5818   }
5819
5820   @Override
5821   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5822   {
5823     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5824   }
5825
5826   /*
5827    * (non-Javadoc)
5828    * 
5829    * @see
5830    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5831    * .event.ActionEvent)
5832    */
5833   @Override
5834   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5835   {
5836     if (avc.makeGroupsFromSelection())
5837     {
5838       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5839       alignPanel.updateAnnotation();
5840       alignPanel.paintAlignment(true);
5841     }
5842   }
5843   public void clearAlignmentSeqRep()
5844   {
5845     // TODO refactor alignmentseqrep to controller
5846     if (viewport.getAlignment().hasSeqrep()) {
5847       viewport.getAlignment().setSeqrep(null);
5848       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5849       alignPanel.updateAnnotation();
5850       alignPanel.paintAlignment(true);
5851     }
5852   }
5853
5854   @Override
5855   protected void createGroup_actionPerformed(ActionEvent e)
5856   {
5857     if (avc.createGroup())
5858     {
5859       alignPanel.alignmentChanged();
5860     }
5861   }
5862
5863   @Override
5864   protected void unGroup_actionPerformed(ActionEvent e)
5865   {
5866     if (avc.unGroup())
5867     {
5868       alignPanel.alignmentChanged();
5869     }
5870   }
5871
5872   /**
5873    * make the given alignmentPanel the currently selected tab
5874    * 
5875    * @param alignmentPanel
5876    */
5877   public void setDisplayedView(AlignmentPanel alignmentPanel)
5878   {
5879     if (!viewport.getSequenceSetId().equals(
5880             alignmentPanel.av.getSequenceSetId()))
5881     {
5882       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
5883     }
5884     if (tabbedPane != null
5885             && tabbedPane.getTabCount() > 0
5886             && alignPanels.indexOf(alignmentPanel) != tabbedPane
5887                     .getSelectedIndex())
5888     {
5889       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5890     }
5891   }
5892
5893   /**
5894    * Action on selection of menu options to Show or Hide annotations.
5895    * 
5896    * @param visible
5897    * @param forSequences
5898    *          update sequence-related annotations
5899    * @param forAlignment
5900    *          update non-sequence-related annotations
5901    */
5902   @Override
5903   protected void setAnnotationsVisibility(boolean visible,
5904           boolean forSequences, boolean forAlignment)
5905   {
5906     for (AlignmentAnnotation aa : alignPanel.getAlignment()
5907             .getAlignmentAnnotation())
5908     {
5909       boolean apply = (aa.sequenceRef == null && forAlignment)
5910               || (aa.sequenceRef != null && forSequences);
5911       if (apply)
5912       {
5913         aa.visible = visible;
5914       }
5915     }
5916     alignPanel.validateAnnotationDimensions(false);
5917     alignPanel.alignmentChanged();
5918   }
5919
5920   /**
5921    * Store selected annotation sort order for the view and repaint.
5922    */
5923   @Override
5924   protected void sortAnnotations_actionPerformed()
5925   {
5926     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
5927     this.alignPanel.av
5928             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
5929     alignPanel.paintAlignment(true);
5930   }
5931
5932   /**
5933    * 
5934    * @return alignment panels in this alignment frame
5935    */
5936   public List<? extends AlignmentViewPanel> getAlignPanels()
5937   {
5938     return alignPanels == null ? Arrays.asList(alignPanel)
5939             : alignPanels;
5940   }
5941
5942   /**
5943    * Open a new alignment window, with the cDNA associated with this (protein)
5944    * alignment, aligned as is the protein.
5945    */
5946   protected void viewAsCdna_actionPerformed()
5947   {
5948     // TODO no longer a menu action - refactor as required
5949     final AlignmentI alignment = getViewport().getAlignment();
5950     Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
5951     if (mappings == null)
5952     {
5953       return;
5954     }
5955     List<SequenceI> cdnaSeqs = new ArrayList<SequenceI>();
5956     for (SequenceI aaSeq : alignment.getSequences()) {
5957       for (AlignedCodonFrame acf : mappings) {
5958         SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence());
5959         if (dnaSeq != null)
5960         {
5961           /*
5962            * There is a cDNA mapping for this protein sequence - add to new
5963            * alignment. It will share the same dataset sequence as other mapped
5964            * cDNA (no new mappings need to be created).
5965            */
5966           final Sequence newSeq = new Sequence(dnaSeq);
5967           newSeq.setDatasetSequence(dnaSeq);
5968           cdnaSeqs.add(newSeq);
5969         }
5970       }
5971     }
5972     if (cdnaSeqs.size() == 0)
5973     {
5974       // show a warning dialog no mapped cDNA
5975       return;
5976     }
5977     AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs
5978             .size()]));
5979     AlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
5980             AlignFrame.DEFAULT_HEIGHT);
5981     cdna.alignAs(alignment);
5982     String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
5983             + this.title;
5984     Desktop.addInternalFrame(alignFrame, newtitle,
5985             AlignFrame.DEFAULT_WIDTH,
5986             AlignFrame.DEFAULT_HEIGHT);
5987   }
5988
5989   /**
5990    * Set visibility of dna/protein complement view (available when shown in a
5991    * split frame).
5992    * 
5993    * @param show
5994    */
5995   @Override
5996   protected void showComplement_actionPerformed(boolean show)
5997   {
5998     SplitContainerI sf = getSplitViewContainer();
5999     if (sf != null) {
6000       sf.setComplementVisible(this, show);
6001     }
6002   }
6003 }
6004
6005 class PrintThread extends Thread
6006 {
6007   AlignmentPanel ap;
6008
6009   public PrintThread(AlignmentPanel ap)
6010   {
6011     this.ap = ap;
6012   }
6013
6014   static PageFormat pf;
6015
6016   @Override
6017   public void run()
6018   {
6019     PrinterJob printJob = PrinterJob.getPrinterJob();
6020
6021     if (pf != null)
6022     {
6023       printJob.setPrintable(ap, pf);
6024     }
6025     else
6026     {
6027       printJob.setPrintable(ap);
6028     }
6029
6030     if (printJob.printDialog())
6031     {
6032       try
6033       {
6034         printJob.print();
6035       } catch (Exception PrintException)
6036       {
6037         PrintException.printStackTrace();
6038       }
6039     }
6040   }
6041 }