8eee942e88e1f2fa656e940a784871caf12ff971
[jalview.git] / src / jalview / gui / AlignFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
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.isShowSequenceFeatures());
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   }
1315
1316   @Override
1317   public void associatedData_actionPerformed(ActionEvent e)
1318   {
1319     // Pick the tree file
1320     JalviewFileChooser chooser = new JalviewFileChooser(
1321             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1322     chooser.setFileView(new JalviewFileView());
1323     chooser.setDialogTitle(MessageManager
1324             .getString("label.load_jalview_annotations"));
1325     chooser.setToolTipText(MessageManager
1326             .getString("label.load_jalview_annotations"));
1327
1328     int value = chooser.showOpenDialog(null);
1329
1330     if (value == JalviewFileChooser.APPROVE_OPTION)
1331     {
1332       String choice = chooser.getSelectedFile().getPath();
1333       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
1334       loadJalviewDataFile(choice, null, null, null);
1335     }
1336
1337   }
1338
1339   /**
1340    * Close the current view or all views in the alignment frame. If the frame
1341    * only contains one view then the alignment will be removed from memory.
1342    * 
1343    * @param closeAllTabs
1344    */
1345   @Override
1346   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1347   {
1348     if (alignPanels != null && alignPanels.size() < 2)
1349     {
1350       closeAllTabs = true;
1351     }
1352
1353     try
1354     {
1355       if (alignPanels != null)
1356       {
1357         if (closeAllTabs)
1358         {
1359           if (this.isClosed())
1360           {
1361             // really close all the windows - otherwise wait till
1362             // setClosed(true) is called
1363             for (int i = 0; i < alignPanels.size(); i++)
1364             {
1365               AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
1366               ap.closePanel();
1367             }
1368           }
1369         }
1370         else
1371         {
1372           closeView(alignPanel);
1373         }
1374       }
1375
1376       if (closeAllTabs)
1377       {
1378         this.setClosed(true);
1379       }
1380     } catch (Exception ex)
1381     {
1382       ex.printStackTrace();
1383     }
1384   }
1385
1386   /**
1387    * close alignPanel2 and shuffle tabs appropriately.
1388    * 
1389    * @param alignPanel2
1390    */
1391   public void closeView(AlignmentPanel alignPanel2)
1392   {
1393     int index = tabbedPane.getSelectedIndex();
1394     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
1395     alignPanels.removeElement(alignPanel2);
1396     // Unnecessary
1397     // if (viewport == alignPanel2.av)
1398     // {
1399     // viewport = null;
1400     // }
1401     alignPanel2.closePanel();
1402     alignPanel2 = null;
1403
1404     tabbedPane.removeTabAt(closedindex);
1405     tabbedPane.validate();
1406
1407     if (index > closedindex || index == tabbedPane.getTabCount())
1408     {
1409       // modify currently selected tab index if necessary.
1410       index--;
1411     }
1412
1413     this.tabSelectionChanged(index);
1414   }
1415
1416   /**
1417    * DOCUMENT ME!
1418    */
1419   void updateEditMenuBar()
1420   {
1421
1422     if (viewport.historyList.size() > 0)
1423     {
1424       undoMenuItem.setEnabled(true);
1425       CommandI command = viewport.historyList.peek();
1426       undoMenuItem.setText(MessageManager.formatMessage(
1427               "label.undo_command", new String[]
1428               { command.getDescription() }));
1429     }
1430     else
1431     {
1432       undoMenuItem.setEnabled(false);
1433       undoMenuItem.setText(MessageManager.getString("action.undo"));
1434     }
1435
1436     if (viewport.redoList.size() > 0)
1437     {
1438       redoMenuItem.setEnabled(true);
1439
1440       CommandI command = viewport.redoList.peek();
1441       redoMenuItem.setText(MessageManager.formatMessage(
1442               "label.redo_command", new String[]
1443               { command.getDescription() }));
1444     }
1445     else
1446     {
1447       redoMenuItem.setEnabled(false);
1448       redoMenuItem.setText(MessageManager.getString("action.redo"));
1449     }
1450   }
1451
1452   public void addHistoryItem(CommandI command)
1453   {
1454     if (command.getSize() > 0)
1455     {
1456       viewport.historyList.push(command);
1457       viewport.redoList.clear();
1458       updateEditMenuBar();
1459       viewport.updateHiddenColumns();
1460       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
1461       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1462       // viewport.getColumnSelection()
1463       // .getHiddenColumns().size() > 0);
1464     }
1465   }
1466
1467   /**
1468    * 
1469    * @return alignment objects for all views
1470    */
1471   AlignmentI[] getViewAlignments()
1472   {
1473     if (alignPanels != null)
1474     {
1475       Enumeration e = alignPanels.elements();
1476       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1477       for (int i = 0; e.hasMoreElements(); i++)
1478       {
1479         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1480       }
1481       return als;
1482     }
1483     if (viewport != null)
1484     {
1485       return new AlignmentI[]
1486       { viewport.getAlignment() };
1487     }
1488     return null;
1489   }
1490
1491   /**
1492    * DOCUMENT ME!
1493    * 
1494    * @param e
1495    *          DOCUMENT ME!
1496    */
1497   @Override
1498   protected void undoMenuItem_actionPerformed(ActionEvent e)
1499   {
1500     if (viewport.historyList.empty())
1501     {
1502       return;
1503     }
1504     CommandI command = viewport.historyList.pop();
1505     viewport.redoList.push(command);
1506     command.undoCommand(getViewAlignments());
1507
1508     AlignViewport originalSource = getOriginatingSource(command);
1509     updateEditMenuBar();
1510
1511     if (originalSource != null)
1512     {
1513       if (originalSource != viewport)
1514       {
1515         Cache.log
1516                 .warn("Implementation worry: mismatch of viewport origin for undo");
1517       }
1518       originalSource.updateHiddenColumns();
1519       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1520       // null
1521       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1522       // viewport.getColumnSelection()
1523       // .getHiddenColumns().size() > 0);
1524       originalSource.firePropertyChange("alignment", null, originalSource
1525               .getAlignment().getSequences());
1526     }
1527   }
1528
1529   /**
1530    * DOCUMENT ME!
1531    * 
1532    * @param e
1533    *          DOCUMENT ME!
1534    */
1535   @Override
1536   protected void redoMenuItem_actionPerformed(ActionEvent e)
1537   {
1538     if (viewport.redoList.size() < 1)
1539     {
1540       return;
1541     }
1542
1543     CommandI command = viewport.redoList.pop();
1544     viewport.historyList.push(command);
1545     command.doCommand(getViewAlignments());
1546
1547     AlignViewport originalSource = getOriginatingSource(command);
1548     updateEditMenuBar();
1549
1550     if (originalSource != null)
1551     {
1552
1553       if (originalSource != viewport)
1554       {
1555         Cache.log
1556                 .warn("Implementation worry: mismatch of viewport origin for redo");
1557       }
1558       originalSource.updateHiddenColumns();
1559       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1560       // null
1561       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1562       // viewport.getColumnSelection()
1563       // .getHiddenColumns().size() > 0);
1564       originalSource.firePropertyChange("alignment", null, originalSource
1565               .getAlignment().getSequences());
1566     }
1567   }
1568
1569   AlignViewport getOriginatingSource(CommandI command)
1570   {
1571     AlignViewport originalSource = null;
1572     // For sequence removal and addition, we need to fire
1573     // the property change event FROM the viewport where the
1574     // original alignment was altered
1575     AlignmentI al = null;
1576     if (command instanceof EditCommand)
1577     {
1578       EditCommand editCommand = (EditCommand) command;
1579       al = editCommand.getAlignment();
1580       Vector comps = (Vector) PaintRefresher.components.get(viewport
1581               .getSequenceSetId());
1582
1583       for (int i = 0; i < comps.size(); i++)
1584       {
1585         if (comps.elementAt(i) instanceof AlignmentPanel)
1586         {
1587           if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
1588           {
1589             originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
1590             break;
1591           }
1592         }
1593       }
1594     }
1595
1596     if (originalSource == null)
1597     {
1598       // The original view is closed, we must validate
1599       // the current view against the closed view first
1600       if (al != null)
1601       {
1602         PaintRefresher.validateSequences(al, viewport.getAlignment());
1603       }
1604
1605       originalSource = viewport;
1606     }
1607
1608     return originalSource;
1609   }
1610
1611   /**
1612    * DOCUMENT ME!
1613    * 
1614    * @param up
1615    *          DOCUMENT ME!
1616    */
1617   public void moveSelectedSequences(boolean up)
1618   {
1619     SequenceGroup sg = viewport.getSelectionGroup();
1620
1621     if (sg == null)
1622     {
1623       return;
1624     }
1625     viewport.getAlignment().moveSelectedSequencesByOne(sg,
1626             viewport.getHiddenRepSequences(), up);
1627     alignPanel.paintAlignment(true);
1628   }
1629
1630   synchronized void slideSequences(boolean right, int size)
1631   {
1632     List<SequenceI> sg = new Vector();
1633     if (viewport.cursorMode)
1634     {
1635       sg.add(viewport.getAlignment().getSequenceAt(
1636               alignPanel.getSeqPanel().seqCanvas.cursorY));
1637     }
1638     else if (viewport.getSelectionGroup() != null
1639             && viewport.getSelectionGroup().getSize() != viewport
1640                     .getAlignment().getHeight())
1641     {
1642       sg = viewport.getSelectionGroup().getSequences(
1643               viewport.getHiddenRepSequences());
1644     }
1645
1646     if (sg.size() < 1)
1647     {
1648       return;
1649     }
1650
1651     Vector invertGroup = new Vector();
1652
1653     for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
1654     {
1655       if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
1656       {
1657         invertGroup.add(viewport.getAlignment().getSequenceAt(i));
1658       }
1659     }
1660
1661     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
1662
1663     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1664     for (int i = 0; i < invertGroup.size(); i++)
1665     {
1666       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1667     }
1668
1669     SlideSequencesCommand ssc;
1670     if (right)
1671     {
1672       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
1673               size, viewport.getGapCharacter());
1674     }
1675     else
1676     {
1677       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
1678               size, viewport.getGapCharacter());
1679     }
1680
1681     int groupAdjustment = 0;
1682     if (ssc.getGapsInsertedBegin() && right)
1683     {
1684       if (viewport.cursorMode)
1685       {
1686         alignPanel.getSeqPanel().moveCursor(size, 0);
1687       }
1688       else
1689       {
1690         groupAdjustment = size;
1691       }
1692     }
1693     else if (!ssc.getGapsInsertedBegin() && !right)
1694     {
1695       if (viewport.cursorMode)
1696       {
1697         alignPanel.getSeqPanel().moveCursor(-size, 0);
1698       }
1699       else
1700       {
1701         groupAdjustment = -size;
1702       }
1703     }
1704
1705     if (groupAdjustment != 0)
1706     {
1707       viewport.getSelectionGroup().setStartRes(
1708               viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1709       viewport.getSelectionGroup().setEndRes(
1710               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1711     }
1712
1713     boolean appendHistoryItem = false;
1714     if (viewport.historyList != null && viewport.historyList.size() > 0
1715             && viewport.historyList.peek() instanceof SlideSequencesCommand)
1716     {
1717       appendHistoryItem = ssc
1718               .appendSlideCommand((SlideSequencesCommand) viewport.historyList
1719                       .peek());
1720     }
1721
1722     if (!appendHistoryItem)
1723     {
1724       addHistoryItem(ssc);
1725     }
1726
1727     repaint();
1728   }
1729
1730   /**
1731    * DOCUMENT ME!
1732    * 
1733    * @param e
1734    *          DOCUMENT ME!
1735    */
1736   @Override
1737   protected void copy_actionPerformed(ActionEvent e)
1738   {
1739     System.gc();
1740     if (viewport.getSelectionGroup() == null)
1741     {
1742       return;
1743     }
1744     // TODO: preserve the ordering of displayed alignment annotation in any
1745     // internal paste (particularly sequence associated annotation)
1746     SequenceI[] seqs = viewport.getSelectionAsNewSequence();
1747     String[] omitHidden = null;
1748
1749     if (viewport.hasHiddenColumns())
1750     {
1751       omitHidden = viewport.getViewAsString(true);
1752     }
1753
1754     String output = new FormatAdapter().formatSequences("Fasta", seqs,
1755             omitHidden);
1756
1757     StringSelection ss = new StringSelection(output);
1758
1759     try
1760     {
1761       jalview.gui.Desktop.internalCopy = true;
1762       // Its really worth setting the clipboard contents
1763       // to empty before setting the large StringSelection!!
1764       Toolkit.getDefaultToolkit().getSystemClipboard()
1765               .setContents(new StringSelection(""), null);
1766
1767       Toolkit.getDefaultToolkit().getSystemClipboard()
1768               .setContents(ss, Desktop.instance);
1769     } catch (OutOfMemoryError er)
1770     {
1771       new OOMWarning("copying region", er);
1772       return;
1773     }
1774
1775     ArrayList<int[]> hiddenColumns = null;
1776     if (viewport.hasHiddenColumns())
1777     {
1778       hiddenColumns = new ArrayList<int[]>();
1779       int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
1780               .getSelectionGroup().getEndRes();
1781       for (int[] region : viewport.getColumnSelection().getHiddenColumns())
1782       {
1783         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
1784         {
1785           hiddenColumns.add(new int[]
1786           { region[0] - hiddenOffset, region[1] - hiddenOffset });
1787         }
1788       }
1789     }
1790
1791     Desktop.jalviewClipboard = new Object[]
1792     { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
1793     statusBar.setText(MessageManager.formatMessage(
1794             "label.copied_sequences_to_clipboard", new String[]
1795             { Integer.valueOf(seqs.length).toString() }));
1796   }
1797
1798   /**
1799    * DOCUMENT ME!
1800    * 
1801    * @param e
1802    *          DOCUMENT ME!
1803    */
1804   @Override
1805   protected void pasteNew_actionPerformed(ActionEvent e)
1806   {
1807     paste(true);
1808   }
1809
1810   /**
1811    * DOCUMENT ME!
1812    * 
1813    * @param e
1814    *          DOCUMENT ME!
1815    */
1816   @Override
1817   protected void pasteThis_actionPerformed(ActionEvent e)
1818   {
1819     paste(false);
1820   }
1821
1822   /**
1823    * Paste contents of Jalview clipboard
1824    * 
1825    * @param newAlignment
1826    *          true to paste to a new alignment, otherwise add to this.
1827    */
1828   void paste(boolean newAlignment)
1829   {
1830     boolean externalPaste = true;
1831     try
1832     {
1833       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1834       Transferable contents = c.getContents(this);
1835
1836       if (contents == null)
1837       {
1838         return;
1839       }
1840
1841       String str, format;
1842       try
1843       {
1844         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1845         if (str.length() < 1)
1846         {
1847           return;
1848         }
1849
1850         format = new IdentifyFile().Identify(str, "Paste");
1851
1852       } catch (OutOfMemoryError er)
1853       {
1854         new OOMWarning("Out of memory pasting sequences!!", er);
1855         return;
1856       }
1857
1858       SequenceI[] sequences;
1859       boolean annotationAdded = false;
1860       AlignmentI alignment = null;
1861
1862       if (Desktop.jalviewClipboard != null)
1863       {
1864         // The clipboard was filled from within Jalview, we must use the
1865         // sequences
1866         // And dataset from the copied alignment
1867         SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
1868         // be doubly sure that we create *new* sequence objects.
1869         sequences = new SequenceI[newseq.length];
1870         for (int i = 0; i < newseq.length; i++)
1871         {
1872           sequences[i] = new Sequence(newseq[i]);
1873         }
1874         alignment = new Alignment(sequences);
1875         externalPaste = false;
1876       }
1877       else
1878       {
1879         // parse the clipboard as an alignment.
1880         alignment = new FormatAdapter().readFile(str, "Paste", format);
1881         sequences = alignment.getSequencesArray();
1882       }
1883
1884       int alwidth = 0;
1885       ArrayList<Integer> newGraphGroups = new ArrayList<Integer>();
1886       int fgroup = -1;
1887
1888       if (newAlignment)
1889       {
1890
1891         if (Desktop.jalviewClipboard != null)
1892         {
1893           // dataset is inherited
1894           alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
1895         }
1896         else
1897         {
1898           // new dataset is constructed
1899           alignment.setDataset(null);
1900         }
1901         alwidth = alignment.getWidth() + 1;
1902       }
1903       else
1904       {
1905         AlignmentI pastedal = alignment; // preserve pasted alignment object
1906         // Add pasted sequences and dataset into existing alignment.
1907         alignment = viewport.getAlignment();
1908         alwidth = alignment.getWidth() + 1;
1909         // decide if we need to import sequences from an existing dataset
1910         boolean importDs = Desktop.jalviewClipboard != null
1911                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1912         // importDs==true instructs us to copy over new dataset sequences from
1913         // an existing alignment
1914         Vector newDs = (importDs) ? new Vector() : null; // used to create
1915         // minimum dataset set
1916
1917         for (int i = 0; i < sequences.length; i++)
1918         {
1919           if (importDs)
1920           {
1921             newDs.addElement(null);
1922           }
1923           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1924           // paste
1925           if (importDs && ds != null)
1926           {
1927             if (!newDs.contains(ds))
1928             {
1929               newDs.setElementAt(ds, i);
1930               ds = new Sequence(ds);
1931               // update with new dataset sequence
1932               sequences[i].setDatasetSequence(ds);
1933             }
1934             else
1935             {
1936               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1937             }
1938           }
1939           else
1940           {
1941             // copy and derive new dataset sequence
1942             sequences[i] = sequences[i].deriveSequence();
1943             alignment.getDataset().addSequence(
1944                     sequences[i].getDatasetSequence());
1945             // TODO: avoid creation of duplicate dataset sequences with a
1946             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1947           }
1948           alignment.addSequence(sequences[i]); // merges dataset
1949         }
1950         if (newDs != null)
1951         {
1952           newDs.clear(); // tidy up
1953         }
1954         if (alignment.getAlignmentAnnotation() != null)
1955         {
1956           for (AlignmentAnnotation alan : alignment
1957                   .getAlignmentAnnotation())
1958           {
1959             if (alan.graphGroup > fgroup)
1960             {
1961               fgroup = alan.graphGroup;
1962             }
1963           }
1964         }
1965         if (pastedal.getAlignmentAnnotation() != null)
1966         {
1967           // Add any annotation attached to alignment.
1968           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1969           for (int i = 0; i < alann.length; i++)
1970           {
1971             annotationAdded = true;
1972             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
1973             {
1974               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1975               if (newann.graphGroup > -1)
1976               {
1977                 if (newGraphGroups.size() <= newann.graphGroup
1978                         || newGraphGroups.get(newann.graphGroup) == null)
1979                 {
1980                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
1981                   {
1982                     newGraphGroups.add(q, null);
1983                   }
1984                   newGraphGroups.set(newann.graphGroup, new Integer(
1985                           ++fgroup));
1986                 }
1987                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
1988                         .intValue();
1989               }
1990
1991               newann.padAnnotation(alwidth);
1992               alignment.addAnnotation(newann);
1993             }
1994           }
1995         }
1996       }
1997       if (!newAlignment)
1998       {
1999         // /////
2000         // ADD HISTORY ITEM
2001         //
2002         addHistoryItem(new EditCommand(
2003                 MessageManager.getString("label.add_sequences"),
2004                 Action.PASTE,
2005                 sequences, 0, alignment.getWidth(), alignment));
2006       }
2007       // Add any annotations attached to sequences
2008       for (int i = 0; i < sequences.length; i++)
2009       {
2010         if (sequences[i].getAnnotation() != null)
2011         {
2012           AlignmentAnnotation newann;
2013           for (int a = 0; a < sequences[i].getAnnotation().length; a++)
2014           {
2015             annotationAdded = true;
2016             newann = sequences[i].getAnnotation()[a];
2017             newann.adjustForAlignment();
2018             newann.padAnnotation(alwidth);
2019             if (newann.graphGroup > -1)
2020             {
2021               if (newann.graphGroup > -1)
2022               {
2023                 if (newGraphGroups.size() <= newann.graphGroup
2024                         || newGraphGroups.get(newann.graphGroup) == null)
2025                 {
2026                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
2027                   {
2028                     newGraphGroups.add(q, null);
2029                   }
2030                   newGraphGroups.set(newann.graphGroup, new Integer(
2031                           ++fgroup));
2032                 }
2033                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
2034                         .intValue();
2035               }
2036             }
2037             alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
2038             // was
2039             // duplicated
2040             // earlier
2041             alignment
2042                     .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
2043           }
2044         }
2045       }
2046       if (!newAlignment)
2047       {
2048
2049         // propagate alignment changed.
2050         viewport.setEndSeq(alignment.getHeight());
2051         if (annotationAdded)
2052         {
2053           // Duplicate sequence annotation in all views.
2054           AlignmentI[] alview = this.getViewAlignments();
2055           for (int i = 0; i < sequences.length; i++)
2056           {
2057             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
2058             if (sann == null)
2059             {
2060               continue;
2061             }
2062             for (int avnum = 0; avnum < alview.length; avnum++)
2063             {
2064               if (alview[avnum] != alignment)
2065               {
2066                 // duplicate in a view other than the one with input focus
2067                 int avwidth = alview[avnum].getWidth() + 1;
2068                 // this relies on sann being preserved after we
2069                 // modify the sequence's annotation array for each duplication
2070                 for (int a = 0; a < sann.length; a++)
2071                 {
2072                   AlignmentAnnotation newann = new AlignmentAnnotation(
2073                           sann[a]);
2074                   sequences[i].addAlignmentAnnotation(newann);
2075                   newann.padAnnotation(avwidth);
2076                   alview[avnum].addAnnotation(newann); // annotation was
2077                   // duplicated earlier
2078                   // TODO JAL-1145 graphGroups are not updated for sequence
2079                   // annotation added to several views. This may cause
2080                   // strangeness
2081                   alview[avnum].setAnnotationIndex(newann, a);
2082                 }
2083               }
2084             }
2085           }
2086           buildSortByAnnotationScoresMenu();
2087         }
2088         viewport.firePropertyChange("alignment", null,
2089                 alignment.getSequences());
2090         if (alignPanels != null)
2091         {
2092           for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
2093           {
2094             ap.validateAnnotationDimensions(false);
2095           }
2096         }
2097         else
2098         {
2099           alignPanel.validateAnnotationDimensions(false);
2100         }
2101
2102       }
2103       else
2104       {
2105         AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2106                 DEFAULT_HEIGHT);
2107         String newtitle = new String("Copied sequences");
2108
2109         if (Desktop.jalviewClipboard != null
2110                 && Desktop.jalviewClipboard[2] != null)
2111         {
2112           List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
2113           for (int[] region : hc)
2114           {
2115             af.viewport.hideColumns(region[0], region[1]);
2116           }
2117         }
2118
2119         // >>>This is a fix for the moment, until a better solution is
2120         // found!!<<<
2121         af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
2122                 .transferSettings(
2123                         alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
2124
2125         // TODO: maintain provenance of an alignment, rather than just make the
2126         // title a concatenation of operations.
2127         if (!externalPaste)
2128         {
2129           if (title.startsWith("Copied sequences"))
2130           {
2131             newtitle = title;
2132           }
2133           else
2134           {
2135             newtitle = newtitle.concat("- from " + title);
2136           }
2137         }
2138         else
2139         {
2140           newtitle = new String("Pasted sequences");
2141         }
2142
2143         Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
2144                 DEFAULT_HEIGHT);
2145
2146       }
2147
2148     } catch (Exception ex)
2149     {
2150       ex.printStackTrace();
2151       System.out.println("Exception whilst pasting: " + ex);
2152       // could be anything being pasted in here
2153     }
2154
2155   }
2156
2157   @Override
2158   protected void expand_newalign(ActionEvent e)
2159   {
2160     try
2161     {
2162       AlignmentI alignment = AlignmentUtils.expandContext(getViewport()
2163               .getAlignment(), -1);
2164       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2165               DEFAULT_HEIGHT);
2166       String newtitle = new String("Flanking alignment");
2167
2168       if (Desktop.jalviewClipboard != null
2169               && Desktop.jalviewClipboard[2] != null)
2170       {
2171         List<int[]> hc = (List<int[]>) Desktop.jalviewClipboard[2];
2172         for (int region[] : hc)
2173         {
2174           af.viewport.hideColumns(region[0], region[1]);
2175         }
2176       }
2177
2178       // >>>This is a fix for the moment, until a better solution is
2179       // found!!<<<
2180       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
2181               .transferSettings(
2182                       alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer());
2183
2184       // TODO: maintain provenance of an alignment, rather than just make the
2185       // title a concatenation of operations.
2186       {
2187         if (title.startsWith("Copied sequences"))
2188         {
2189           newtitle = title;
2190         }
2191         else
2192         {
2193           newtitle = newtitle.concat("- from " + title);
2194         }
2195       }
2196
2197       Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
2198
2199     } catch (Exception ex)
2200     {
2201       ex.printStackTrace();
2202       System.out.println("Exception whilst pasting: " + ex);
2203       // could be anything being pasted in here
2204     } catch (OutOfMemoryError oom)
2205     {
2206       new OOMWarning("Viewing flanking region of alignment", oom);
2207     }
2208   }
2209
2210   /**
2211    * DOCUMENT ME!
2212    * 
2213    * @param e
2214    *          DOCUMENT ME!
2215    */
2216   @Override
2217   protected void cut_actionPerformed(ActionEvent e)
2218   {
2219     copy_actionPerformed(null);
2220     delete_actionPerformed(null);
2221   }
2222
2223   /**
2224    * DOCUMENT ME!
2225    * 
2226    * @param e
2227    *          DOCUMENT ME!
2228    */
2229   @Override
2230   protected void delete_actionPerformed(ActionEvent evt)
2231   {
2232
2233     SequenceGroup sg = viewport.getSelectionGroup();
2234     if (sg == null)
2235     {
2236       return;
2237     }
2238
2239     List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
2240     SequenceI seq;
2241     for (int i = 0; i < sg.getSize(); i++)
2242     {
2243       seq = sg.getSequenceAt(i);
2244       seqs.add(seq);
2245     }
2246
2247     // If the cut affects all sequences, warn, remove highlighted columns
2248     if (sg.getSize() == viewport.getAlignment().getHeight())
2249     {
2250       int confirm = JOptionPane.showConfirmDialog(this,
2251               MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
2252               MessageManager.getString("label.delete_all"), // $NON-NLS-1$
2253               JOptionPane.OK_CANCEL_OPTION);
2254
2255       if (confirm == JOptionPane.CANCEL_OPTION
2256               || confirm == JOptionPane.CLOSED_OPTION)
2257       {
2258         return;
2259       }
2260       viewport.getColumnSelection().removeElements(sg.getStartRes(),
2261               sg.getEndRes() + 1);
2262     }
2263
2264     SequenceI[] cut = new SequenceI[seqs.size()];
2265     for (int i = 0; i < seqs.size(); i++)
2266     {
2267       cut[i] = seqs.get(i);
2268     }
2269
2270     /*
2271      * //ADD HISTORY ITEM
2272      */
2273     addHistoryItem(new EditCommand(
2274             MessageManager.getString("label.cut_sequences"), Action.CUT,
2275             cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
2276             viewport.getAlignment()));
2277
2278     viewport.setSelectionGroup(null);
2279     viewport.sendSelection();
2280     viewport.getAlignment().deleteGroup(sg);
2281
2282     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2283             .getSequences());
2284     if (viewport.getAlignment().getHeight() < 1)
2285     {
2286       try
2287       {
2288         this.setClosed(true);
2289       } catch (Exception ex)
2290       {
2291       }
2292     }
2293   }
2294
2295   /**
2296    * DOCUMENT ME!
2297    * 
2298    * @param e
2299    *          DOCUMENT ME!
2300    */
2301   @Override
2302   protected void deleteGroups_actionPerformed(ActionEvent e)
2303   {
2304     if (avc.deleteGroups())
2305     {
2306       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
2307       alignPanel.updateAnnotation();
2308       alignPanel.paintAlignment(true);
2309     }
2310   }
2311
2312   /**
2313    * DOCUMENT ME!
2314    * 
2315    * @param e
2316    *          DOCUMENT ME!
2317    */
2318   @Override
2319   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2320   {
2321     SequenceGroup sg = new SequenceGroup();
2322
2323     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2324     {
2325       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
2326     }
2327
2328     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
2329     viewport.setSelectionGroup(sg);
2330     viewport.sendSelection();
2331     alignPanel.paintAlignment(true);
2332     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2333   }
2334
2335   /**
2336    * DOCUMENT ME!
2337    * 
2338    * @param e
2339    *          DOCUMENT ME!
2340    */
2341   @Override
2342   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2343   {
2344     if (viewport.cursorMode)
2345     {
2346       alignPanel.getSeqPanel().keyboardNo1 = null;
2347       alignPanel.getSeqPanel().keyboardNo2 = null;
2348     }
2349     viewport.setSelectionGroup(null);
2350     viewport.getColumnSelection().clear();
2351     viewport.setSelectionGroup(null);
2352     alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null);
2353     alignPanel.getIdPanel().getIdCanvas().searchResults = null;
2354     alignPanel.paintAlignment(true);
2355     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2356     viewport.sendSelection();
2357   }
2358
2359   /**
2360    * DOCUMENT ME!
2361    * 
2362    * @param e
2363    *          DOCUMENT ME!
2364    */
2365   @Override
2366   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
2367   {
2368     SequenceGroup sg = viewport.getSelectionGroup();
2369
2370     if (sg == null)
2371     {
2372       selectAllSequenceMenuItem_actionPerformed(null);
2373
2374       return;
2375     }
2376
2377     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2378     {
2379       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
2380     }
2381
2382     alignPanel.paintAlignment(true);
2383     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2384     viewport.sendSelection();
2385   }
2386
2387   @Override
2388   public void invertColSel_actionPerformed(ActionEvent e)
2389   {
2390     viewport.invertColumnSelection();
2391     alignPanel.paintAlignment(true);
2392     viewport.sendSelection();
2393   }
2394
2395   /**
2396    * DOCUMENT ME!
2397    * 
2398    * @param e
2399    *          DOCUMENT ME!
2400    */
2401   @Override
2402   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
2403   {
2404     trimAlignment(true);
2405   }
2406
2407   /**
2408    * DOCUMENT ME!
2409    * 
2410    * @param e
2411    *          DOCUMENT ME!
2412    */
2413   @Override
2414   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
2415   {
2416     trimAlignment(false);
2417   }
2418
2419   void trimAlignment(boolean trimLeft)
2420   {
2421     ColumnSelection colSel = viewport.getColumnSelection();
2422     int column;
2423
2424     if (colSel.size() > 0)
2425     {
2426       if (trimLeft)
2427       {
2428         column = colSel.getMin();
2429       }
2430       else
2431       {
2432         column = colSel.getMax();
2433       }
2434
2435       SequenceI[] seqs;
2436       if (viewport.getSelectionGroup() != null)
2437       {
2438         seqs = viewport.getSelectionGroup().getSequencesAsArray(
2439                 viewport.getHiddenRepSequences());
2440       }
2441       else
2442       {
2443         seqs = viewport.getAlignment().getSequencesArray();
2444       }
2445
2446       TrimRegionCommand trimRegion;
2447       if (trimLeft)
2448       {
2449         trimRegion = new TrimRegionCommand("Remove Left",
2450                 TrimRegionCommand.TRIM_LEFT, seqs, column,
2451                 viewport.getAlignment(), viewport.getColumnSelection(),
2452                 viewport.getSelectionGroup());
2453         viewport.setStartRes(0);
2454       }
2455       else
2456       {
2457         trimRegion = new TrimRegionCommand("Remove Right",
2458                 TrimRegionCommand.TRIM_RIGHT, seqs, column,
2459                 viewport.getAlignment(), viewport.getColumnSelection(),
2460                 viewport.getSelectionGroup());
2461       }
2462
2463       statusBar.setText(MessageManager.formatMessage(
2464               "label.removed_columns", new String[]
2465               { Integer.valueOf(trimRegion.getSize()).toString() }));
2466
2467       addHistoryItem(trimRegion);
2468
2469       for (SequenceGroup sg : viewport.getAlignment().getGroups())
2470       {
2471         if ((trimLeft && !sg.adjustForRemoveLeft(column))
2472                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
2473         {
2474           viewport.getAlignment().deleteGroup(sg);
2475         }
2476       }
2477
2478       viewport.firePropertyChange("alignment", null, viewport
2479               .getAlignment().getSequences());
2480     }
2481   }
2482
2483   /**
2484    * DOCUMENT ME!
2485    * 
2486    * @param e
2487    *          DOCUMENT ME!
2488    */
2489   @Override
2490   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2491   {
2492     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2493
2494     SequenceI[] seqs;
2495     if (viewport.getSelectionGroup() != null)
2496     {
2497       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2498               viewport.getHiddenRepSequences());
2499       start = viewport.getSelectionGroup().getStartRes();
2500       end = viewport.getSelectionGroup().getEndRes();
2501     }
2502     else
2503     {
2504       seqs = viewport.getAlignment().getSequencesArray();
2505     }
2506
2507     RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2508             "Remove Gapped Columns", seqs, start, end,
2509             viewport.getAlignment());
2510
2511     addHistoryItem(removeGapCols);
2512
2513     statusBar.setText(MessageManager.formatMessage(
2514             "label.removed_empty_columns", new String[]
2515             { Integer.valueOf(removeGapCols.getSize()).toString() }));
2516
2517     // This is to maintain viewport position on first residue
2518     // of first sequence
2519     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2520     int startRes = seq.findPosition(viewport.startRes);
2521     // ShiftList shifts;
2522     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2523     // edit.alColumnChanges=shifts.getInverse();
2524     // if (viewport.hasHiddenColumns)
2525     // viewport.getColumnSelection().compensateForEdits(shifts);
2526     viewport.setStartRes(seq.findIndex(startRes) - 1);
2527     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2528             .getSequences());
2529
2530   }
2531
2532   /**
2533    * DOCUMENT ME!
2534    * 
2535    * @param e
2536    *          DOCUMENT ME!
2537    */
2538   @Override
2539   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2540   {
2541     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2542
2543     SequenceI[] seqs;
2544     if (viewport.getSelectionGroup() != null)
2545     {
2546       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2547               viewport.getHiddenRepSequences());
2548       start = viewport.getSelectionGroup().getStartRes();
2549       end = viewport.getSelectionGroup().getEndRes();
2550     }
2551     else
2552     {
2553       seqs = viewport.getAlignment().getSequencesArray();
2554     }
2555
2556     // This is to maintain viewport position on first residue
2557     // of first sequence
2558     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2559     int startRes = seq.findPosition(viewport.startRes);
2560
2561     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
2562             viewport.getAlignment()));
2563
2564     viewport.setStartRes(seq.findIndex(startRes) - 1);
2565
2566     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2567             .getSequences());
2568
2569   }
2570
2571   /**
2572    * DOCUMENT ME!
2573    * 
2574    * @param e
2575    *          DOCUMENT ME!
2576    */
2577   @Override
2578   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2579   {
2580     viewport.setPadGaps(padGapsMenuitem.isSelected());
2581     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2582             .getSequences());
2583   }
2584
2585   // else
2586   {
2587     // if (justifySeqs>0)
2588     {
2589       // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
2590     }
2591   }
2592
2593   // }
2594
2595   /**
2596    * DOCUMENT ME!
2597    * 
2598    * @param e
2599    *          DOCUMENT ME!
2600    */
2601   @Override
2602   public void findMenuItem_actionPerformed(ActionEvent e)
2603   {
2604     new Finder();
2605   }
2606
2607   @Override
2608   public void newView_actionPerformed(ActionEvent e)
2609   {
2610     newView(true);
2611   }
2612
2613   /**
2614    * 
2615    * @param copyAnnotation
2616    *          if true then duplicate all annnotation, groups and settings
2617    * @return new alignment panel, already displayed.
2618    */
2619   public AlignmentPanel newView(boolean copyAnnotation)
2620   {
2621     return newView(null, copyAnnotation);
2622   }
2623
2624   /**
2625    * 
2626    * @param viewTitle
2627    *          title of newly created view
2628    * @return new alignment panel, already displayed.
2629    */
2630   public AlignmentPanel newView(String viewTitle)
2631   {
2632     return newView(viewTitle, true);
2633   }
2634
2635   /**
2636    * 
2637    * @param viewTitle
2638    *          title of newly created view
2639    * @param copyAnnotation
2640    *          if true then duplicate all annnotation, groups and settings
2641    * @return new alignment panel, already displayed.
2642    */
2643   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
2644   {
2645     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
2646             true);
2647     if (!copyAnnotation)
2648     {
2649       // just remove all the current annotation except for the automatic stuff
2650       newap.av.getAlignment().deleteAllGroups();
2651       for (AlignmentAnnotation alan : newap.av.getAlignment()
2652               .getAlignmentAnnotation())
2653       {
2654         if (!alan.autoCalculated)
2655         {
2656           newap.av.getAlignment().deleteAnnotation(alan);
2657         }
2658         ;
2659       }
2660     }
2661
2662     newap.av.gatherViewsHere = false;
2663
2664     if (viewport.viewName == null)
2665     {
2666       viewport.viewName = "Original";
2667     }
2668
2669     newap.av.historyList = viewport.historyList;
2670     newap.av.redoList = viewport.redoList;
2671
2672     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2673     // make sure the new view has a unique name - this is essential for Jalview
2674     // 2 archives
2675     boolean addFirstIndex = false;
2676     if (viewTitle == null || viewTitle.trim().length() == 0)
2677     {
2678       viewTitle = MessageManager.getString("action.view");
2679       addFirstIndex = true;
2680     }
2681     else
2682     {
2683       index = 1;// we count from 1 if given a specific name
2684     }
2685     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
2686     Vector comps = (Vector) PaintRefresher.components.get(viewport
2687             .getSequenceSetId());
2688     Vector existingNames = new Vector();
2689     for (int i = 0; i < comps.size(); i++)
2690     {
2691       if (comps.elementAt(i) instanceof AlignmentPanel)
2692       {
2693         AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
2694         if (!existingNames.contains(ap.av.viewName))
2695         {
2696           existingNames.addElement(ap.av.viewName);
2697         }
2698       }
2699     }
2700
2701     while (existingNames.contains(newViewName))
2702     {
2703       newViewName = viewTitle + " " + (++index);
2704     }
2705
2706     newap.av.viewName = newViewName;
2707
2708     addAlignmentPanel(newap, true);
2709     newap.alignmentChanged();
2710
2711     if (alignPanels.size() == 2)
2712     {
2713       viewport.gatherViewsHere = true;
2714     }
2715     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2716     return newap;
2717   }
2718
2719   @Override
2720   public void expandViews_actionPerformed(ActionEvent e)
2721   {
2722     Desktop.instance.explodeViews(this);
2723   }
2724
2725   @Override
2726   public void gatherViews_actionPerformed(ActionEvent e)
2727   {
2728     Desktop.instance.gatherViews(this);
2729   }
2730
2731   /**
2732    * DOCUMENT ME!
2733    * 
2734    * @param e
2735    *          DOCUMENT ME!
2736    */
2737   @Override
2738   public void font_actionPerformed(ActionEvent e)
2739   {
2740     new FontChooser(alignPanel);
2741   }
2742
2743   /**
2744    * DOCUMENT ME!
2745    * 
2746    * @param e
2747    *          DOCUMENT ME!
2748    */
2749   @Override
2750   protected void seqLimit_actionPerformed(ActionEvent e)
2751   {
2752     viewport.setShowJVSuffix(seqLimits.isSelected());
2753
2754     alignPanel.getIdPanel().getIdCanvas().setPreferredSize(alignPanel
2755             .calculateIdWidth());
2756     alignPanel.paintAlignment(true);
2757   }
2758
2759   @Override
2760   public void idRightAlign_actionPerformed(ActionEvent e)
2761   {
2762     viewport.setRightAlignIds(idRightAlign.isSelected());
2763     alignPanel.paintAlignment(true);
2764   }
2765
2766   @Override
2767   public void centreColumnLabels_actionPerformed(ActionEvent e)
2768   {
2769     viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
2770     alignPanel.paintAlignment(true);
2771   }
2772
2773   /*
2774    * (non-Javadoc)
2775    * 
2776    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
2777    */
2778   @Override
2779   protected void followHighlight_actionPerformed()
2780   {
2781     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
2782     {
2783       alignPanel.scrollToPosition(
2784               alignPanel.getSeqPanel().seqCanvas.searchResults, false);
2785     }
2786   }
2787
2788   /**
2789    * DOCUMENT ME!
2790    * 
2791    * @param e
2792    *          DOCUMENT ME!
2793    */
2794   @Override
2795   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2796   {
2797     viewport.setColourText(colourTextMenuItem.isSelected());
2798     alignPanel.paintAlignment(true);
2799   }
2800
2801   /**
2802    * DOCUMENT ME!
2803    * 
2804    * @param e
2805    *          DOCUMENT ME!
2806    */
2807   @Override
2808   public void wrapMenuItem_actionPerformed(ActionEvent e)
2809   {
2810     scaleAbove.setVisible(wrapMenuItem.isSelected());
2811     scaleLeft.setVisible(wrapMenuItem.isSelected());
2812     scaleRight.setVisible(wrapMenuItem.isSelected());
2813     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2814     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
2815   }
2816
2817   @Override
2818   public void showAllSeqs_actionPerformed(ActionEvent e)
2819   {
2820     viewport.showAllHiddenSeqs();
2821   }
2822
2823   @Override
2824   public void showAllColumns_actionPerformed(ActionEvent e)
2825   {
2826     viewport.showAllHiddenColumns();
2827     repaint();
2828   }
2829
2830   @Override
2831   public void hideSelSequences_actionPerformed(ActionEvent e)
2832   {
2833     viewport.hideAllSelectedSeqs();
2834     alignPanel.paintAlignment(true);
2835   }
2836
2837   /**
2838    * called by key handler and the hide all/show all menu items
2839    * 
2840    * @param toggleSeqs
2841    * @param toggleCols
2842    */
2843   private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
2844   {
2845
2846     boolean hide = false;
2847     SequenceGroup sg = viewport.getSelectionGroup();
2848     if (!toggleSeqs && !toggleCols)
2849     {
2850       // Hide everything by the current selection - this is a hack - we do the
2851       // invert and then hide
2852       // first check that there will be visible columns after the invert.
2853       if ((viewport.getColumnSelection() != null
2854               && viewport.getColumnSelection().getSelected() != null && viewport
2855               .getColumnSelection().getSelected().size() > 0)
2856               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
2857                       .getEndRes()))
2858       {
2859         // now invert the sequence set, if required - empty selection implies
2860         // that no hiding is required.
2861         if (sg != null)
2862         {
2863           invertSequenceMenuItem_actionPerformed(null);
2864           sg = viewport.getSelectionGroup();
2865           toggleSeqs = true;
2866
2867         }
2868         viewport.expandColSelection(sg, true);
2869         // finally invert the column selection and get the new sequence
2870         // selection.
2871         invertColSel_actionPerformed(null);
2872         toggleCols = true;
2873       }
2874     }
2875
2876     if (toggleSeqs)
2877     {
2878       if (sg != null && sg.getSize() != viewport.getAlignment().getHeight())
2879       {
2880         hideSelSequences_actionPerformed(null);
2881         hide = true;
2882       }
2883       else if (!(toggleCols && viewport.getColumnSelection().getSelected()
2884               .size() > 0))
2885       {
2886         showAllSeqs_actionPerformed(null);
2887       }
2888     }
2889
2890     if (toggleCols)
2891     {
2892       if (viewport.getColumnSelection().getSelected().size() > 0)
2893       {
2894         hideSelColumns_actionPerformed(null);
2895         if (!toggleSeqs)
2896         {
2897           viewport.setSelectionGroup(sg);
2898         }
2899       }
2900       else if (!hide)
2901       {
2902         showAllColumns_actionPerformed(null);
2903       }
2904     }
2905   }
2906
2907   /*
2908    * (non-Javadoc)
2909    * 
2910    * @see
2911    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
2912    * event.ActionEvent)
2913    */
2914   @Override
2915   public void hideAllButSelection_actionPerformed(ActionEvent e)
2916   {
2917     toggleHiddenRegions(false, false);
2918   }
2919
2920   /*
2921    * (non-Javadoc)
2922    * 
2923    * @see
2924    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
2925    * .ActionEvent)
2926    */
2927   @Override
2928   public void hideAllSelection_actionPerformed(ActionEvent e)
2929   {
2930     SequenceGroup sg = viewport.getSelectionGroup();
2931     viewport.expandColSelection(sg, false);
2932     viewport.hideAllSelectedSeqs();
2933     viewport.hideSelectedColumns();
2934     alignPanel.paintAlignment(true);
2935   }
2936
2937   /*
2938    * (non-Javadoc)
2939    * 
2940    * @see
2941    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
2942    * ActionEvent)
2943    */
2944   @Override
2945   public void showAllhidden_actionPerformed(ActionEvent e)
2946   {
2947     viewport.showAllHiddenColumns();
2948     viewport.showAllHiddenSeqs();
2949     alignPanel.paintAlignment(true);
2950   }
2951
2952   @Override
2953   public void hideSelColumns_actionPerformed(ActionEvent e)
2954   {
2955     viewport.hideSelectedColumns();
2956     alignPanel.paintAlignment(true);
2957   }
2958
2959   @Override
2960   public void hiddenMarkers_actionPerformed(ActionEvent e)
2961   {
2962     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
2963     repaint();
2964   }
2965
2966   /**
2967    * DOCUMENT ME!
2968    * 
2969    * @param e
2970    *          DOCUMENT ME!
2971    */
2972   @Override
2973   protected void scaleAbove_actionPerformed(ActionEvent e)
2974   {
2975     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
2976     alignPanel.paintAlignment(true);
2977   }
2978
2979   /**
2980    * DOCUMENT ME!
2981    * 
2982    * @param e
2983    *          DOCUMENT ME!
2984    */
2985   @Override
2986   protected void scaleLeft_actionPerformed(ActionEvent e)
2987   {
2988     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
2989     alignPanel.paintAlignment(true);
2990   }
2991
2992   /**
2993    * DOCUMENT ME!
2994    * 
2995    * @param e
2996    *          DOCUMENT ME!
2997    */
2998   @Override
2999   protected void scaleRight_actionPerformed(ActionEvent e)
3000   {
3001     viewport.setScaleRightWrapped(scaleRight.isSelected());
3002     alignPanel.paintAlignment(true);
3003   }
3004
3005   /**
3006    * DOCUMENT ME!
3007    * 
3008    * @param e
3009    *          DOCUMENT ME!
3010    */
3011   @Override
3012   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
3013   {
3014     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
3015     alignPanel.paintAlignment(true);
3016   }
3017
3018   /**
3019    * DOCUMENT ME!
3020    * 
3021    * @param e
3022    *          DOCUMENT ME!
3023    */
3024   @Override
3025   public void viewTextMenuItem_actionPerformed(ActionEvent e)
3026   {
3027     viewport.setShowText(viewTextMenuItem.isSelected());
3028     alignPanel.paintAlignment(true);
3029   }
3030
3031   /**
3032    * DOCUMENT ME!
3033    * 
3034    * @param e
3035    *          DOCUMENT ME!
3036    */
3037   @Override
3038   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
3039   {
3040     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
3041     alignPanel.paintAlignment(true);
3042   }
3043
3044   public FeatureSettings featureSettings;
3045
3046   @Override
3047   public void featureSettings_actionPerformed(ActionEvent e)
3048   {
3049     if (featureSettings != null)
3050     {
3051       featureSettings.close();
3052       featureSettings = null;
3053     }
3054     if (!showSeqFeatures.isSelected())
3055     {
3056       // make sure features are actually displayed
3057       showSeqFeatures.setSelected(true);
3058       showSeqFeatures_actionPerformed(null);
3059     }
3060     featureSettings = new FeatureSettings(this);
3061   }
3062
3063   /**
3064    * Set or clear 'Show Sequence Features'
3065    * 
3066    * @param evt
3067    *          DOCUMENT ME!
3068    */
3069   @Override
3070   public void showSeqFeatures_actionPerformed(ActionEvent evt)
3071   {
3072     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
3073     alignPanel.paintAlignment(true);
3074     if (alignPanel.getOverviewPanel() != null)
3075     {
3076       alignPanel.getOverviewPanel().updateOverviewImage();
3077     }
3078   }
3079
3080   /**
3081    * Set or clear 'Show Sequence Features'
3082    * 
3083    * @param evt
3084    *          DOCUMENT ME!
3085    */
3086   @Override
3087   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
3088   {
3089     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
3090             .isSelected());
3091     if (viewport.isShowSequenceFeaturesHeight())
3092     {
3093       // ensure we're actually displaying features
3094       viewport.setShowSequenceFeatures(true);
3095       showSeqFeatures.setSelected(true);
3096     }
3097     alignPanel.paintAlignment(true);
3098     if (alignPanel.getOverviewPanel() != null)
3099     {
3100       alignPanel.getOverviewPanel().updateOverviewImage();
3101     }
3102   }
3103
3104   /**
3105    * Action on toggle of the 'Show annotations' menu item. This shows or hides
3106    * the annotations panel as a whole.
3107    * 
3108    * The options to show/hide all annotations should be enabled when the panel
3109    * is shown, and disabled when the panel is hidden.
3110    * 
3111    * @param e
3112    */
3113   @Override
3114   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
3115   {
3116     final boolean setVisible = annotationPanelMenuItem.isSelected();
3117     viewport.setShowAnnotation(setVisible);
3118     alignPanel.setAnnotationVisible(setVisible);
3119     this.showAllSeqAnnotations.setEnabled(setVisible);
3120     this.hideAllSeqAnnotations.setEnabled(setVisible);
3121     this.showAllAlAnnotations.setEnabled(setVisible);
3122     this.hideAllAlAnnotations.setEnabled(setVisible);
3123   }
3124
3125   @Override
3126   public void alignmentProperties()
3127   {
3128     JEditorPane editPane = new JEditorPane("text/html", "");
3129     editPane.setEditable(false);
3130     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
3131             .formatAsHtml();
3132     editPane.setText(MessageManager.formatMessage("label.html_content",
3133             new String[]
3134             { contents.toString() }));
3135     JInternalFrame frame = new JInternalFrame();
3136     frame.getContentPane().add(new JScrollPane(editPane));
3137
3138     Desktop.instance.addInternalFrame(frame, MessageManager.formatMessage(
3139             "label.alignment_properties", new String[]
3140             { getTitle() }), 500, 400);
3141   }
3142
3143   /**
3144    * DOCUMENT ME!
3145    * 
3146    * @param e
3147    *          DOCUMENT ME!
3148    */
3149   @Override
3150   public void overviewMenuItem_actionPerformed(ActionEvent e)
3151   {
3152     if (alignPanel.overviewPanel != null)
3153     {
3154       return;
3155     }
3156
3157     JInternalFrame frame = new JInternalFrame();
3158     OverviewPanel overview = new OverviewPanel(alignPanel);
3159     frame.setContentPane(overview);
3160     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3161             "label.overview_params", new String[]
3162             { this.getTitle() }), frame.getWidth(), frame.getHeight());
3163     frame.pack();
3164     frame.setLayer(JLayeredPane.PALETTE_LAYER);
3165     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
3166     {
3167       @Override
3168       public void internalFrameClosed(
3169               javax.swing.event.InternalFrameEvent evt)
3170       {
3171         alignPanel.setOverviewPanel(null);
3172       };
3173     });
3174
3175     alignPanel.setOverviewPanel(overview);
3176   }
3177
3178   @Override
3179   public void textColour_actionPerformed(ActionEvent e)
3180   {
3181     new TextColourChooser().chooseColour(alignPanel, null);
3182   }
3183
3184   /**
3185    * DOCUMENT ME!
3186    * 
3187    * @param e
3188    *          DOCUMENT ME!
3189    */
3190   @Override
3191   protected void noColourmenuItem_actionPerformed(ActionEvent e)
3192   {
3193     changeColour(null);
3194   }
3195
3196   /**
3197    * DOCUMENT ME!
3198    * 
3199    * @param e
3200    *          DOCUMENT ME!
3201    */
3202   @Override
3203   public void clustalColour_actionPerformed(ActionEvent e)
3204   {
3205     changeColour(new ClustalxColourScheme(viewport.getAlignment(),
3206             viewport.getHiddenRepSequences()));
3207   }
3208
3209   /**
3210    * DOCUMENT ME!
3211    * 
3212    * @param e
3213    *          DOCUMENT ME!
3214    */
3215   @Override
3216   public void zappoColour_actionPerformed(ActionEvent e)
3217   {
3218     changeColour(new ZappoColourScheme());
3219   }
3220
3221   /**
3222    * DOCUMENT ME!
3223    * 
3224    * @param e
3225    *          DOCUMENT ME!
3226    */
3227   @Override
3228   public void taylorColour_actionPerformed(ActionEvent e)
3229   {
3230     changeColour(new TaylorColourScheme());
3231   }
3232
3233   /**
3234    * DOCUMENT ME!
3235    * 
3236    * @param e
3237    *          DOCUMENT ME!
3238    */
3239   @Override
3240   public void hydrophobicityColour_actionPerformed(ActionEvent e)
3241   {
3242     changeColour(new HydrophobicColourScheme());
3243   }
3244
3245   /**
3246    * DOCUMENT ME!
3247    * 
3248    * @param e
3249    *          DOCUMENT ME!
3250    */
3251   @Override
3252   public void helixColour_actionPerformed(ActionEvent e)
3253   {
3254     changeColour(new HelixColourScheme());
3255   }
3256
3257   /**
3258    * DOCUMENT ME!
3259    * 
3260    * @param e
3261    *          DOCUMENT ME!
3262    */
3263   @Override
3264   public void strandColour_actionPerformed(ActionEvent e)
3265   {
3266     changeColour(new StrandColourScheme());
3267   }
3268
3269   /**
3270    * DOCUMENT ME!
3271    * 
3272    * @param e
3273    *          DOCUMENT ME!
3274    */
3275   @Override
3276   public void turnColour_actionPerformed(ActionEvent e)
3277   {
3278     changeColour(new TurnColourScheme());
3279   }
3280
3281   /**
3282    * DOCUMENT ME!
3283    * 
3284    * @param e
3285    *          DOCUMENT ME!
3286    */
3287   @Override
3288   public void buriedColour_actionPerformed(ActionEvent e)
3289   {
3290     changeColour(new BuriedColourScheme());
3291   }
3292
3293   /**
3294    * DOCUMENT ME!
3295    * 
3296    * @param e
3297    *          DOCUMENT ME!
3298    */
3299   @Override
3300   public void nucleotideColour_actionPerformed(ActionEvent e)
3301   {
3302     changeColour(new NucleotideColourScheme());
3303   }
3304
3305   @Override
3306   public void purinePyrimidineColour_actionPerformed(ActionEvent e)
3307   {
3308     changeColour(new PurinePyrimidineColourScheme());
3309   }
3310
3311   /*
3312    * public void covariationColour_actionPerformed(ActionEvent e) {
3313    * changeColour(new
3314    * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
3315    * ()[0])); }
3316    */
3317   @Override
3318   public void annotationColour_actionPerformed(ActionEvent e)
3319   {
3320     new AnnotationColourChooser(viewport, alignPanel);
3321   }
3322
3323   @Override
3324   public void annotationColumn_actionPerformed(ActionEvent e)
3325   {
3326     new AnnotationColumnChooser(viewport, alignPanel);
3327   }
3328
3329   @Override
3330   public void rnahelicesColour_actionPerformed(ActionEvent e)
3331   {
3332     new RNAHelicesColourChooser(viewport, alignPanel);
3333   }
3334
3335   /**
3336    * DOCUMENT ME!
3337    * 
3338    * @param e
3339    *          DOCUMENT ME!
3340    */
3341   @Override
3342   protected void applyToAllGroups_actionPerformed(ActionEvent e)
3343   {
3344     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
3345   }
3346
3347   /**
3348    * DOCUMENT ME!
3349    * 
3350    * @param cs
3351    *          DOCUMENT ME!
3352    */
3353   public void changeColour(ColourSchemeI cs)
3354   {
3355     // TODO: compare with applet and pull up to model method
3356     int threshold = 0;
3357
3358     if (cs != null)
3359     {
3360       if (viewport.getAbovePIDThreshold())
3361       {
3362         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
3363                 "Background");
3364         cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3365       }
3366       else
3367       {
3368         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3369       }
3370
3371       if (viewport.getConservationSelected())
3372       {
3373
3374         Alignment al = (Alignment) viewport.getAlignment();
3375         Conservation c = new Conservation("All",
3376                 ResidueProperties.propHash, 3, al.getSequences(), 0,
3377                 al.getWidth() - 1);
3378
3379         c.calculate();
3380         c.verdict(false, viewport.getConsPercGaps());
3381
3382         cs.setConservation(c);
3383
3384         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
3385                 cs, "Background"));
3386       }
3387       else
3388       {
3389         cs.setConservation(null);
3390       }
3391
3392       cs.setConsensus(viewport.getSequenceConsensusHash());
3393     }
3394
3395     viewport.setGlobalColourScheme(cs);
3396
3397     if (viewport.getColourAppliesToAllGroups())
3398     {
3399
3400       for (SequenceGroup sg : viewport.getAlignment().getGroups())
3401       {
3402         if (cs == null)
3403         {
3404           sg.cs = null;
3405           continue;
3406         }
3407
3408         if (cs instanceof ClustalxColourScheme)
3409         {
3410           sg.cs = new ClustalxColourScheme(sg,
3411                   viewport.getHiddenRepSequences());
3412         }
3413         else if (cs instanceof UserColourScheme)
3414         {
3415           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
3416         }
3417         else
3418         {
3419           try
3420           {
3421             sg.cs = cs.getClass().newInstance();
3422           } catch (Exception ex)
3423           {
3424           }
3425         }
3426
3427         if (viewport.getAbovePIDThreshold()
3428                 || cs instanceof PIDColourScheme
3429                 || cs instanceof Blosum62ColourScheme)
3430         {
3431           sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3432
3433           sg.cs.setConsensus(AAFrequency.calculate(
3434                   sg.getSequences(viewport.getHiddenRepSequences()),
3435                   sg.getStartRes(), sg.getEndRes() + 1));
3436         }
3437         else
3438         {
3439           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3440         }
3441
3442         if (viewport.getConservationSelected())
3443         {
3444           Conservation c = new Conservation("Group",
3445                   ResidueProperties.propHash, 3, sg.getSequences(viewport
3446                           .getHiddenRepSequences()), sg.getStartRes(),
3447                   sg.getEndRes() + 1);
3448           c.calculate();
3449           c.verdict(false, viewport.getConsPercGaps());
3450           sg.cs.setConservation(c);
3451         }
3452         else
3453         {
3454           sg.cs.setConservation(null);
3455         }
3456       }
3457     }
3458
3459     if (alignPanel.getOverviewPanel() != null)
3460     {
3461       alignPanel.getOverviewPanel().updateOverviewImage();
3462     }
3463
3464     alignPanel.paintAlignment(true);
3465   }
3466
3467   /**
3468    * DOCUMENT ME!
3469    * 
3470    * @param e
3471    *          DOCUMENT ME!
3472    */
3473   @Override
3474   protected void modifyPID_actionPerformed(ActionEvent e)
3475   {
3476     if (viewport.getAbovePIDThreshold()
3477             && viewport.getGlobalColourScheme() != null)
3478     {
3479       SliderPanel.setPIDSliderSource(alignPanel,
3480               viewport.getGlobalColourScheme(), "Background");
3481       SliderPanel.showPIDSlider();
3482     }
3483   }
3484
3485   /**
3486    * DOCUMENT ME!
3487    * 
3488    * @param e
3489    *          DOCUMENT ME!
3490    */
3491   @Override
3492   protected void modifyConservation_actionPerformed(ActionEvent e)
3493   {
3494     if (viewport.getConservationSelected()
3495             && viewport.getGlobalColourScheme() != null)
3496     {
3497       SliderPanel.setConservationSlider(alignPanel,
3498               viewport.getGlobalColourScheme(), "Background");
3499       SliderPanel.showConservationSlider();
3500     }
3501   }
3502
3503   /**
3504    * DOCUMENT ME!
3505    * 
3506    * @param e
3507    *          DOCUMENT ME!
3508    */
3509   @Override
3510   protected void conservationMenuItem_actionPerformed(ActionEvent e)
3511   {
3512     viewport.setConservationSelected(conservationMenuItem.isSelected());
3513
3514     viewport.setAbovePIDThreshold(false);
3515     abovePIDThreshold.setSelected(false);
3516
3517     changeColour(viewport.getGlobalColourScheme());
3518
3519     modifyConservation_actionPerformed(null);
3520   }
3521
3522   /**
3523    * DOCUMENT ME!
3524    * 
3525    * @param e
3526    *          DOCUMENT ME!
3527    */
3528   @Override
3529   public void abovePIDThreshold_actionPerformed(ActionEvent e)
3530   {
3531     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
3532
3533     conservationMenuItem.setSelected(false);
3534     viewport.setConservationSelected(false);
3535
3536     changeColour(viewport.getGlobalColourScheme());
3537
3538     modifyPID_actionPerformed(null);
3539   }
3540
3541   /**
3542    * DOCUMENT ME!
3543    * 
3544    * @param e
3545    *          DOCUMENT ME!
3546    */
3547   @Override
3548   public void userDefinedColour_actionPerformed(ActionEvent e)
3549   {
3550     if (e.getActionCommand().equals(
3551             MessageManager.getString("action.user_defined")))
3552     {
3553       new UserDefinedColours(alignPanel, null);
3554     }
3555     else
3556     {
3557       UserColourScheme udc = (UserColourScheme) UserDefinedColours
3558               .getUserColourSchemes().get(e.getActionCommand());
3559
3560       changeColour(udc);
3561     }
3562   }
3563
3564   public void updateUserColourMenu()
3565   {
3566
3567     Component[] menuItems = colourMenu.getMenuComponents();
3568     int i, iSize = menuItems.length;
3569     for (i = 0; i < iSize; i++)
3570     {
3571       if (menuItems[i].getName() != null
3572               && menuItems[i].getName().equals("USER_DEFINED"))
3573       {
3574         colourMenu.remove(menuItems[i]);
3575         iSize--;
3576       }
3577     }
3578     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
3579     {
3580       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
3581               .getUserColourSchemes().keys();
3582
3583       while (userColours.hasMoreElements())
3584       {
3585         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
3586                 userColours.nextElement().toString());
3587         radioItem.setName("USER_DEFINED");
3588         radioItem.addMouseListener(new MouseAdapter()
3589         {
3590           @Override
3591           public void mousePressed(MouseEvent evt)
3592           {
3593             if (evt.isControlDown()
3594                     || SwingUtilities.isRightMouseButton(evt))
3595             {
3596               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
3597
3598               int option = JOptionPane.showInternalConfirmDialog(
3599                       jalview.gui.Desktop.desktop,
3600                       MessageManager
3601                               .getString("label.remove_from_default_list"),
3602                       MessageManager
3603                               .getString("label.remove_user_defined_colour"),
3604                       JOptionPane.YES_NO_OPTION);
3605               if (option == JOptionPane.YES_OPTION)
3606               {
3607                 jalview.gui.UserDefinedColours
3608                         .removeColourFromDefaults(radioItem.getText());
3609                 colourMenu.remove(radioItem);
3610               }
3611               else
3612               {
3613                 radioItem.addActionListener(new ActionListener()
3614                 {
3615                   @Override
3616                   public void actionPerformed(ActionEvent evt)
3617                   {
3618                     userDefinedColour_actionPerformed(evt);
3619                   }
3620                 });
3621               }
3622             }
3623           }
3624         });
3625         radioItem.addActionListener(new ActionListener()
3626         {
3627           @Override
3628           public void actionPerformed(ActionEvent evt)
3629           {
3630             userDefinedColour_actionPerformed(evt);
3631           }
3632         });
3633
3634         colourMenu.insert(radioItem, 15);
3635         colours.add(radioItem);
3636       }
3637     }
3638   }
3639
3640   /**
3641    * DOCUMENT ME!
3642    * 
3643    * @param e
3644    *          DOCUMENT ME!
3645    */
3646   @Override
3647   public void PIDColour_actionPerformed(ActionEvent e)
3648   {
3649     changeColour(new PIDColourScheme());
3650   }
3651
3652   /**
3653    * DOCUMENT ME!
3654    * 
3655    * @param e
3656    *          DOCUMENT ME!
3657    */
3658   @Override
3659   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
3660   {
3661     changeColour(new Blosum62ColourScheme());
3662   }
3663
3664   /**
3665    * DOCUMENT ME!
3666    * 
3667    * @param e
3668    *          DOCUMENT ME!
3669    */
3670   @Override
3671   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3672   {
3673     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3674     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
3675             .getAlignment().getSequenceAt(0), null);
3676     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3677             viewport.getAlignment()));
3678     alignPanel.paintAlignment(true);
3679   }
3680
3681   /**
3682    * DOCUMENT ME!
3683    * 
3684    * @param e
3685    *          DOCUMENT ME!
3686    */
3687   @Override
3688   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3689   {
3690     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3691     AlignmentSorter.sortByID(viewport.getAlignment());
3692     addHistoryItem(new OrderCommand("ID Sort", oldOrder,
3693             viewport.getAlignment()));
3694     alignPanel.paintAlignment(true);
3695   }
3696
3697   /**
3698    * DOCUMENT ME!
3699    * 
3700    * @param e
3701    *          DOCUMENT ME!
3702    */
3703   @Override
3704   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3705   {
3706     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3707     AlignmentSorter.sortByLength(viewport.getAlignment());
3708     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3709             viewport.getAlignment()));
3710     alignPanel.paintAlignment(true);
3711   }
3712
3713   /**
3714    * DOCUMENT ME!
3715    * 
3716    * @param e
3717    *          DOCUMENT ME!
3718    */
3719   @Override
3720   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3721   {
3722     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3723     AlignmentSorter.sortByGroup(viewport.getAlignment());
3724     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3725             viewport.getAlignment()));
3726
3727     alignPanel.paintAlignment(true);
3728   }
3729
3730   /**
3731    * DOCUMENT ME!
3732    * 
3733    * @param e
3734    *          DOCUMENT ME!
3735    */
3736   @Override
3737   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3738   {
3739     new RedundancyPanel(alignPanel, this);
3740   }
3741
3742   /**
3743    * DOCUMENT ME!
3744    * 
3745    * @param e
3746    *          DOCUMENT ME!
3747    */
3748   @Override
3749   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3750   {
3751     if ((viewport.getSelectionGroup() == null)
3752             || (viewport.getSelectionGroup().getSize() < 2))
3753     {
3754       JOptionPane.showInternalMessageDialog(this, MessageManager
3755               .getString("label.you_must_select_least_two_sequences"),
3756               MessageManager.getString("label.invalid_selection"),
3757               JOptionPane.WARNING_MESSAGE);
3758     }
3759     else
3760     {
3761       JInternalFrame frame = new JInternalFrame();
3762       frame.setContentPane(new PairwiseAlignPanel(viewport));
3763       Desktop.addInternalFrame(frame,
3764               MessageManager.getString("action.pairwise_alignment"), 600,
3765               500);
3766     }
3767   }
3768
3769   /**
3770    * DOCUMENT ME!
3771    * 
3772    * @param e
3773    *          DOCUMENT ME!
3774    */
3775   @Override
3776   public void PCAMenuItem_actionPerformed(ActionEvent e)
3777   {
3778     if (((viewport.getSelectionGroup() != null)
3779             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
3780             .getSelectionGroup().getSize() > 0))
3781             || (viewport.getAlignment().getHeight() < 4))
3782     {
3783       JOptionPane
3784               .showInternalMessageDialog(
3785                       this,
3786                       MessageManager
3787                               .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
3788                       MessageManager
3789                               .getString("label.sequence_selection_insufficient"),
3790                       JOptionPane.WARNING_MESSAGE);
3791
3792       return;
3793     }
3794
3795     new PCAPanel(alignPanel);
3796   }
3797
3798   @Override
3799   public void autoCalculate_actionPerformed(ActionEvent e)
3800   {
3801     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3802     if (viewport.autoCalculateConsensus)
3803     {
3804       viewport.firePropertyChange("alignment", null, viewport
3805               .getAlignment().getSequences());
3806     }
3807   }
3808
3809   @Override
3810   public void sortByTreeOption_actionPerformed(ActionEvent e)
3811   {
3812     viewport.sortByTree = sortByTree.isSelected();
3813   }
3814
3815   @Override
3816   protected void listenToViewSelections_actionPerformed(ActionEvent e)
3817   {
3818     viewport.followSelection = listenToViewSelections.isSelected();
3819   }
3820
3821   /**
3822    * DOCUMENT ME!
3823    * 
3824    * @param e
3825    *          DOCUMENT ME!
3826    */
3827   @Override
3828   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3829   {
3830     NewTreePanel("AV", "PID", "Average distance tree using PID");
3831   }
3832
3833   /**
3834    * DOCUMENT ME!
3835    * 
3836    * @param e
3837    *          DOCUMENT ME!
3838    */
3839   @Override
3840   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3841   {
3842     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3843   }
3844
3845   /**
3846    * DOCUMENT ME!
3847    * 
3848    * @param e
3849    *          DOCUMENT ME!
3850    */
3851   @Override
3852   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3853   {
3854     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3855   }
3856
3857   /**
3858    * DOCUMENT ME!
3859    * 
3860    * @param e
3861    *          DOCUMENT ME!
3862    */
3863   @Override
3864   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3865   {
3866     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3867   }
3868
3869   /**
3870    * DOCUMENT ME!
3871    * 
3872    * @param type
3873    *          DOCUMENT ME!
3874    * @param pwType
3875    *          DOCUMENT ME!
3876    * @param title
3877    *          DOCUMENT ME!
3878    */
3879   void NewTreePanel(String type, String pwType, String title)
3880   {
3881     TreePanel tp;
3882
3883     if (viewport.getSelectionGroup() != null
3884             && viewport.getSelectionGroup().getSize() > 0)
3885     {
3886       if (viewport.getSelectionGroup().getSize() < 3)
3887       {
3888         JOptionPane
3889                 .showMessageDialog(
3890                         Desktop.desktop,
3891                         MessageManager
3892                                 .getString("label.you_need_more_two_sequences_selected_build_tree"),
3893                         MessageManager
3894                                 .getString("label.not_enough_sequences"),
3895                         JOptionPane.WARNING_MESSAGE);
3896         return;
3897       }
3898
3899       SequenceGroup sg = viewport.getSelectionGroup();
3900
3901       /* Decide if the selection is a column region */
3902       for (SequenceI _s : sg.getSequences())
3903       {
3904         if (_s.getLength() < sg.getEndRes())
3905         {
3906           JOptionPane
3907                   .showMessageDialog(
3908                           Desktop.desktop,
3909                           MessageManager
3910                                   .getString("label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
3911                           MessageManager
3912                                   .getString("label.sequences_selection_not_aligned"),
3913                           JOptionPane.WARNING_MESSAGE);
3914
3915           return;
3916         }
3917       }
3918
3919       title = title + " on region";
3920       tp = new TreePanel(alignPanel, type, pwType);
3921     }
3922     else
3923     {
3924       // are the visible sequences aligned?
3925       if (!viewport.getAlignment().isAligned(false))
3926       {
3927         JOptionPane
3928                 .showMessageDialog(
3929                         Desktop.desktop,
3930                         MessageManager
3931                                 .getString("label.sequences_must_be_aligned_before_creating_tree"),
3932                         MessageManager
3933                                 .getString("label.sequences_not_aligned"),
3934                         JOptionPane.WARNING_MESSAGE);
3935
3936         return;
3937       }
3938
3939       if (viewport.getAlignment().getHeight() < 2)
3940       {
3941         return;
3942       }
3943
3944       tp = new TreePanel(alignPanel, type, pwType);
3945     }
3946
3947     title += " from ";
3948
3949     if (viewport.viewName != null)
3950     {
3951       title += viewport.viewName + " of ";
3952     }
3953
3954     title += this.title;
3955
3956     Desktop.addInternalFrame(tp, title, 600, 500);
3957   }
3958
3959   /**
3960    * DOCUMENT ME!
3961    * 
3962    * @param title
3963    *          DOCUMENT ME!
3964    * @param order
3965    *          DOCUMENT ME!
3966    */
3967   public void addSortByOrderMenuItem(String title,
3968           final AlignmentOrder order)
3969   {
3970     final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new String[]{title}));
3971     sort.add(item);
3972     item.addActionListener(new java.awt.event.ActionListener()
3973     {
3974       @Override
3975       public void actionPerformed(ActionEvent e)
3976       {
3977         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3978
3979         // TODO: JBPNote - have to map order entries to curent SequenceI
3980         // pointers
3981         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3982
3983         addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
3984                 .getAlignment()));
3985
3986         alignPanel.paintAlignment(true);
3987       }
3988     });
3989   }
3990
3991   /**
3992    * Add a new sort by annotation score menu item
3993    * 
3994    * @param sort
3995    *          the menu to add the option to
3996    * @param scoreLabel
3997    *          the label used to retrieve scores for each sequence on the
3998    *          alignment
3999    */
4000   public void addSortByAnnotScoreMenuItem(JMenu sort,
4001           final String scoreLabel)
4002   {
4003     final JMenuItem item = new JMenuItem(scoreLabel);
4004     sort.add(item);
4005     item.addActionListener(new java.awt.event.ActionListener()
4006     {
4007       @Override
4008       public void actionPerformed(ActionEvent e)
4009       {
4010         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4011         AlignmentSorter.sortByAnnotationScore(scoreLabel,
4012                 viewport.getAlignment());// ,viewport.getSelectionGroup());
4013         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
4014                 viewport.getAlignment()));
4015         alignPanel.paintAlignment(true);
4016       }
4017     });
4018   }
4019
4020   /**
4021    * last hash for alignment's annotation array - used to minimise cost of
4022    * rebuild.
4023    */
4024   protected int _annotationScoreVectorHash;
4025
4026   /**
4027    * search the alignment and rebuild the sort by annotation score submenu the
4028    * last alignment annotation vector hash is stored to minimize cost of
4029    * rebuilding in subsequence calls.
4030    * 
4031    */
4032   @Override
4033   public void buildSortByAnnotationScoresMenu()
4034   {
4035     if (viewport.getAlignment().getAlignmentAnnotation() == null)
4036     {
4037       return;
4038     }
4039
4040     if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
4041     {
4042       sortByAnnotScore.removeAll();
4043       // almost certainly a quicker way to do this - but we keep it simple
4044       Hashtable scoreSorts = new Hashtable();
4045       AlignmentAnnotation aann[];
4046       for (SequenceI sqa : viewport.getAlignment().getSequences())
4047       {
4048         aann = sqa.getAnnotation();
4049         for (int i = 0; aann != null && i < aann.length; i++)
4050         {
4051           if (aann[i].hasScore() && aann[i].sequenceRef != null)
4052           {
4053             scoreSorts.put(aann[i].label, aann[i].label);
4054           }
4055         }
4056       }
4057       Enumeration labels = scoreSorts.keys();
4058       while (labels.hasMoreElements())
4059       {
4060         addSortByAnnotScoreMenuItem(sortByAnnotScore,
4061                 (String) labels.nextElement());
4062       }
4063       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
4064       scoreSorts.clear();
4065
4066       _annotationScoreVectorHash = viewport.getAlignment()
4067               .getAlignmentAnnotation().hashCode();
4068     }
4069   }
4070
4071   /**
4072    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
4073    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
4074    * call. Listeners are added to remove the menu item when the treePanel is
4075    * closed, and adjust the tree leaf to sequence mapping when the alignment is
4076    * modified.
4077    * 
4078    * @param treePanel
4079    *          Displayed tree window.
4080    * @param title
4081    *          SortBy menu item title.
4082    */
4083   @Override
4084   public void buildTreeMenu()
4085   {
4086     calculateTree.removeAll();
4087     // build the calculate menu
4088
4089     for (final String type : new String[]
4090     { "NJ", "AV" })
4091     {
4092       String treecalcnm = MessageManager.getString("label.tree_calc_"
4093               + type.toLowerCase());
4094       for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
4095       {
4096         JMenuItem tm = new JMenuItem();
4097         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
4098         if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
4099         {
4100           String smn = MessageManager.getStringOrReturn(
4101                   "label.score_model_", sm.getName());
4102           final String title = MessageManager.formatMessage(
4103                   "label.treecalc_title", treecalcnm, smn);
4104           tm.setText(title);//
4105           tm.addActionListener(new java.awt.event.ActionListener()
4106           {
4107             @Override
4108             public void actionPerformed(ActionEvent e)
4109             {
4110               NewTreePanel(type, (String) pwtype, title);
4111             }
4112           });
4113           calculateTree.add(tm);
4114         }
4115
4116       }
4117     }
4118     sortByTreeMenu.removeAll();
4119
4120     Vector comps = (Vector) PaintRefresher.components.get(viewport
4121             .getSequenceSetId());
4122     Vector treePanels = new Vector();
4123     int i, iSize = comps.size();
4124     for (i = 0; i < iSize; i++)
4125     {
4126       if (comps.elementAt(i) instanceof TreePanel)
4127       {
4128         treePanels.add(comps.elementAt(i));
4129       }
4130     }
4131
4132     iSize = treePanels.size();
4133
4134     if (iSize < 1)
4135     {
4136       sortByTreeMenu.setVisible(false);
4137       return;
4138     }
4139
4140     sortByTreeMenu.setVisible(true);
4141
4142     for (i = 0; i < treePanels.size(); i++)
4143     {
4144       final TreePanel tp = (TreePanel) treePanels.elementAt(i);
4145       final JMenuItem item = new JMenuItem(tp.getTitle());
4146       final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
4147       item.addActionListener(new java.awt.event.ActionListener()
4148       {
4149         @Override
4150         public void actionPerformed(ActionEvent e)
4151         {
4152           tp.sortByTree_actionPerformed(null);
4153           addHistoryItem(tp.sortAlignmentIn(alignPanel));
4154
4155         }
4156       });
4157
4158       sortByTreeMenu.add(item);
4159     }
4160   }
4161
4162   public boolean sortBy(AlignmentOrder alorder, String undoname)
4163   {
4164     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4165     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
4166     if (undoname != null)
4167     {
4168       addHistoryItem(new OrderCommand(undoname, oldOrder,
4169               viewport.getAlignment()));
4170     }
4171     alignPanel.paintAlignment(true);
4172     return true;
4173   }
4174
4175   /**
4176    * Work out whether the whole set of sequences or just the selected set will
4177    * be submitted for multiple alignment.
4178    * 
4179    */
4180   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
4181   {
4182     // Now, check we have enough sequences
4183     AlignmentView msa = null;
4184
4185     if ((viewport.getSelectionGroup() != null)
4186             && (viewport.getSelectionGroup().getSize() > 1))
4187     {
4188       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
4189       // some common interface!
4190       /*
4191        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
4192        * SequenceI[sz = seqs.getSize(false)];
4193        * 
4194        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
4195        * seqs.getSequenceAt(i); }
4196        */
4197       msa = viewport.getAlignmentView(true);
4198     }
4199     else if (viewport.getSelectionGroup() != null
4200             && viewport.getSelectionGroup().getSize() == 1)
4201     {
4202       int option = JOptionPane.showConfirmDialog(this,
4203               MessageManager.getString("warn.oneseq_msainput_selection"),
4204               MessageManager.getString("label.invalid_selection"),
4205               JOptionPane.OK_CANCEL_OPTION);
4206       if (option == JOptionPane.OK_OPTION)
4207       {
4208         msa = viewport.getAlignmentView(false);
4209       }
4210     }
4211     else
4212     {
4213       msa = viewport.getAlignmentView(false);
4214     }
4215     return msa;
4216   }
4217
4218   /**
4219    * Decides what is submitted to a secondary structure prediction service: the
4220    * first sequence in the alignment, or in the current selection, or, if the
4221    * alignment is 'aligned' (ie padded with gaps), then the currently selected
4222    * region or the whole alignment. (where the first sequence in the set is the
4223    * one that the prediction will be for).
4224    */
4225   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4226   {
4227     AlignmentView seqs = null;
4228
4229     if ((viewport.getSelectionGroup() != null)
4230             && (viewport.getSelectionGroup().getSize() > 0))
4231     {
4232       seqs = viewport.getAlignmentView(true);
4233     }
4234     else
4235     {
4236       seqs = viewport.getAlignmentView(false);
4237     }
4238     // limit sequences - JBPNote in future - could spawn multiple prediction
4239     // jobs
4240     // TODO: viewport.getAlignment().isAligned is a global state - the local
4241     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4242     if (!viewport.getAlignment().isAligned(false))
4243     {
4244       seqs.setSequences(new SeqCigar[]
4245       { seqs.getSequences()[0] });
4246       // TODO: if seqs.getSequences().length>1 then should really have warned
4247       // user!
4248
4249     }
4250     return seqs;
4251   }
4252
4253   /**
4254    * DOCUMENT ME!
4255    * 
4256    * @param e
4257    *          DOCUMENT ME!
4258    */
4259   @Override
4260   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
4261   {
4262     // Pick the tree file
4263     JalviewFileChooser chooser = new JalviewFileChooser(
4264             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4265     chooser.setFileView(new JalviewFileView());
4266     chooser.setDialogTitle(MessageManager
4267             .getString("label.select_newick_like_tree_file"));
4268     chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
4269
4270     int value = chooser.showOpenDialog(null);
4271
4272     if (value == JalviewFileChooser.APPROVE_OPTION)
4273     {
4274       String choice = chooser.getSelectedFile().getPath();
4275       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4276       jalview.io.NewickFile fin = null;
4277       try
4278       {
4279         fin = new jalview.io.NewickFile(choice, "File");
4280         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4281       } catch (Exception ex)
4282       {
4283         JOptionPane
4284                 .showMessageDialog(
4285                         Desktop.desktop,
4286                         ex.getMessage(),
4287                         MessageManager
4288                                 .getString("label.problem_reading_tree_file"),
4289                         JOptionPane.WARNING_MESSAGE);
4290         ex.printStackTrace();
4291       }
4292       if (fin != null && fin.hasWarningMessage())
4293       {
4294         JOptionPane.showMessageDialog(Desktop.desktop, fin
4295                 .getWarningMessage(), MessageManager
4296                 .getString("label.possible_problem_with_tree_file"),
4297                 JOptionPane.WARNING_MESSAGE);
4298       }
4299     }
4300   }
4301
4302   @Override
4303   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4304   {
4305     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4306   }
4307
4308   public TreePanel ShowNewickTree(NewickFile nf, String title)
4309   {
4310     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4311   }
4312
4313   public TreePanel ShowNewickTree(NewickFile nf, String title,
4314           AlignmentView input)
4315   {
4316     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4317   }
4318
4319   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4320           int h, int x, int y)
4321   {
4322     return ShowNewickTree(nf, title, null, w, h, x, y);
4323   }
4324
4325   /**
4326    * Add a treeviewer for the tree extracted from a newick file object to the
4327    * current alignment view
4328    * 
4329    * @param nf
4330    *          the tree
4331    * @param title
4332    *          tree viewer title
4333    * @param input
4334    *          Associated alignment input data (or null)
4335    * @param w
4336    *          width
4337    * @param h
4338    *          height
4339    * @param x
4340    *          position
4341    * @param y
4342    *          position
4343    * @return TreePanel handle
4344    */
4345   public TreePanel ShowNewickTree(NewickFile nf, String title,
4346           AlignmentView input, int w, int h, int x, int y)
4347   {
4348     TreePanel tp = null;
4349
4350     try
4351     {
4352       nf.parse();
4353
4354       if (nf.getTree() != null)
4355       {
4356         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4357
4358         tp.setSize(w, h);
4359
4360         if (x > 0 && y > 0)
4361         {
4362           tp.setLocation(x, y);
4363         }
4364
4365         Desktop.addInternalFrame(tp, title, w, h);
4366       }
4367     } catch (Exception ex)
4368     {
4369       ex.printStackTrace();
4370     }
4371
4372     return tp;
4373   }
4374
4375   private boolean buildingMenu = false;
4376
4377   /**
4378    * Generates menu items and listener event actions for web service clients
4379    * 
4380    */
4381   public void BuildWebServiceMenu()
4382   {
4383     while (buildingMenu)
4384     {
4385       try
4386       {
4387         System.err.println("Waiting for building menu to finish.");
4388         Thread.sleep(10);
4389       } catch (Exception e)
4390       {
4391       }
4392       ;
4393     }
4394     final AlignFrame me = this;
4395     buildingMenu = true;
4396     new Thread(new Runnable()
4397     {
4398       @Override
4399       public void run()
4400       {
4401         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
4402         try
4403         {
4404           System.err.println("Building ws menu again "
4405                   + Thread.currentThread());
4406           // TODO: add support for context dependent disabling of services based
4407           // on
4408           // alignment and current selection
4409           // TODO: add additional serviceHandle parameter to specify abstract
4410           // handler
4411           // class independently of AbstractName
4412           // TODO: add in rediscovery GUI function to restart discoverer
4413           // TODO: group services by location as well as function and/or
4414           // introduce
4415           // object broker mechanism.
4416           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4417           final IProgressIndicator af = me;
4418           final JMenu msawsmenu = new JMenu("Alignment");
4419           final JMenu secstrmenu = new JMenu(
4420                   "Secondary Structure Prediction");
4421           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4422           final JMenu analymenu = new JMenu("Analysis");
4423           final JMenu dismenu = new JMenu("Protein Disorder");
4424           // final JMenu msawsmenu = new
4425           // JMenu(MessageManager.getString("label.alignment"));
4426           // final JMenu secstrmenu = new
4427           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
4428           // final JMenu seqsrchmenu = new
4429           // JMenu(MessageManager.getString("label.sequence_database_search"));
4430           // final JMenu analymenu = new
4431           // JMenu(MessageManager.getString("label.analysis"));
4432           // final JMenu dismenu = new
4433           // JMenu(MessageManager.getString("label.protein_disorder"));
4434           // JAL-940 - only show secondary structure prediction services from
4435           // the legacy server
4436           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4437               // &&
4438           Discoverer.services != null && (Discoverer.services.size() > 0))
4439           {
4440             // TODO: refactor to allow list of AbstractName/Handler bindings to
4441             // be
4442             // stored or retrieved from elsewhere
4443             // No MSAWS used any more:
4444             // Vector msaws = null; // (Vector)
4445             // Discoverer.services.get("MsaWS");
4446             Vector secstrpr = (Vector) Discoverer.services
4447                     .get("SecStrPred");
4448             if (secstrpr != null)
4449             {
4450               // Add any secondary structure prediction services
4451               for (int i = 0, j = secstrpr.size(); i < j; i++)
4452               {
4453                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4454                         .get(i);
4455                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4456                         .getServiceClient(sh);
4457                 int p = secstrmenu.getItemCount();
4458                 impl.attachWSMenuEntry(secstrmenu, me);
4459                 int q = secstrmenu.getItemCount();
4460                 for (int litm = p; litm < q; litm++)
4461                 {
4462                   legacyItems.add(secstrmenu.getItem(litm));
4463                 }
4464               }
4465             }
4466           }
4467
4468           // Add all submenus in the order they should appear on the web
4469           // services menu
4470           wsmenu.add(msawsmenu);
4471           wsmenu.add(secstrmenu);
4472           wsmenu.add(dismenu);
4473           wsmenu.add(analymenu);
4474           // No search services yet
4475           // wsmenu.add(seqsrchmenu);
4476
4477           javax.swing.SwingUtilities.invokeLater(new Runnable()
4478           {
4479             @Override
4480             public void run()
4481             {
4482               try
4483               {
4484                 webService.removeAll();
4485                 // first, add discovered services onto the webservices menu
4486                 if (wsmenu.size() > 0)
4487                 {
4488                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4489                   {
4490                     webService.add(wsmenu.get(i));
4491                   }
4492                 }
4493                 else
4494                 {
4495                   webService.add(me.webServiceNoServices);
4496                 }
4497                 // TODO: move into separate menu builder class.
4498                 boolean new_sspred = false;
4499                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4500                 {
4501                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4502                   if (jws2servs != null)
4503                   {
4504                     if (jws2servs.hasServices())
4505                     {
4506                       jws2servs.attachWSMenuEntry(webService, me);
4507                       for (Jws2Instance sv : jws2servs.getServices())
4508                       {
4509                         if (sv.description.toLowerCase().contains("jpred"))
4510                         {
4511                           for (JMenuItem jmi : legacyItems)
4512                           {
4513                             jmi.setVisible(false);
4514                           }
4515                         }
4516                       }
4517
4518                     }
4519                     if (jws2servs.isRunning())
4520                     {
4521                       JMenuItem tm = new JMenuItem(
4522                               "Still discovering JABA Services");
4523                       tm.setEnabled(false);
4524                       webService.add(tm);
4525                     }
4526                   }
4527                 }
4528                 build_urlServiceMenu(me.webService);
4529                 build_fetchdbmenu(webService);
4530                 for (JMenu item : wsmenu)
4531                 {
4532                   if (item.getItemCount() == 0)
4533                   {
4534                     item.setEnabled(false);
4535                   }
4536                   else
4537                   {
4538                     item.setEnabled(true);
4539                   }
4540                 }
4541               } catch (Exception e)
4542               {
4543                 Cache.log
4544                         .debug("Exception during web service menu building process.",
4545                                 e);
4546               }
4547               ;
4548             }
4549           });
4550         } catch (Exception e)
4551         {
4552         }
4553         ;
4554
4555         buildingMenu = false;
4556       }
4557     }).start();
4558
4559   }
4560
4561   /**
4562    * construct any groupURL type service menu entries.
4563    * 
4564    * @param webService
4565    */
4566   private void build_urlServiceMenu(JMenu webService)
4567   {
4568     // TODO: remove this code when 2.7 is released
4569     // DEBUG - alignmentView
4570     /*
4571      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4572      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4573      * 
4574      * @Override public void actionPerformed(ActionEvent e) {
4575      * jalview.datamodel.AlignmentView
4576      * .testSelectionViews(af.viewport.getAlignment(),
4577      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4578      * 
4579      * }); webService.add(testAlView);
4580      */
4581     // TODO: refactor to RestClient discoverer and merge menu entries for
4582     // rest-style services with other types of analysis/calculation service
4583     // SHmmr test client - still being implemented.
4584     // DEBUG - alignmentView
4585
4586     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4587             .getRestClients())
4588     {
4589       client.attachWSMenuEntry(
4590               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4591               this);
4592     }
4593   }
4594
4595   /*
4596    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4597    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4598    * getProperty("LAST_DIRECTORY"));
4599    * 
4600    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4601    * to Vamsas file"); chooser.setToolTipText("Export");
4602    * 
4603    * int value = chooser.showSaveDialog(this);
4604    * 
4605    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4606    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4607    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4608    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4609    */
4610   /**
4611    * prototype of an automatically enabled/disabled analysis function
4612    * 
4613    */
4614   protected void setShowProductsEnabled()
4615   {
4616     SequenceI[] selection = viewport.getSequenceSelection();
4617     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4618             viewport.getAlignment().getDataset()))
4619     {
4620       showProducts.setEnabled(true);
4621
4622     }
4623     else
4624     {
4625       showProducts.setEnabled(false);
4626     }
4627   }
4628
4629   /**
4630    * search selection for sequence xRef products and build the show products
4631    * menu.
4632    * 
4633    * @param selection
4634    * @param dataset
4635    * @return true if showProducts menu should be enabled.
4636    */
4637   public boolean canShowProducts(SequenceI[] selection,
4638           boolean isRegionSelection, Alignment dataset)
4639   {
4640     boolean showp = false;
4641     try
4642     {
4643       showProducts.removeAll();
4644       final boolean dna = viewport.getAlignment().isNucleotide();
4645       final Alignment ds = dataset;
4646       String[] ptypes = (selection == null || selection.length == 0) ? null
4647               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4648       // Object[] prods =
4649       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4650       // selection, dataset, true);
4651       final SequenceI[] sel = selection;
4652       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4653       {
4654         showp = true;
4655         final boolean isRegSel = isRegionSelection;
4656         final AlignFrame af = this;
4657         final String source = ptypes[t];
4658         JMenuItem xtype = new JMenuItem(ptypes[t]);
4659         xtype.addActionListener(new ActionListener()
4660         {
4661
4662           @Override
4663           public void actionPerformed(ActionEvent e)
4664           {
4665             // TODO: new thread for this call with vis-delay
4666             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4667                     isRegSel, dna, source);
4668           }
4669
4670         });
4671         showProducts.add(xtype);
4672       }
4673       showProducts.setVisible(showp);
4674       showProducts.setEnabled(showp);
4675     } catch (Exception e)
4676     {
4677       jalview.bin.Cache.log
4678               .warn("canTranslate threw an exception - please report to help@jalview.org",
4679                       e);
4680       return false;
4681     }
4682     return showp;
4683   }
4684
4685   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4686           boolean isRegSel, boolean dna, String source)
4687   {
4688     final boolean fisRegSel = isRegSel;
4689     final boolean fdna = dna;
4690     final String fsrc = source;
4691     final AlignFrame ths = this;
4692     final SequenceI[] fsel = sel;
4693     Runnable foo = new Runnable()
4694     {
4695
4696       @Override
4697       public void run()
4698       {
4699         final long sttime = System.currentTimeMillis();
4700         ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
4701         try
4702         {
4703           Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
4704           // our local
4705           // dataset
4706           // reference
4707           Alignment prods = CrossRef
4708                   .findXrefSequences(fsel, fdna, fsrc, ds);
4709           if (prods != null)
4710           {
4711             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4712             for (int s = 0; s < sprods.length; s++)
4713             {
4714               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4715               if (ds.getSequences() == null
4716                       || !ds.getSequences().contains(
4717                               sprods[s].getDatasetSequence()))
4718               {
4719                 ds.addSequence(sprods[s].getDatasetSequence());
4720               }
4721               sprods[s].updatePDBIds();
4722             }
4723             Alignment al = new Alignment(sprods);
4724             AlignedCodonFrame[] cf = prods.getCodonFrames();
4725             al.setDataset(ds);
4726             for (int s = 0; cf != null && s < cf.length; s++)
4727             {
4728               al.addCodonFrame(cf[s]);
4729               cf[s] = null;
4730             }
4731             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4732                     DEFAULT_HEIGHT);
4733             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4734                     + " for " + ((fisRegSel) ? "selected region of " : "")
4735                     + getTitle();
4736             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4737                     DEFAULT_HEIGHT);
4738           }
4739           else
4740           {
4741             System.err.println("No Sequences generated for xRef type "
4742                     + fsrc);
4743           }
4744         } catch (Exception e)
4745         {
4746           jalview.bin.Cache.log.error(
4747                   "Exception when finding crossreferences", e);
4748         } catch (OutOfMemoryError e)
4749         {
4750           new OOMWarning("whilst fetching crossreferences", e);
4751         } catch (Error e)
4752         {
4753           jalview.bin.Cache.log.error("Error when finding crossreferences",
4754                   e);
4755         }
4756         ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
4757                 sttime);
4758       }
4759
4760     };
4761     Thread frunner = new Thread(foo);
4762     frunner.start();
4763   }
4764
4765   public boolean canShowTranslationProducts(SequenceI[] selection,
4766           AlignmentI alignment)
4767   {
4768     // old way
4769     try
4770     {
4771       return (jalview.analysis.Dna.canTranslate(selection,
4772               viewport.getViewAsVisibleContigs(true)));
4773     } catch (Exception e)
4774     {
4775       jalview.bin.Cache.log
4776               .warn("canTranslate threw an exception - please report to help@jalview.org",
4777                       e);
4778       return false;
4779     }
4780   }
4781
4782   @Override
4783   public void showProducts_actionPerformed(ActionEvent e)
4784   {
4785     // /////////////////////////////
4786     // Collect Data to be translated/transferred
4787
4788     SequenceI[] selection = viewport.getSequenceSelection();
4789     AlignmentI al = null;
4790     try
4791     {
4792       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4793               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4794               viewport.getAlignment().getDataset());
4795     } catch (Exception ex)
4796     {
4797       al = null;
4798       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4799     }
4800     if (al == null)
4801     {
4802       JOptionPane
4803               .showMessageDialog(
4804                       Desktop.desktop,
4805                       MessageManager
4806                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
4807                       MessageManager.getString("label.translation_failed"),
4808                       JOptionPane.WARNING_MESSAGE);
4809     }
4810     else
4811     {
4812       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4813       Desktop.addInternalFrame(af, MessageManager.formatMessage(
4814               "label.translation_of_params", new String[]
4815               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
4816     }
4817   }
4818
4819   @Override
4820   public void showTranslation_actionPerformed(ActionEvent e)
4821   {
4822     // /////////////////////////////
4823     // Collect Data to be translated/transferred
4824
4825     SequenceI[] selection = viewport.getSequenceSelection();
4826     String[] seqstring = viewport.getViewAsString(true);
4827     AlignmentI al = null;
4828     try
4829     {
4830       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4831               viewport.getViewAsVisibleContigs(true), viewport
4832                       .getGapCharacter(), viewport.getAlignment()
4833                       .getAlignmentAnnotation(), viewport.getAlignment()
4834                       .getWidth(), viewport.getAlignment().getDataset());
4835     } catch (Exception ex)
4836     {
4837       al = null;
4838       jalview.bin.Cache.log.error(
4839               "Exception during translation. Please report this !", ex);
4840       JOptionPane
4841               .showMessageDialog(
4842                       Desktop.desktop,
4843                       MessageManager
4844                               .getString("label.error_when_translating_sequences_submit_bug_report"),
4845                       MessageManager
4846                               .getString("label.implementation_error")
4847                               + MessageManager
4848                                       .getString("translation_failed"),
4849                       JOptionPane.ERROR_MESSAGE);
4850       return;
4851     }
4852     if (al == null)
4853     {
4854       JOptionPane
4855               .showMessageDialog(
4856                       Desktop.desktop,
4857                       MessageManager
4858                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
4859                       MessageManager.getString("label.translation_failed"),
4860                       JOptionPane.WARNING_MESSAGE);
4861     }
4862     else
4863     {
4864       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4865       Desktop.addInternalFrame(af, MessageManager.formatMessage(
4866               "label.translation_of_params", new String[]
4867               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
4868     }
4869   }
4870
4871   /**
4872    * Try to load a features file onto the alignment.
4873    * 
4874    * @param file
4875    *          contents or path to retrieve file
4876    * @param type
4877    *          access mode of file (see jalview.io.AlignFile)
4878    * @return true if features file was parsed corectly.
4879    */
4880   public boolean parseFeaturesFile(String file, String type)
4881   {
4882     boolean featuresFile = false;
4883     try
4884     {
4885       featuresFile = new FeaturesFile(file, type).parse(viewport
4886               .getAlignment().getDataset(), alignPanel.getSeqPanel().seqCanvas
4887               .getFeatureRenderer().getFeatureColours(), false,
4888               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4889     } catch (Exception ex)
4890     {
4891       ex.printStackTrace();
4892     }
4893
4894     if (featuresFile)
4895     {
4896       viewport.setShowSequenceFeatures(true);
4897       showSeqFeatures.setSelected(true);
4898       if (alignPanel.getSeqPanel().seqCanvas.fr != null)
4899       {
4900         // update the min/max ranges where necessary
4901         alignPanel.getSeqPanel().seqCanvas.fr.findAllFeatures(true);
4902       }
4903       if (featureSettings != null)
4904       {
4905         featureSettings.setTableData();
4906       }
4907       alignPanel.paintAlignment(true);
4908     }
4909
4910     return featuresFile;
4911   }
4912
4913   @Override
4914   public void dragEnter(DropTargetDragEvent evt)
4915   {
4916   }
4917
4918   @Override
4919   public void dragExit(DropTargetEvent evt)
4920   {
4921   }
4922
4923   @Override
4924   public void dragOver(DropTargetDragEvent evt)
4925   {
4926   }
4927
4928   @Override
4929   public void dropActionChanged(DropTargetDragEvent evt)
4930   {
4931   }
4932
4933   @Override
4934   public void drop(DropTargetDropEvent evt)
4935   {
4936     Transferable t = evt.getTransferable();
4937     java.util.List files = null;
4938
4939     try
4940     {
4941       DataFlavor uriListFlavor = new DataFlavor(
4942               "text/uri-list;class=java.lang.String");
4943       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4944       {
4945         // Works on Windows and MacOSX
4946         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4947         files = (java.util.List) t
4948                 .getTransferData(DataFlavor.javaFileListFlavor);
4949       }
4950       else if (t.isDataFlavorSupported(uriListFlavor))
4951       {
4952         // This is used by Unix drag system
4953         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4954         String data = (String) t.getTransferData(uriListFlavor);
4955         files = new java.util.ArrayList(1);
4956         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4957                 data, "\r\n"); st.hasMoreTokens();)
4958         {
4959           String s = st.nextToken();
4960           if (s.startsWith("#"))
4961           {
4962             // the line is a comment (as per the RFC 2483)
4963             continue;
4964           }
4965
4966           java.net.URI uri = new java.net.URI(s);
4967           // check to see if we can handle this kind of URI
4968           if (uri.getScheme().toLowerCase().startsWith("http"))
4969           {
4970             files.add(uri.toString());
4971           }
4972           else
4973           {
4974             // otherwise preserve old behaviour: catch all for file objects
4975             java.io.File file = new java.io.File(uri);
4976             files.add(file.toString());
4977           }
4978         }
4979       }
4980     } catch (Exception e)
4981     {
4982       e.printStackTrace();
4983     }
4984     if (files != null)
4985     {
4986       try
4987       {
4988         // check to see if any of these files have names matching sequences in
4989         // the alignment
4990         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4991                 .getAlignment().getSequencesArray());
4992         /**
4993          * Object[] { String,SequenceI}
4994          */
4995         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4996         ArrayList<String> filesnotmatched = new ArrayList<String>();
4997         for (int i = 0; i < files.size(); i++)
4998         {
4999           String file = files.get(i).toString();
5000           String pdbfn = "";
5001           String protocol = FormatAdapter.checkProtocol(file);
5002           if (protocol == jalview.io.FormatAdapter.FILE)
5003           {
5004             File fl = new File(file);
5005             pdbfn = fl.getName();
5006           }
5007           else if (protocol == jalview.io.FormatAdapter.URL)
5008           {
5009             URL url = new URL(file);
5010             pdbfn = url.getFile();
5011           }
5012           if (pdbfn.length() > 0)
5013           {
5014             // attempt to find a match in the alignment
5015             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
5016             int l = 0, c = pdbfn.indexOf(".");
5017             while (mtch == null && c != -1)
5018             {
5019               do
5020               {
5021                 l = c;
5022               } while ((c = pdbfn.indexOf(".", l)) > l);
5023               if (l > -1)
5024               {
5025                 pdbfn = pdbfn.substring(0, l);
5026               }
5027               mtch = idm.findAllIdMatches(pdbfn);
5028             }
5029             if (mtch != null)
5030             {
5031               String type = null;
5032               try
5033               {
5034                 type = new IdentifyFile().Identify(file, protocol);
5035               } catch (Exception ex)
5036               {
5037                 type = null;
5038               }
5039               if (type != null)
5040               {
5041                 if (type.equalsIgnoreCase("PDB"))
5042                 {
5043                   filesmatched.add(new Object[]
5044                   { file, protocol, mtch });
5045                   continue;
5046                 }
5047               }
5048             }
5049             // File wasn't named like one of the sequences or wasn't a PDB file.
5050             filesnotmatched.add(file);
5051           }
5052         }
5053         int assocfiles = 0;
5054         if (filesmatched.size() > 0)
5055         {
5056           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
5057                   || JOptionPane
5058                           .showConfirmDialog(
5059                                   this,
5060                                   MessageManager
5061                                           .formatMessage(
5062                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
5063                                                   new String[]
5064                                                   { Integer.valueOf(
5065                                                           filesmatched
5066                                                                   .size())
5067                                                           .toString() }),
5068                                   MessageManager
5069                                           .getString("label.automatically_associate_pdb_files_by_name"),
5070                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
5071
5072           {
5073             for (Object[] fm : filesmatched)
5074             {
5075               // try and associate
5076               // TODO: may want to set a standard ID naming formalism for
5077               // associating PDB files which have no IDs.
5078               for (SequenceI toassoc : (SequenceI[]) fm[2])
5079               {
5080                 PDBEntry pe = new AssociatePdbFileWithSeq()
5081                         .associatePdbWithSeq((String) fm[0],
5082                                 (String) fm[1], toassoc, false,
5083                                 Desktop.instance);
5084                 if (pe != null)
5085                 {
5086                   System.err.println("Associated file : "
5087                           + ((String) fm[0]) + " with "
5088                           + toassoc.getDisplayId(true));
5089                   assocfiles++;
5090                 }
5091               }
5092               alignPanel.paintAlignment(true);
5093             }
5094           }
5095         }
5096         if (filesnotmatched.size() > 0)
5097         {
5098           if (assocfiles > 0
5099                   && (Cache.getDefault(
5100                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
5101                           .showConfirmDialog(
5102                                   this,
5103                                   "<html>"+MessageManager
5104                                           .formatMessage(
5105                                                   "label.ignore_unmatched_dropped_files_info",
5106                                                   new String[]
5107                                                   { Integer.valueOf(
5108                                                           filesnotmatched
5109                                                                   .size())
5110                                                           .toString() })+"</html>",
5111                                   MessageManager
5112                                           .getString("label.ignore_unmatched_dropped_files"),
5113                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
5114           {
5115             return;
5116           }
5117           for (String fn : filesnotmatched)
5118           {
5119             loadJalviewDataFile(fn, null, null, null);
5120           }
5121
5122         }
5123       } catch (Exception ex)
5124       {
5125         ex.printStackTrace();
5126       }
5127     }
5128   }
5129
5130   /**
5131    * Attempt to load a "dropped" file or URL string: First by testing whether
5132    * it's and Annotation file, then a JNet file, and finally a features file. If
5133    * all are false then the user may have dropped an alignment file onto this
5134    * AlignFrame.
5135    * 
5136    * @param file
5137    *          either a filename or a URL string.
5138    */
5139   public void loadJalviewDataFile(String file, String protocol,
5140           String format, SequenceI assocSeq)
5141   {
5142     try
5143     {
5144       if (protocol == null)
5145       {
5146         protocol = jalview.io.FormatAdapter.checkProtocol(file);
5147       }
5148       // if the file isn't identified, or not positively identified as some
5149       // other filetype (PFAM is default unidentified alignment file type) then
5150       // try to parse as annotation.
5151       boolean isAnnotation = (format == null || format
5152               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
5153               .annotateAlignmentView(viewport, file, protocol)
5154               : false;
5155
5156       if (!isAnnotation)
5157       {
5158         // first see if its a T-COFFEE score file
5159         TCoffeeScoreFile tcf = null;
5160         try
5161         {
5162           tcf = new TCoffeeScoreFile(file, protocol);
5163           if (tcf.isValid())
5164           {
5165             if (tcf.annotateAlignment(viewport.getAlignment(), true))
5166             {
5167               tcoffeeColour.setEnabled(true);
5168               tcoffeeColour.setSelected(true);
5169               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
5170               isAnnotation = true;
5171               statusBar
5172                       .setText(MessageManager
5173                               .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
5174             }
5175             else
5176             {
5177               // some problem - if no warning its probable that the ID matching
5178               // process didn't work
5179               JOptionPane
5180                       .showMessageDialog(
5181                               Desktop.desktop,
5182                               tcf.getWarningMessage() == null ? MessageManager
5183                                       .getString("label.check_file_matches_sequence_ids_alignment")
5184                                       : tcf.getWarningMessage(),
5185                               MessageManager
5186                                       .getString("label.problem_reading_tcoffee_score_file"),
5187                               JOptionPane.WARNING_MESSAGE);
5188             }
5189           }
5190           else
5191           {
5192             tcf = null;
5193           }
5194         } catch (Exception x)
5195         {
5196           Cache.log
5197                   .debug("Exception when processing data source as T-COFFEE score file",
5198                           x);
5199           tcf = null;
5200         }
5201         if (tcf == null)
5202         {
5203           // try to see if its a JNet 'concise' style annotation file *before*
5204           // we
5205           // try to parse it as a features file
5206           if (format == null)
5207           {
5208             format = new IdentifyFile().Identify(file, protocol);
5209           }
5210           if (format.equalsIgnoreCase("JnetFile"))
5211           {
5212             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
5213                     file, protocol);
5214             new JnetAnnotationMaker().add_annotation(predictions,
5215                     viewport.getAlignment(), 0, false);
5216             isAnnotation = true;
5217           }
5218           else
5219           {
5220             /*
5221              * if (format.equalsIgnoreCase("PDB")) {
5222              * 
5223              * String pdbfn = ""; // try to match up filename with sequence id
5224              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
5225              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
5226              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
5227              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
5228              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5229              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
5230              * // attempt to find a match in the alignment SequenceI mtch =
5231              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
5232              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
5233              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
5234              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
5235              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
5236              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
5237              * { System.err.println("Associated file : " + file + " with " +
5238              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
5239              * TODO: maybe need to load as normal otherwise return; } }
5240              */
5241             // try to parse it as a features file
5242             boolean isGroupsFile = parseFeaturesFile(file, protocol);
5243             // if it wasn't a features file then we just treat it as a general
5244             // alignment file to load into the current view.
5245             if (!isGroupsFile)
5246             {
5247               new FileLoader().LoadFile(viewport, file, protocol, format);
5248             }
5249             else
5250             {
5251               alignPanel.paintAlignment(true);
5252             }
5253           }
5254         }
5255       }
5256       if (isAnnotation)
5257       {
5258
5259         alignPanel.adjustAnnotationHeight();
5260         viewport.updateSequenceIdColours();
5261         buildSortByAnnotationScoresMenu();
5262         alignPanel.paintAlignment(true);
5263       }
5264     } catch (Exception ex)
5265     {
5266       ex.printStackTrace();
5267     } catch (OutOfMemoryError oom)
5268     {
5269       try
5270       {
5271         System.gc();
5272       } catch (Exception x)
5273       {
5274       }
5275       ;
5276       new OOMWarning(
5277               "loading data "
5278                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5279                               : "using " + protocol + " from " + file)
5280                               : ".")
5281                       + (format != null ? "(parsing as '" + format
5282                               + "' file)" : ""), oom, Desktop.desktop);
5283     }
5284   }
5285
5286   @Override
5287   public void tabSelectionChanged(int index)
5288   {
5289     if (index > -1)
5290     {
5291       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
5292       viewport = alignPanel.av;
5293       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
5294       setMenusFromViewport(viewport);
5295     }
5296   }
5297
5298   @Override
5299   public void tabbedPane_mousePressed(MouseEvent e)
5300   {
5301     if (SwingUtilities.isRightMouseButton(e))
5302     {
5303       String reply = JOptionPane.showInternalInputDialog(this,
5304               MessageManager.getString("label.enter_view_name"),
5305               MessageManager.getString("label.enter_view_name"),
5306               JOptionPane.QUESTION_MESSAGE);
5307
5308       if (reply != null)
5309       {
5310         viewport.viewName = reply;
5311         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5312       }
5313     }
5314   }
5315
5316   public AlignViewport getCurrentView()
5317   {
5318     return viewport;
5319   }
5320
5321   /**
5322    * Open the dialog for regex description parsing.
5323    */
5324   @Override
5325   protected void extractScores_actionPerformed(ActionEvent e)
5326   {
5327     ParseProperties pp = new jalview.analysis.ParseProperties(
5328             viewport.getAlignment());
5329     // TODO: verify regex and introduce GUI dialog for version 2.5
5330     // if (pp.getScoresFromDescription("col", "score column ",
5331     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5332     // true)>0)
5333     if (pp.getScoresFromDescription("description column",
5334             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5335     {
5336       buildSortByAnnotationScoresMenu();
5337     }
5338   }
5339
5340   /*
5341    * (non-Javadoc)
5342    * 
5343    * @see
5344    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5345    * )
5346    */
5347   @Override
5348   protected void showDbRefs_actionPerformed(ActionEvent e)
5349   {
5350     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
5351   }
5352
5353   /*
5354    * (non-Javadoc)
5355    * 
5356    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5357    * ActionEvent)
5358    */
5359   @Override
5360   protected void showNpFeats_actionPerformed(ActionEvent e)
5361   {
5362     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
5363   }
5364
5365   /**
5366    * find the viewport amongst the tabs in this alignment frame and close that
5367    * tab
5368    * 
5369    * @param av
5370    */
5371   public boolean closeView(AlignViewport av)
5372   {
5373     if (viewport == av)
5374     {
5375       this.closeMenuItem_actionPerformed(false);
5376       return true;
5377     }
5378     Component[] comp = tabbedPane.getComponents();
5379     for (int i = 0; comp != null && i < comp.length; i++)
5380     {
5381       if (comp[i] instanceof AlignmentPanel)
5382       {
5383         if (((AlignmentPanel) comp[i]).av == av)
5384         {
5385           // close the view.
5386           closeView((AlignmentPanel) comp[i]);
5387           return true;
5388         }
5389       }
5390     }
5391     return false;
5392   }
5393
5394   protected void build_fetchdbmenu(JMenu webService)
5395   {
5396     // Temporary hack - DBRef Fetcher always top level ws entry.
5397     // TODO We probably want to store a sequence database checklist in
5398     // preferences and have checkboxes.. rather than individual sources selected
5399     // here
5400     final JMenu rfetch = new JMenu(
5401             MessageManager.getString("action.fetch_db_references"));
5402     rfetch.setToolTipText(MessageManager
5403             .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
5404     webService.add(rfetch);
5405
5406     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
5407             MessageManager.getString("option.trim_retrieved_seqs"));
5408     trimrs.setToolTipText(MessageManager
5409             .getString("label.trim_retrieved_sequences"));
5410     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
5411     trimrs.addActionListener(new ActionListener()
5412     {
5413       @Override
5414       public void actionPerformed(ActionEvent e)
5415       {
5416         trimrs.setSelected(trimrs.isSelected());
5417         Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
5418                 Boolean.valueOf(trimrs.isSelected()).toString());
5419       };
5420     });
5421     rfetch.add(trimrs);
5422     JMenuItem fetchr = new JMenuItem(
5423             MessageManager.getString("label.standard_databases"));
5424     fetchr.setToolTipText(MessageManager
5425             .getString("label.fetch_embl_uniprot"));
5426     fetchr.addActionListener(new ActionListener()
5427     {
5428
5429       @Override
5430       public void actionPerformed(ActionEvent e)
5431       {
5432         new Thread(new Runnable()
5433         {
5434
5435           @Override
5436           public void run()
5437           {
5438             new jalview.ws.DBRefFetcher(alignPanel.av
5439                     .getSequenceSelection(), alignPanel.alignFrame)
5440                     .fetchDBRefs(false);
5441           }
5442         }).start();
5443
5444       }
5445
5446     });
5447     rfetch.add(fetchr);
5448     final AlignFrame me = this;
5449     new Thread(new Runnable()
5450     {
5451       @Override
5452       public void run()
5453       {
5454         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5455                 .getSequenceFetcherSingleton(me);
5456         javax.swing.SwingUtilities.invokeLater(new Runnable()
5457         {
5458           @Override
5459           public void run()
5460           {
5461             String[] dbclasses = sf.getOrderedSupportedSources();
5462             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5463             // jalview.util.QuickSort.sort(otherdb, otherdb);
5464             List<DbSourceProxy> otherdb;
5465             JMenu dfetch = new JMenu();
5466             JMenu ifetch = new JMenu();
5467             JMenuItem fetchr = null;
5468             int comp = 0, icomp = 0, mcomp = 15;
5469             String mname = null;
5470             int dbi = 0;
5471             for (String dbclass : dbclasses)
5472             {
5473               otherdb = sf.getSourceProxy(dbclass);
5474               // add a single entry for this class, or submenu allowing 'fetch
5475               // all' or pick one
5476               if (otherdb == null || otherdb.size() < 1)
5477               {
5478                 continue;
5479               }
5480               // List<DbSourceProxy> dbs=otherdb;
5481               // otherdb=new ArrayList<DbSourceProxy>();
5482               // for (DbSourceProxy db:dbs)
5483               // {
5484               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5485               // }
5486               if (mname == null)
5487               {
5488                 mname = "From " + dbclass;
5489               }
5490               if (otherdb.size() == 1)
5491               {
5492                 final DbSourceProxy[] dassource = otherdb
5493                         .toArray(new DbSourceProxy[0]);
5494                 DbSourceProxy src = otherdb.get(0);
5495                 fetchr = new JMenuItem(src.getDbSource());
5496                 fetchr.addActionListener(new ActionListener()
5497                 {
5498
5499                   @Override
5500                   public void actionPerformed(ActionEvent e)
5501                   {
5502                     new Thread(new Runnable()
5503                     {
5504
5505                       @Override
5506                       public void run()
5507                       {
5508                         new jalview.ws.DBRefFetcher(alignPanel.av
5509                                 .getSequenceSelection(),
5510                                 alignPanel.alignFrame, dassource)
5511                                 .fetchDBRefs(false);
5512                       }
5513                     }).start();
5514                   }
5515
5516                 });
5517                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
5518                 dfetch.add(fetchr);
5519                 comp++;
5520               }
5521               else
5522               {
5523                 final DbSourceProxy[] dassource = otherdb
5524                         .toArray(new DbSourceProxy[0]);
5525                 // fetch all entry
5526                 DbSourceProxy src = otherdb.get(0);
5527                 fetchr = new JMenuItem(MessageManager.formatMessage(
5528                         "label.fetch_all_param", new String[]
5529                         { src.getDbSource() }));
5530                 fetchr.addActionListener(new ActionListener()
5531                 {
5532                   @Override
5533                   public void actionPerformed(ActionEvent e)
5534                   {
5535                     new Thread(new Runnable()
5536                     {
5537
5538                       @Override
5539                       public void run()
5540                       {
5541                         new jalview.ws.DBRefFetcher(alignPanel.av
5542                                 .getSequenceSelection(),
5543                                 alignPanel.alignFrame, dassource)
5544                                 .fetchDBRefs(false);
5545                       }
5546                     }).start();
5547                   }
5548                 });
5549
5550                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
5551                 dfetch.add(fetchr);
5552                 comp++;
5553                 // and then build the rest of the individual menus
5554                 ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()}));
5555                 icomp = 0;
5556                 String imname = null;
5557                 int i = 0;
5558                 for (DbSourceProxy sproxy : otherdb)
5559                 {
5560                   String dbname = sproxy.getDbName();
5561                   String sname = dbname.length() > 5 ? dbname.substring(0,
5562                           5) + "..." : dbname;
5563                   String msname = dbname.length() > 10 ? dbname.substring(
5564                           0, 10) + "..." : dbname;
5565                   if (imname == null)
5566                   {
5567                     imname = MessageManager.formatMessage("label.from_msname", new String[]{sname});
5568                   }
5569                   fetchr = new JMenuItem(msname);
5570                   final DbSourceProxy[] dassrc =
5571                   { sproxy };
5572                   fetchr.addActionListener(new ActionListener()
5573                   {
5574
5575                     @Override
5576                     public void actionPerformed(ActionEvent e)
5577                     {
5578                       new Thread(new Runnable()
5579                       {
5580
5581                         @Override
5582                         public void run()
5583                         {
5584                           new jalview.ws.DBRefFetcher(alignPanel.av
5585                                   .getSequenceSelection(),
5586                                   alignPanel.alignFrame, dassrc)
5587                                   .fetchDBRefs(false);
5588                         }
5589                       }).start();
5590                     }
5591
5592                   });
5593                   fetchr.setToolTipText("<html>"
5594                           + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
5595                   ifetch.add(fetchr);
5596                   ++i;
5597                   if (++icomp >= mcomp || i == (otherdb.size()))
5598                   {
5599                     ifetch.setText(MessageManager.formatMessage(
5600                             "label.source_to_target", imname, sname));
5601                     dfetch.add(ifetch);
5602                     ifetch = new JMenu();
5603                     imname = null;
5604                     icomp = 0;
5605                     comp++;
5606                   }
5607                 }
5608               }
5609               ++dbi;
5610               if (comp >= mcomp || dbi >= (dbclasses.length))
5611               {
5612                 dfetch.setText(MessageManager.formatMessage(
5613                         "label.source_to_target", mname, dbclass));
5614                 rfetch.add(dfetch);
5615                 dfetch = new JMenu();
5616                 mname = null;
5617                 comp = 0;
5618               }
5619             }
5620           }
5621         });
5622       }
5623     }).start();
5624
5625   }
5626
5627   /**
5628    * Left justify the whole alignment.
5629    */
5630   @Override
5631   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5632   {
5633     AlignmentI al = viewport.getAlignment();
5634     al.justify(false);
5635     viewport.firePropertyChange("alignment", null, al);
5636   }
5637
5638   /**
5639    * Right justify the whole alignment.
5640    */
5641   @Override
5642   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5643   {
5644     AlignmentI al = viewport.getAlignment();
5645     al.justify(true);
5646     viewport.firePropertyChange("alignment", null, al);
5647   }
5648
5649   public void setShowSeqFeatures(boolean b)
5650   {
5651     showSeqFeatures.setSelected(true);
5652     viewport.setShowSequenceFeatures(true);
5653   }
5654
5655   /*
5656    * (non-Javadoc)
5657    * 
5658    * @see
5659    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5660    * awt.event.ActionEvent)
5661    */
5662   @Override
5663   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5664   {
5665     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5666     alignPanel.paintAlignment(true);
5667   }
5668
5669   /*
5670    * (non-Javadoc)
5671    * 
5672    * @see
5673    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5674    * .ActionEvent)
5675    */
5676   @Override
5677   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5678   {
5679     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5680     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5681
5682   }
5683
5684   /*
5685    * (non-Javadoc)
5686    * 
5687    * @see
5688    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5689    * .event.ActionEvent)
5690    */
5691   @Override
5692   protected void showGroupConservation_actionPerformed(ActionEvent e)
5693   {
5694     viewport.setShowGroupConservation(showGroupConservation.getState());
5695     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5696   }
5697
5698   /*
5699    * (non-Javadoc)
5700    * 
5701    * @see
5702    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5703    * .event.ActionEvent)
5704    */
5705   @Override
5706   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5707   {
5708     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5709     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5710   }
5711
5712   /*
5713    * (non-Javadoc)
5714    * 
5715    * @see
5716    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5717    * .event.ActionEvent)
5718    */
5719   @Override
5720   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5721   {
5722     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5723     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5724   }
5725
5726   @Override
5727   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5728   {
5729     showSequenceLogo.setState(true);
5730     viewport.setShowSequenceLogo(true);
5731     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5732     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5733   }
5734
5735   @Override
5736   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5737   {
5738     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5739   }
5740
5741   /*
5742    * (non-Javadoc)
5743    * 
5744    * @see
5745    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5746    * .event.ActionEvent)
5747    */
5748   @Override
5749   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5750   {
5751     if (avc.makeGroupsFromSelection())
5752     {
5753       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5754       alignPanel.updateAnnotation();
5755       alignPanel.paintAlignment(true);
5756     }
5757   }
5758   public void clearAlignmentSeqRep()
5759   {
5760     // TODO refactor alignmentseqrep to controller
5761     if (viewport.getAlignment().hasSeqrep()) {
5762       viewport.getAlignment().setSeqrep(null);
5763       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5764       alignPanel.updateAnnotation();
5765       alignPanel.paintAlignment(true);
5766     }
5767   }
5768
5769   @Override
5770   protected void createGroup_actionPerformed(ActionEvent e)
5771   {
5772     if (avc.createGroup())
5773     {
5774       alignPanel.alignmentChanged();
5775     }
5776   }
5777
5778   @Override
5779   protected void unGroup_actionPerformed(ActionEvent e)
5780   {
5781     if (avc.unGroup())
5782     {
5783       alignPanel.alignmentChanged();
5784     }
5785   }
5786
5787   /**
5788    * make the given alignmentPanel the currently selected tab
5789    * 
5790    * @param alignmentPanel
5791    */
5792   public void setDisplayedView(AlignmentPanel alignmentPanel)
5793   {
5794     if (!viewport.getSequenceSetId().equals(
5795             alignmentPanel.av.getSequenceSetId()))
5796     {
5797       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
5798     }
5799     if (tabbedPane != null
5800             & alignPanels.indexOf(alignmentPanel) != tabbedPane
5801                     .getSelectedIndex())
5802     {
5803       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5804     }
5805   }
5806
5807   /**
5808    * Action on selection of menu options to Show or Hide annotations.
5809    * 
5810    * @param visible
5811    * @param forSequences
5812    *          update sequence-related annotations
5813    * @param forAlignment
5814    *          update non-sequence-related annotations
5815    */
5816   @Override
5817   protected void setAnnotationsVisibility(boolean visible,
5818           boolean forSequences, boolean forAlignment)
5819   {
5820     for (AlignmentAnnotation aa : alignPanel.getAlignment()
5821             .getAlignmentAnnotation())
5822     {
5823       boolean apply = (aa.sequenceRef == null && forAlignment)
5824               || (aa.sequenceRef != null && forSequences);
5825       if (apply)
5826       {
5827         aa.visible = visible;
5828       }
5829     }
5830     alignPanel.validateAnnotationDimensions(false);
5831     alignPanel.alignmentChanged();
5832   }
5833
5834   /**
5835    * Store selected annotation sort order for the view and repaint.
5836    */
5837   @Override
5838   protected void sortAnnotations_actionPerformed()
5839   {
5840     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
5841     this.alignPanel.av
5842             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
5843     alignPanel.paintAlignment(true);
5844   }
5845
5846   /**
5847    * 
5848    * @return alignment panels in this alignemnt frame
5849    */
5850   public List<AlignmentViewPanel> getAlignPanels()
5851   {
5852     return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
5853   }
5854 }
5855
5856 class PrintThread extends Thread
5857 {
5858   AlignmentPanel ap;
5859
5860   public PrintThread(AlignmentPanel ap)
5861   {
5862     this.ap = ap;
5863   }
5864
5865   static PageFormat pf;
5866
5867   @Override
5868   public void run()
5869   {
5870     PrinterJob printJob = PrinterJob.getPrinterJob();
5871
5872     if (pf != null)
5873     {
5874       printJob.setPrintable(ap, pf);
5875     }
5876     else
5877     {
5878       printJob.setPrintable(ap);
5879     }
5880
5881     if (printJob.printDialog())
5882     {
5883       try
5884       {
5885         printJob.print();
5886       } catch (Exception PrintException)
5887       {
5888         PrintException.printStackTrace();
5889       }
5890     }
5891   }
5892 }