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