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