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