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