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