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