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