2a804ad6ea1abdb60f50a4ce0132ad1abbc17cce
[jalview.git] / src / jalview / gui / AlignFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.analysis.AAFrequency;
24 import jalview.analysis.AlignmentSorter;
25 import jalview.analysis.AlignmentUtils;
26 import jalview.analysis.Conservation;
27 import jalview.analysis.CrossRef;
28 import jalview.analysis.NJTree;
29 import jalview.analysis.ParseProperties;
30 import jalview.analysis.SequenceIdMatcher;
31 import jalview.api.AlignViewControllerGuiI;
32 import jalview.api.AlignViewControllerI;
33 import jalview.api.analysis.ScoreModelI;
34 import jalview.bin.Cache;
35 import jalview.commands.CommandI;
36 import jalview.commands.EditCommand;
37 import jalview.commands.EditCommand.Action;
38 import jalview.commands.OrderCommand;
39 import jalview.commands.RemoveGapColCommand;
40 import jalview.commands.RemoveGapsCommand;
41 import jalview.commands.SlideSequencesCommand;
42 import jalview.commands.TrimRegionCommand;
43 import jalview.datamodel.AlignedCodonFrame;
44 import jalview.datamodel.Alignment;
45 import jalview.datamodel.AlignmentAnnotation;
46 import jalview.datamodel.AlignmentI;
47 import jalview.datamodel.AlignmentOrder;
48 import jalview.datamodel.AlignmentView;
49 import jalview.datamodel.ColumnSelection;
50 import jalview.datamodel.PDBEntry;
51 import jalview.datamodel.SeqCigar;
52 import jalview.datamodel.Sequence;
53 import jalview.datamodel.SequenceGroup;
54 import jalview.datamodel.SequenceI;
55 import jalview.io.AlignmentProperties;
56 import jalview.io.AnnotationFile;
57 import jalview.io.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   public void createImageMap(File file, String image)
1236   {
1237     alignPanel.makePNGImageMap(file, image);
1238   }
1239
1240   /**
1241    * DOCUMENT ME!
1242    * 
1243    * @param e
1244    *          DOCUMENT ME!
1245    */
1246   @Override
1247   public void createPNG(File f)
1248   {
1249     alignPanel.makePNG(f);
1250   }
1251
1252   /**
1253    * DOCUMENT ME!
1254    * 
1255    * @param e
1256    *          DOCUMENT ME!
1257    */
1258   @Override
1259   public void createEPS(File f)
1260   {
1261     alignPanel.makeEPS(f);
1262   }
1263
1264   public void createSVG(File f)
1265   {
1266     alignPanel.makeSVG(f);
1267   }
1268   @Override
1269   public void pageSetup_actionPerformed(ActionEvent e)
1270   {
1271     PrinterJob printJob = PrinterJob.getPrinterJob();
1272     PrintThread.pf = printJob.pageDialog(printJob.defaultPage());
1273   }
1274
1275   /**
1276    * DOCUMENT ME!
1277    * 
1278    * @param e
1279    *          DOCUMENT ME!
1280    */
1281   @Override
1282   public void printMenuItem_actionPerformed(ActionEvent e)
1283   {
1284     // Putting in a thread avoids Swing painting problems
1285     PrintThread thread = new PrintThread(alignPanel);
1286     thread.start();
1287   }
1288
1289   @Override
1290   public void exportFeatures_actionPerformed(ActionEvent e)
1291   {
1292     new AnnotationExporter().exportFeatures(alignPanel);
1293   }
1294
1295   @Override
1296   public void exportAnnotations_actionPerformed(ActionEvent e)
1297   {
1298     new AnnotationExporter().exportAnnotations(alignPanel,
1299             viewport.showAnnotation ? viewport.getAlignment()
1300                     .getAlignmentAnnotation() : null, viewport
1301                     .getAlignment().getGroups(), ((Alignment) viewport
1302                     .getAlignment()).alignmentProperties);
1303   }
1304
1305   @Override
1306   public void associatedData_actionPerformed(ActionEvent e)
1307   {
1308     // Pick the tree file
1309     JalviewFileChooser chooser = new JalviewFileChooser(
1310             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1311     chooser.setFileView(new JalviewFileView());
1312     chooser.setDialogTitle(MessageManager
1313             .getString("label.load_jalview_annotations"));
1314     chooser.setToolTipText(MessageManager
1315             .getString("label.load_jalview_annotations"));
1316
1317     int value = chooser.showOpenDialog(null);
1318
1319     if (value == JalviewFileChooser.APPROVE_OPTION)
1320     {
1321       String choice = chooser.getSelectedFile().getPath();
1322       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
1323       loadJalviewDataFile(choice, null, null, null);
1324     }
1325
1326   }
1327
1328   /**
1329    * Close the current view or all views in the alignment frame. If the frame
1330    * only contains one view then the alignment will be removed from memory.
1331    * 
1332    * @param closeAllTabs
1333    */
1334   @Override
1335   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1336   {
1337     if (alignPanels != null && alignPanels.size() < 2)
1338     {
1339       closeAllTabs = true;
1340     }
1341
1342     try
1343     {
1344       if (alignPanels != null)
1345       {
1346         if (closeAllTabs)
1347         {
1348           if (this.isClosed())
1349           {
1350             // really close all the windows - otherwise wait till
1351             // setClosed(true) is called
1352             for (int i = 0; i < alignPanels.size(); i++)
1353             {
1354               AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
1355               ap.closePanel();
1356             }
1357           }
1358         }
1359         else
1360         {
1361           closeView(alignPanel);
1362         }
1363       }
1364
1365       if (closeAllTabs)
1366       {
1367         this.setClosed(true);
1368       }
1369     } catch (Exception ex)
1370     {
1371       ex.printStackTrace();
1372     }
1373   }
1374
1375   /**
1376    * close alignPanel2 and shuffle tabs appropriately.
1377    * 
1378    * @param alignPanel2
1379    */
1380   public void closeView(AlignmentPanel alignPanel2)
1381   {
1382     int index = tabbedPane.getSelectedIndex();
1383     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
1384     alignPanels.removeElement(alignPanel2);
1385     // Unnecessary
1386     // if (viewport == alignPanel2.av)
1387     // {
1388     // viewport = null;
1389     // }
1390     alignPanel2.closePanel();
1391     alignPanel2 = null;
1392
1393     tabbedPane.removeTabAt(closedindex);
1394     tabbedPane.validate();
1395
1396     if (index > closedindex || index == tabbedPane.getTabCount())
1397     {
1398       // modify currently selected tab index if necessary.
1399       index--;
1400     }
1401
1402     this.tabSelectionChanged(index);
1403   }
1404
1405   /**
1406    * DOCUMENT ME!
1407    */
1408   void updateEditMenuBar()
1409   {
1410
1411     if (viewport.historyList.size() > 0)
1412     {
1413       undoMenuItem.setEnabled(true);
1414       CommandI command = viewport.historyList.peek();
1415       undoMenuItem.setText(MessageManager.formatMessage(
1416               "label.undo_command", new String[]
1417               { command.getDescription() }));
1418     }
1419     else
1420     {
1421       undoMenuItem.setEnabled(false);
1422       undoMenuItem.setText(MessageManager.getString("action.undo"));
1423     }
1424
1425     if (viewport.redoList.size() > 0)
1426     {
1427       redoMenuItem.setEnabled(true);
1428
1429       CommandI command = viewport.redoList.peek();
1430       redoMenuItem.setText(MessageManager.formatMessage(
1431               "label.redo_command", new String[]
1432               { command.getDescription() }));
1433     }
1434     else
1435     {
1436       redoMenuItem.setEnabled(false);
1437       redoMenuItem.setText(MessageManager.getString("action.redo"));
1438     }
1439   }
1440
1441   public void addHistoryItem(CommandI command)
1442   {
1443     if (command.getSize() > 0)
1444     {
1445       viewport.historyList.push(command);
1446       viewport.redoList.clear();
1447       updateEditMenuBar();
1448       viewport.updateHiddenColumns();
1449       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
1450       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1451       // viewport.getColumnSelection()
1452       // .getHiddenColumns().size() > 0);
1453     }
1454   }
1455
1456   /**
1457    * 
1458    * @return alignment objects for all views
1459    */
1460   AlignmentI[] getViewAlignments()
1461   {
1462     if (alignPanels != null)
1463     {
1464       Enumeration e = alignPanels.elements();
1465       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1466       for (int i = 0; e.hasMoreElements(); i++)
1467       {
1468         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1469       }
1470       return als;
1471     }
1472     if (viewport != null)
1473     {
1474       return new AlignmentI[]
1475       { viewport.getAlignment() };
1476     }
1477     return null;
1478   }
1479
1480   /**
1481    * DOCUMENT ME!
1482    * 
1483    * @param e
1484    *          DOCUMENT ME!
1485    */
1486   @Override
1487   protected void undoMenuItem_actionPerformed(ActionEvent e)
1488   {
1489     if (viewport.historyList.empty())
1490     {
1491       return;
1492     }
1493     CommandI command = viewport.historyList.pop();
1494     viewport.redoList.push(command);
1495     command.undoCommand(getViewAlignments());
1496
1497     AlignViewport originalSource = getOriginatingSource(command);
1498     updateEditMenuBar();
1499
1500     if (originalSource != null)
1501     {
1502       if (originalSource != viewport)
1503       {
1504         Cache.log
1505                 .warn("Implementation worry: mismatch of viewport origin for undo");
1506       }
1507       originalSource.updateHiddenColumns();
1508       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1509       // null
1510       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1511       // viewport.getColumnSelection()
1512       // .getHiddenColumns().size() > 0);
1513       originalSource.firePropertyChange("alignment", null, originalSource
1514               .getAlignment().getSequences());
1515     }
1516   }
1517
1518   /**
1519    * DOCUMENT ME!
1520    * 
1521    * @param e
1522    *          DOCUMENT ME!
1523    */
1524   @Override
1525   protected void redoMenuItem_actionPerformed(ActionEvent e)
1526   {
1527     if (viewport.redoList.size() < 1)
1528     {
1529       return;
1530     }
1531
1532     CommandI command = viewport.redoList.pop();
1533     viewport.historyList.push(command);
1534     command.doCommand(getViewAlignments());
1535
1536     AlignViewport originalSource = getOriginatingSource(command);
1537     updateEditMenuBar();
1538
1539     if (originalSource != null)
1540     {
1541
1542       if (originalSource != viewport)
1543       {
1544         Cache.log
1545                 .warn("Implementation worry: mismatch of viewport origin for redo");
1546       }
1547       originalSource.updateHiddenColumns();
1548       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1549       // null
1550       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1551       // viewport.getColumnSelection()
1552       // .getHiddenColumns().size() > 0);
1553       originalSource.firePropertyChange("alignment", null, originalSource
1554               .getAlignment().getSequences());
1555     }
1556   }
1557
1558   AlignViewport getOriginatingSource(CommandI command)
1559   {
1560     AlignViewport originalSource = null;
1561     // For sequence removal and addition, we need to fire
1562     // the property change event FROM the viewport where the
1563     // original alignment was altered
1564     AlignmentI al = null;
1565     if (command instanceof EditCommand)
1566     {
1567       EditCommand editCommand = (EditCommand) command;
1568       al = editCommand.getAlignment();
1569       Vector comps = (Vector) PaintRefresher.components.get(viewport
1570               .getSequenceSetId());
1571
1572       for (int i = 0; i < comps.size(); i++)
1573       {
1574         if (comps.elementAt(i) instanceof AlignmentPanel)
1575         {
1576           if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
1577           {
1578             originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
1579             break;
1580           }
1581         }
1582       }
1583     }
1584
1585     if (originalSource == null)
1586     {
1587       // The original view is closed, we must validate
1588       // the current view against the closed view first
1589       if (al != null)
1590       {
1591         PaintRefresher.validateSequences(al, viewport.getAlignment());
1592       }
1593
1594       originalSource = viewport;
1595     }
1596
1597     return originalSource;
1598   }
1599
1600   /**
1601    * DOCUMENT ME!
1602    * 
1603    * @param up
1604    *          DOCUMENT ME!
1605    */
1606   public void moveSelectedSequences(boolean up)
1607   {
1608     SequenceGroup sg = viewport.getSelectionGroup();
1609
1610     if (sg == null)
1611     {
1612       return;
1613     }
1614     viewport.getAlignment().moveSelectedSequencesByOne(sg,
1615             viewport.getHiddenRepSequences(), up);
1616     alignPanel.paintAlignment(true);
1617   }
1618
1619   synchronized void slideSequences(boolean right, int size)
1620   {
1621     List<SequenceI> sg = new Vector();
1622     if (viewport.cursorMode)
1623     {
1624       sg.add(viewport.getAlignment().getSequenceAt(
1625               alignPanel.seqPanel.seqCanvas.cursorY));
1626     }
1627     else if (viewport.getSelectionGroup() != null
1628             && viewport.getSelectionGroup().getSize() != viewport
1629                     .getAlignment().getHeight())
1630     {
1631       sg = viewport.getSelectionGroup().getSequences(
1632               viewport.getHiddenRepSequences());
1633     }
1634
1635     if (sg.size() < 1)
1636     {
1637       return;
1638     }
1639
1640     Vector invertGroup = new Vector();
1641
1642     for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
1643     {
1644       if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
1645       {
1646         invertGroup.add(viewport.getAlignment().getSequenceAt(i));
1647       }
1648     }
1649
1650     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
1651
1652     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1653     for (int i = 0; i < invertGroup.size(); i++)
1654     {
1655       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1656     }
1657
1658     SlideSequencesCommand ssc;
1659     if (right)
1660     {
1661       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
1662               size, viewport.getGapCharacter());
1663     }
1664     else
1665     {
1666       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
1667               size, viewport.getGapCharacter());
1668     }
1669
1670     int groupAdjustment = 0;
1671     if (ssc.getGapsInsertedBegin() && right)
1672     {
1673       if (viewport.cursorMode)
1674       {
1675         alignPanel.seqPanel.moveCursor(size, 0);
1676       }
1677       else
1678       {
1679         groupAdjustment = size;
1680       }
1681     }
1682     else if (!ssc.getGapsInsertedBegin() && !right)
1683     {
1684       if (viewport.cursorMode)
1685       {
1686         alignPanel.seqPanel.moveCursor(-size, 0);
1687       }
1688       else
1689       {
1690         groupAdjustment = -size;
1691       }
1692     }
1693
1694     if (groupAdjustment != 0)
1695     {
1696       viewport.getSelectionGroup().setStartRes(
1697               viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1698       viewport.getSelectionGroup().setEndRes(
1699               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1700     }
1701
1702     boolean appendHistoryItem = false;
1703     if (viewport.historyList != null && viewport.historyList.size() > 0
1704             && viewport.historyList.peek() instanceof SlideSequencesCommand)
1705     {
1706       appendHistoryItem = ssc
1707               .appendSlideCommand((SlideSequencesCommand) viewport.historyList
1708                       .peek());
1709     }
1710
1711     if (!appendHistoryItem)
1712     {
1713       addHistoryItem(ssc);
1714     }
1715
1716     repaint();
1717   }
1718
1719   /**
1720    * DOCUMENT ME!
1721    * 
1722    * @param e
1723    *          DOCUMENT ME!
1724    */
1725   @Override
1726   protected void copy_actionPerformed(ActionEvent e)
1727   {
1728     System.gc();
1729     if (viewport.getSelectionGroup() == null)
1730     {
1731       return;
1732     }
1733     // TODO: preserve the ordering of displayed alignment annotation in any
1734     // internal paste (particularly sequence associated annotation)
1735     SequenceI[] seqs = viewport.getSelectionAsNewSequence();
1736     String[] omitHidden = null;
1737
1738     if (viewport.hasHiddenColumns())
1739     {
1740       omitHidden = viewport.getViewAsString(true);
1741     }
1742
1743     String output = new FormatAdapter().formatSequences("Fasta", seqs,
1744             omitHidden);
1745
1746     StringSelection ss = new StringSelection(output);
1747
1748     try
1749     {
1750       jalview.gui.Desktop.internalCopy = true;
1751       // Its really worth setting the clipboard contents
1752       // to empty before setting the large StringSelection!!
1753       Toolkit.getDefaultToolkit().getSystemClipboard()
1754               .setContents(new StringSelection(""), null);
1755
1756       Toolkit.getDefaultToolkit().getSystemClipboard()
1757               .setContents(ss, Desktop.instance);
1758     } catch (OutOfMemoryError er)
1759     {
1760       new OOMWarning("copying region", er);
1761       return;
1762     }
1763
1764     Vector hiddenColumns = null;
1765     if (viewport.hasHiddenColumns())
1766     {
1767       hiddenColumns = new Vector();
1768       int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
1769               .getSelectionGroup().getEndRes();
1770       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
1771               .size(); i++)
1772       {
1773         int[] region = (int[]) viewport.getColumnSelection()
1774                 .getHiddenColumns().elementAt(i);
1775         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
1776         {
1777           hiddenColumns.addElement(new int[]
1778           { region[0] - hiddenOffset, region[1] - hiddenOffset });
1779         }
1780       }
1781     }
1782
1783     Desktop.jalviewClipboard = new Object[]
1784     { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
1785     statusBar.setText(MessageManager.formatMessage(
1786             "label.copied_sequences_to_clipboard", new String[]
1787             { Integer.valueOf(seqs.length).toString() }));
1788   }
1789
1790   /**
1791    * DOCUMENT ME!
1792    * 
1793    * @param e
1794    *          DOCUMENT ME!
1795    */
1796   @Override
1797   protected void pasteNew_actionPerformed(ActionEvent e)
1798   {
1799     paste(true);
1800   }
1801
1802   /**
1803    * DOCUMENT ME!
1804    * 
1805    * @param e
1806    *          DOCUMENT ME!
1807    */
1808   @Override
1809   protected void pasteThis_actionPerformed(ActionEvent e)
1810   {
1811     paste(false);
1812   }
1813
1814   /**
1815    * Paste contents of Jalview clipboard
1816    * 
1817    * @param newAlignment
1818    *          true to paste to a new alignment, otherwise add to this.
1819    */
1820   void paste(boolean newAlignment)
1821   {
1822     boolean externalPaste = true;
1823     try
1824     {
1825       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1826       Transferable contents = c.getContents(this);
1827
1828       if (contents == null)
1829       {
1830         return;
1831       }
1832
1833       String str, format;
1834       try
1835       {
1836         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1837         if (str.length() < 1)
1838         {
1839           return;
1840         }
1841
1842         format = new IdentifyFile().Identify(str, "Paste");
1843
1844       } catch (OutOfMemoryError er)
1845       {
1846         new OOMWarning("Out of memory pasting sequences!!", er);
1847         return;
1848       }
1849
1850       SequenceI[] sequences;
1851       boolean annotationAdded = false;
1852       AlignmentI alignment = null;
1853
1854       if (Desktop.jalviewClipboard != null)
1855       {
1856         // The clipboard was filled from within Jalview, we must use the
1857         // sequences
1858         // And dataset from the copied alignment
1859         SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
1860         // be doubly sure that we create *new* sequence objects.
1861         sequences = new SequenceI[newseq.length];
1862         for (int i = 0; i < newseq.length; i++)
1863         {
1864           sequences[i] = new Sequence(newseq[i]);
1865         }
1866         alignment = new Alignment(sequences);
1867         externalPaste = false;
1868       }
1869       else
1870       {
1871         // parse the clipboard as an alignment.
1872         alignment = new FormatAdapter().readFile(str, "Paste", format);
1873         sequences = alignment.getSequencesArray();
1874       }
1875
1876       int alwidth = 0;
1877       ArrayList<Integer> newGraphGroups = new ArrayList<Integer>();
1878       int fgroup = -1;
1879
1880       if (newAlignment)
1881       {
1882
1883         if (Desktop.jalviewClipboard != null)
1884         {
1885           // dataset is inherited
1886           alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
1887         }
1888         else
1889         {
1890           // new dataset is constructed
1891           alignment.setDataset(null);
1892         }
1893         alwidth = alignment.getWidth() + 1;
1894       }
1895       else
1896       {
1897         AlignmentI pastedal = alignment; // preserve pasted alignment object
1898         // Add pasted sequences and dataset into existing alignment.
1899         alignment = viewport.getAlignment();
1900         alwidth = alignment.getWidth() + 1;
1901         // decide if we need to import sequences from an existing dataset
1902         boolean importDs = Desktop.jalviewClipboard != null
1903                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1904         // importDs==true instructs us to copy over new dataset sequences from
1905         // an existing alignment
1906         Vector newDs = (importDs) ? new Vector() : null; // used to create
1907         // minimum dataset set
1908
1909         for (int i = 0; i < sequences.length; i++)
1910         {
1911           if (importDs)
1912           {
1913             newDs.addElement(null);
1914           }
1915           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1916           // paste
1917           if (importDs && ds != null)
1918           {
1919             if (!newDs.contains(ds))
1920             {
1921               newDs.setElementAt(ds, i);
1922               ds = new Sequence(ds);
1923               // update with new dataset sequence
1924               sequences[i].setDatasetSequence(ds);
1925             }
1926             else
1927             {
1928               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1929             }
1930           }
1931           else
1932           {
1933             // copy and derive new dataset sequence
1934             sequences[i] = sequences[i].deriveSequence();
1935             alignment.getDataset().addSequence(
1936                     sequences[i].getDatasetSequence());
1937             // TODO: avoid creation of duplicate dataset sequences with a
1938             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1939           }
1940           alignment.addSequence(sequences[i]); // merges dataset
1941         }
1942         if (newDs != null)
1943         {
1944           newDs.clear(); // tidy up
1945         }
1946         if (alignment.getAlignmentAnnotation() != null)
1947         {
1948           for (AlignmentAnnotation alan : alignment
1949                   .getAlignmentAnnotation())
1950           {
1951             if (alan.graphGroup > fgroup)
1952             {
1953               fgroup = alan.graphGroup;
1954             }
1955           }
1956         }
1957         if (pastedal.getAlignmentAnnotation() != null)
1958         {
1959           // Add any annotation attached to alignment.
1960           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1961           for (int i = 0; i < alann.length; i++)
1962           {
1963             annotationAdded = true;
1964             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
1965             {
1966               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1967               if (newann.graphGroup > -1)
1968               {
1969                 if (newGraphGroups.size() <= newann.graphGroup
1970                         || newGraphGroups.get(newann.graphGroup) == null)
1971                 {
1972                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
1973                   {
1974                     newGraphGroups.add(q, null);
1975                   }
1976                   newGraphGroups.set(newann.graphGroup, new Integer(
1977                           ++fgroup));
1978                 }
1979                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
1980                         .intValue();
1981               }
1982
1983               newann.padAnnotation(alwidth);
1984               alignment.addAnnotation(newann);
1985             }
1986           }
1987         }
1988       }
1989       if (!newAlignment)
1990       {
1991         // /////
1992         // ADD HISTORY ITEM
1993         //
1994         addHistoryItem(new EditCommand(
1995                 MessageManager.getString("label.add_sequences"),
1996                 Action.PASTE,
1997                 sequences, 0, alignment.getWidth(), alignment));
1998       }
1999       // Add any annotations attached to sequences
2000       for (int i = 0; i < sequences.length; i++)
2001       {
2002         if (sequences[i].getAnnotation() != null)
2003         {
2004           AlignmentAnnotation newann;
2005           for (int a = 0; a < sequences[i].getAnnotation().length; a++)
2006           {
2007             annotationAdded = true;
2008             newann = sequences[i].getAnnotation()[a];
2009             newann.adjustForAlignment();
2010             newann.padAnnotation(alwidth);
2011             if (newann.graphGroup > -1)
2012             {
2013               if (newann.graphGroup > -1)
2014               {
2015                 if (newGraphGroups.size() <= newann.graphGroup
2016                         || newGraphGroups.get(newann.graphGroup) == null)
2017                 {
2018                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
2019                   {
2020                     newGraphGroups.add(q, null);
2021                   }
2022                   newGraphGroups.set(newann.graphGroup, new Integer(
2023                           ++fgroup));
2024                 }
2025                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
2026                         .intValue();
2027               }
2028             }
2029             alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
2030             // was
2031             // duplicated
2032             // earlier
2033             alignment
2034                     .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
2035           }
2036         }
2037       }
2038       if (!newAlignment)
2039       {
2040
2041         // propagate alignment changed.
2042         viewport.setEndSeq(alignment.getHeight());
2043         if (annotationAdded)
2044         {
2045           // Duplicate sequence annotation in all views.
2046           AlignmentI[] alview = this.getViewAlignments();
2047           for (int i = 0; i < sequences.length; i++)
2048           {
2049             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
2050             if (sann == null)
2051             {
2052               continue;
2053             }
2054             for (int avnum = 0; avnum < alview.length; avnum++)
2055             {
2056               if (alview[avnum] != alignment)
2057               {
2058                 // duplicate in a view other than the one with input focus
2059                 int avwidth = alview[avnum].getWidth() + 1;
2060                 // this relies on sann being preserved after we
2061                 // modify the sequence's annotation array for each duplication
2062                 for (int a = 0; a < sann.length; a++)
2063                 {
2064                   AlignmentAnnotation newann = new AlignmentAnnotation(
2065                           sann[a]);
2066                   sequences[i].addAlignmentAnnotation(newann);
2067                   newann.padAnnotation(avwidth);
2068                   alview[avnum].addAnnotation(newann); // annotation was
2069                   // duplicated earlier
2070                   // TODO JAL-1145 graphGroups are not updated for sequence
2071                   // annotation added to several views. This may cause
2072                   // strangeness
2073                   alview[avnum].setAnnotationIndex(newann, a);
2074                 }
2075               }
2076             }
2077           }
2078           buildSortByAnnotationScoresMenu();
2079         }
2080         viewport.firePropertyChange("alignment", null,
2081                 alignment.getSequences());
2082         if (alignPanels != null)
2083         {
2084           for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
2085           {
2086             ap.validateAnnotationDimensions(false);
2087           }
2088         }
2089         else
2090         {
2091           alignPanel.validateAnnotationDimensions(false);
2092         }
2093
2094       }
2095       else
2096       {
2097         AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2098                 DEFAULT_HEIGHT);
2099         String newtitle = new String("Copied sequences");
2100
2101         if (Desktop.jalviewClipboard != null
2102                 && Desktop.jalviewClipboard[2] != null)
2103         {
2104           Vector hc = (Vector) Desktop.jalviewClipboard[2];
2105           for (int i = 0; i < hc.size(); i++)
2106           {
2107             int[] region = (int[]) hc.elementAt(i);
2108             af.viewport.hideColumns(region[0], region[1]);
2109           }
2110         }
2111
2112         // >>>This is a fix for the moment, until a better solution is
2113         // found!!<<<
2114         af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2115                 .transferSettings(
2116                         alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2117
2118         // TODO: maintain provenance of an alignment, rather than just make the
2119         // title a concatenation of operations.
2120         if (!externalPaste)
2121         {
2122           if (title.startsWith("Copied sequences"))
2123           {
2124             newtitle = title;
2125           }
2126           else
2127           {
2128             newtitle = newtitle.concat("- from " + title);
2129           }
2130         }
2131         else
2132         {
2133           newtitle = new String("Pasted sequences");
2134         }
2135
2136         Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
2137                 DEFAULT_HEIGHT);
2138
2139       }
2140
2141     } catch (Exception ex)
2142     {
2143       ex.printStackTrace();
2144       System.out.println("Exception whilst pasting: " + ex);
2145       // could be anything being pasted in here
2146     }
2147
2148   }
2149
2150   @Override
2151   protected void expand_newalign(ActionEvent e)
2152   {
2153     try
2154     {
2155       AlignmentI alignment = AlignmentUtils.expandContext(getViewport()
2156               .getAlignment(), -1);
2157       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2158               DEFAULT_HEIGHT);
2159       String newtitle = new String("Flanking alignment");
2160
2161       if (Desktop.jalviewClipboard != null
2162               && Desktop.jalviewClipboard[2] != null)
2163       {
2164         Vector hc = (Vector) Desktop.jalviewClipboard[2];
2165         for (int i = 0; i < hc.size(); i++)
2166         {
2167           int[] region = (int[]) hc.elementAt(i);
2168           af.viewport.hideColumns(region[0], region[1]);
2169         }
2170       }
2171
2172       // >>>This is a fix for the moment, until a better solution is
2173       // found!!<<<
2174       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2175               .transferSettings(
2176                       alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2177
2178       // TODO: maintain provenance of an alignment, rather than just make the
2179       // title a concatenation of operations.
2180       {
2181         if (title.startsWith("Copied sequences"))
2182         {
2183           newtitle = title;
2184         }
2185         else
2186         {
2187           newtitle = newtitle.concat("- from " + title);
2188         }
2189       }
2190
2191       Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
2192
2193     } catch (Exception ex)
2194     {
2195       ex.printStackTrace();
2196       System.out.println("Exception whilst pasting: " + ex);
2197       // could be anything being pasted in here
2198     } catch (OutOfMemoryError oom)
2199     {
2200       new OOMWarning("Viewing flanking region of alignment", oom);
2201     }
2202   }
2203
2204   /**
2205    * DOCUMENT ME!
2206    * 
2207    * @param e
2208    *          DOCUMENT ME!
2209    */
2210   @Override
2211   protected void cut_actionPerformed(ActionEvent e)
2212   {
2213     copy_actionPerformed(null);
2214     delete_actionPerformed(null);
2215   }
2216
2217   /**
2218    * DOCUMENT ME!
2219    * 
2220    * @param e
2221    *          DOCUMENT ME!
2222    */
2223   @Override
2224   protected void delete_actionPerformed(ActionEvent evt)
2225   {
2226
2227     SequenceGroup sg = viewport.getSelectionGroup();
2228     if (sg == null)
2229     {
2230       return;
2231     }
2232
2233     List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
2234     SequenceI seq;
2235     for (int i = 0; i < sg.getSize(); i++)
2236     {
2237       seq = sg.getSequenceAt(i);
2238       seqs.add(seq);
2239     }
2240
2241     // If the cut affects all sequences, warn, remove highlighted columns
2242     if (sg.getSize() == viewport.getAlignment().getHeight())
2243     {
2244       int confirm = JOptionPane.showConfirmDialog(this,
2245               MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
2246               MessageManager.getString("label.delete_all"), // $NON-NLS-1$
2247               JOptionPane.OK_CANCEL_OPTION);
2248
2249       if (confirm == JOptionPane.CANCEL_OPTION
2250               || confirm == JOptionPane.CLOSED_OPTION)
2251       {
2252         return;
2253       }
2254       viewport.getColumnSelection().removeElements(sg.getStartRes(),
2255               sg.getEndRes() + 1);
2256     }
2257
2258     SequenceI[] cut = new SequenceI[seqs.size()];
2259     for (int i = 0; i < seqs.size(); i++)
2260     {
2261       cut[i] = seqs.get(i);
2262     }
2263
2264     /*
2265      * //ADD HISTORY ITEM
2266      */
2267     addHistoryItem(new EditCommand(
2268             MessageManager.getString("label.cut_sequences"), Action.CUT,
2269             cut, 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 if (viewport.getSelectionGroup() != null
4188             && viewport.getSelectionGroup().getSize() == 1)
4189     {
4190       int option = JOptionPane
4191               .showConfirmDialog(
4192 this,
4193                       "More than one sequece group selection is required for this Job, click \n'Cancel' to edit your selection or 'Ok' to submit the entire sequence.",
4194                       "Invalid selection",
4195                       JOptionPane.OK_CANCEL_OPTION);
4196       if (option == JOptionPane.OK_OPTION)
4197       {
4198         msa = viewport.getAlignmentView(false);
4199       }
4200
4201     }
4202     else
4203     {
4204       /*
4205        * Vector seqs = viewport.getAlignment().getSequences();
4206        * 
4207        * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
4208        * 
4209        * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
4210        * seqs.elementAt(i); } }
4211        */
4212       msa = viewport.getAlignmentView(false);
4213     }
4214     return msa;
4215   }
4216
4217   /**
4218    * Decides what is submitted to a secondary structure prediction service: the
4219    * first sequence in the alignment, or in the current selection, or, if the
4220    * alignment is 'aligned' (ie padded with gaps), then the currently selected
4221    * region or the whole alignment. (where the first sequence in the set is the
4222    * one that the prediction will be for).
4223    */
4224   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4225   {
4226     AlignmentView seqs = null;
4227
4228     if ((viewport.getSelectionGroup() != null)
4229             && (viewport.getSelectionGroup().getSize() > 0))
4230     {
4231       seqs = viewport.getAlignmentView(true);
4232     }
4233     else
4234     {
4235       seqs = viewport.getAlignmentView(false);
4236     }
4237     // limit sequences - JBPNote in future - could spawn multiple prediction
4238     // jobs
4239     // TODO: viewport.getAlignment().isAligned is a global state - the local
4240     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4241     if (!viewport.getAlignment().isAligned(false))
4242     {
4243       seqs.setSequences(new SeqCigar[]
4244       { seqs.getSequences()[0] });
4245       // TODO: if seqs.getSequences().length>1 then should really have warned
4246       // user!
4247
4248     }
4249     return seqs;
4250   }
4251
4252   /**
4253    * DOCUMENT ME!
4254    * 
4255    * @param e
4256    *          DOCUMENT ME!
4257    */
4258   @Override
4259   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
4260   {
4261     // Pick the tree file
4262     JalviewFileChooser chooser = new JalviewFileChooser(
4263             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4264     chooser.setFileView(new JalviewFileView());
4265     chooser.setDialogTitle(MessageManager
4266             .getString("label.select_newick_like_tree_file"));
4267     chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
4268
4269     int value = chooser.showOpenDialog(null);
4270
4271     if (value == JalviewFileChooser.APPROVE_OPTION)
4272     {
4273       String choice = chooser.getSelectedFile().getPath();
4274       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4275       jalview.io.NewickFile fin = null;
4276       try
4277       {
4278         fin = new jalview.io.NewickFile(choice, "File");
4279         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4280       } catch (Exception ex)
4281       {
4282         JOptionPane
4283                 .showMessageDialog(
4284                         Desktop.desktop,
4285                         ex.getMessage(),
4286                         MessageManager
4287                                 .getString("label.problem_reading_tree_file"),
4288                         JOptionPane.WARNING_MESSAGE);
4289         ex.printStackTrace();
4290       }
4291       if (fin != null && fin.hasWarningMessage())
4292       {
4293         JOptionPane.showMessageDialog(Desktop.desktop, fin
4294                 .getWarningMessage(), MessageManager
4295                 .getString("label.possible_problem_with_tree_file"),
4296                 JOptionPane.WARNING_MESSAGE);
4297       }
4298     }
4299   }
4300
4301   @Override
4302   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4303   {
4304     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4305   }
4306
4307   public TreePanel ShowNewickTree(NewickFile nf, String title)
4308   {
4309     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4310   }
4311
4312   public TreePanel ShowNewickTree(NewickFile nf, String title,
4313           AlignmentView input)
4314   {
4315     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4316   }
4317
4318   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4319           int h, int x, int y)
4320   {
4321     return ShowNewickTree(nf, title, null, w, h, x, y);
4322   }
4323
4324   /**
4325    * Add a treeviewer for the tree extracted from a newick file object to the
4326    * current alignment view
4327    * 
4328    * @param nf
4329    *          the tree
4330    * @param title
4331    *          tree viewer title
4332    * @param input
4333    *          Associated alignment input data (or null)
4334    * @param w
4335    *          width
4336    * @param h
4337    *          height
4338    * @param x
4339    *          position
4340    * @param y
4341    *          position
4342    * @return TreePanel handle
4343    */
4344   public TreePanel ShowNewickTree(NewickFile nf, String title,
4345           AlignmentView input, int w, int h, int x, int y)
4346   {
4347     TreePanel tp = null;
4348
4349     try
4350     {
4351       nf.parse();
4352
4353       if (nf.getTree() != null)
4354       {
4355         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4356
4357         tp.setSize(w, h);
4358
4359         if (x > 0 && y > 0)
4360         {
4361           tp.setLocation(x, y);
4362         }
4363
4364         Desktop.addInternalFrame(tp, title, w, h);
4365       }
4366     } catch (Exception ex)
4367     {
4368       ex.printStackTrace();
4369     }
4370
4371     return tp;
4372   }
4373
4374   private boolean buildingMenu = false;
4375
4376   /**
4377    * Generates menu items and listener event actions for web service clients
4378    * 
4379    */
4380   public void BuildWebServiceMenu()
4381   {
4382     while (buildingMenu)
4383     {
4384       try
4385       {
4386         System.err.println("Waiting for building menu to finish.");
4387         Thread.sleep(10);
4388       } catch (Exception e)
4389       {
4390       }
4391       ;
4392     }
4393     final AlignFrame me = this;
4394     buildingMenu = true;
4395     new Thread(new Runnable()
4396     {
4397       @Override
4398       public void run()
4399       {
4400         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
4401         try
4402         {
4403           System.err.println("Building ws menu again "
4404                   + Thread.currentThread());
4405           // TODO: add support for context dependent disabling of services based
4406           // on
4407           // alignment and current selection
4408           // TODO: add additional serviceHandle parameter to specify abstract
4409           // handler
4410           // class independently of AbstractName
4411           // TODO: add in rediscovery GUI function to restart discoverer
4412           // TODO: group services by location as well as function and/or
4413           // introduce
4414           // object broker mechanism.
4415           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4416           final IProgressIndicator af = me;
4417           final JMenu msawsmenu = new JMenu("Alignment");
4418           final JMenu secstrmenu = new JMenu(
4419                   "Secondary Structure Prediction");
4420           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4421           final JMenu analymenu = new JMenu("Analysis");
4422           final JMenu dismenu = new JMenu("Protein Disorder");
4423           // final JMenu msawsmenu = new
4424           // JMenu(MessageManager.getString("label.alignment"));
4425           // final JMenu secstrmenu = new
4426           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
4427           // final JMenu seqsrchmenu = new
4428           // JMenu(MessageManager.getString("label.sequence_database_search"));
4429           // final JMenu analymenu = new
4430           // JMenu(MessageManager.getString("label.analysis"));
4431           // final JMenu dismenu = new
4432           // JMenu(MessageManager.getString("label.protein_disorder"));
4433           // JAL-940 - only show secondary structure prediction services from
4434           // the legacy server
4435           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4436               // &&
4437           Discoverer.services != null && (Discoverer.services.size() > 0))
4438           {
4439             // TODO: refactor to allow list of AbstractName/Handler bindings to
4440             // be
4441             // stored or retrieved from elsewhere
4442             // No MSAWS used any more:
4443             // Vector msaws = null; // (Vector)
4444             // Discoverer.services.get("MsaWS");
4445             Vector secstrpr = (Vector) Discoverer.services
4446                     .get("SecStrPred");
4447             if (secstrpr != null)
4448             {
4449               // Add any secondary structure prediction services
4450               for (int i = 0, j = secstrpr.size(); i < j; i++)
4451               {
4452                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4453                         .get(i);
4454                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4455                         .getServiceClient(sh);
4456                 int p = secstrmenu.getItemCount();
4457                 impl.attachWSMenuEntry(secstrmenu, me);
4458                 int q = secstrmenu.getItemCount();
4459                 for (int litm = p; litm < q; litm++)
4460                 {
4461                   legacyItems.add(secstrmenu.getItem(litm));
4462                 }
4463               }
4464             }
4465           }
4466
4467           // Add all submenus in the order they should appear on the web
4468           // services menu
4469           wsmenu.add(msawsmenu);
4470           wsmenu.add(secstrmenu);
4471           wsmenu.add(dismenu);
4472           wsmenu.add(analymenu);
4473           // No search services yet
4474           // wsmenu.add(seqsrchmenu);
4475
4476           javax.swing.SwingUtilities.invokeLater(new Runnable()
4477           {
4478             @Override
4479             public void run()
4480             {
4481               try
4482               {
4483                 webService.removeAll();
4484                 // first, add discovered services onto the webservices menu
4485                 if (wsmenu.size() > 0)
4486                 {
4487                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4488                   {
4489                     webService.add(wsmenu.get(i));
4490                   }
4491                 }
4492                 else
4493                 {
4494                   webService.add(me.webServiceNoServices);
4495                 }
4496                 // TODO: move into separate menu builder class.
4497                 boolean new_sspred = false;
4498                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4499                 {
4500                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4501                   if (jws2servs != null)
4502                   {
4503                     if (jws2servs.hasServices())
4504                     {
4505                       jws2servs.attachWSMenuEntry(webService, me);
4506                       for (Jws2Instance sv : jws2servs.getServices())
4507                       {
4508                         if (sv.description.toLowerCase().contains("jpred"))
4509                         {
4510                           for (JMenuItem jmi : legacyItems)
4511                           {
4512                             jmi.setVisible(false);
4513                           }
4514                         }
4515                       }
4516
4517                     }
4518                     if (jws2servs.isRunning())
4519                     {
4520                       JMenuItem tm = new JMenuItem(
4521                               "Still discovering JABA Services");
4522                       tm.setEnabled(false);
4523                       webService.add(tm);
4524                     }
4525                   }
4526                 }
4527                 build_urlServiceMenu(me.webService);
4528                 build_fetchdbmenu(webService);
4529                 for (JMenu item : wsmenu)
4530                 {
4531                   if (item.getItemCount() == 0)
4532                   {
4533                     item.setEnabled(false);
4534                   }
4535                   else
4536                   {
4537                     item.setEnabled(true);
4538                   }
4539                 }
4540               } catch (Exception e)
4541               {
4542                 Cache.log
4543                         .debug("Exception during web service menu building process.",
4544                                 e);
4545               }
4546               ;
4547             }
4548           });
4549         } catch (Exception e)
4550         {
4551         }
4552         ;
4553
4554         buildingMenu = false;
4555       }
4556     }).start();
4557
4558   }
4559
4560   /**
4561    * construct any groupURL type service menu entries.
4562    * 
4563    * @param webService
4564    */
4565   private void build_urlServiceMenu(JMenu webService)
4566   {
4567     // TODO: remove this code when 2.7 is released
4568     // DEBUG - alignmentView
4569     /*
4570      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4571      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4572      * 
4573      * @Override public void actionPerformed(ActionEvent e) {
4574      * jalview.datamodel.AlignmentView
4575      * .testSelectionViews(af.viewport.getAlignment(),
4576      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4577      * 
4578      * }); webService.add(testAlView);
4579      */
4580     // TODO: refactor to RestClient discoverer and merge menu entries for
4581     // rest-style services with other types of analysis/calculation service
4582     // SHmmr test client - still being implemented.
4583     // DEBUG - alignmentView
4584
4585     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4586             .getRestClients())
4587     {
4588       client.attachWSMenuEntry(
4589               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4590               this);
4591     }
4592   }
4593
4594   /*
4595    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4596    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4597    * getProperty("LAST_DIRECTORY"));
4598    * 
4599    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4600    * to Vamsas file"); chooser.setToolTipText("Export");
4601    * 
4602    * int value = chooser.showSaveDialog(this);
4603    * 
4604    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4605    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4606    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4607    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4608    */
4609   /**
4610    * prototype of an automatically enabled/disabled analysis function
4611    * 
4612    */
4613   protected void setShowProductsEnabled()
4614   {
4615     SequenceI[] selection = viewport.getSequenceSelection();
4616     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4617             viewport.getAlignment().getDataset()))
4618     {
4619       showProducts.setEnabled(true);
4620
4621     }
4622     else
4623     {
4624       showProducts.setEnabled(false);
4625     }
4626   }
4627
4628   /**
4629    * search selection for sequence xRef products and build the show products
4630    * menu.
4631    * 
4632    * @param selection
4633    * @param dataset
4634    * @return true if showProducts menu should be enabled.
4635    */
4636   public boolean canShowProducts(SequenceI[] selection,
4637           boolean isRegionSelection, Alignment dataset)
4638   {
4639     boolean showp = false;
4640     try
4641     {
4642       showProducts.removeAll();
4643       final boolean dna = viewport.getAlignment().isNucleotide();
4644       final Alignment ds = dataset;
4645       String[] ptypes = (selection == null || selection.length == 0) ? null
4646               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4647       // Object[] prods =
4648       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4649       // selection, dataset, true);
4650       final SequenceI[] sel = selection;
4651       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4652       {
4653         showp = true;
4654         final boolean isRegSel = isRegionSelection;
4655         final AlignFrame af = this;
4656         final String source = ptypes[t];
4657         JMenuItem xtype = new JMenuItem(ptypes[t]);
4658         xtype.addActionListener(new ActionListener()
4659         {
4660
4661           @Override
4662           public void actionPerformed(ActionEvent e)
4663           {
4664             // TODO: new thread for this call with vis-delay
4665             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4666                     isRegSel, dna, source);
4667           }
4668
4669         });
4670         showProducts.add(xtype);
4671       }
4672       showProducts.setVisible(showp);
4673       showProducts.setEnabled(showp);
4674     } catch (Exception e)
4675     {
4676       jalview.bin.Cache.log
4677               .warn("canTranslate threw an exception - please report to help@jalview.org",
4678                       e);
4679       return false;
4680     }
4681     return showp;
4682   }
4683
4684   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4685           boolean isRegSel, boolean dna, String source)
4686   {
4687     final boolean fisRegSel = isRegSel;
4688     final boolean fdna = dna;
4689     final String fsrc = source;
4690     final AlignFrame ths = this;
4691     final SequenceI[] fsel = sel;
4692     Runnable foo = new Runnable()
4693     {
4694
4695       @Override
4696       public void run()
4697       {
4698         final long sttime = System.currentTimeMillis();
4699         ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
4700         try
4701         {
4702           Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
4703           // our local
4704           // dataset
4705           // reference
4706           Alignment prods = CrossRef
4707                   .findXrefSequences(fsel, fdna, fsrc, ds);
4708           if (prods != null)
4709           {
4710             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4711             for (int s = 0; s < sprods.length; s++)
4712             {
4713               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4714               if (ds.getSequences() == null
4715                       || !ds.getSequences().contains(
4716                               sprods[s].getDatasetSequence()))
4717               {
4718                 ds.addSequence(sprods[s].getDatasetSequence());
4719               }
4720               sprods[s].updatePDBIds();
4721             }
4722             Alignment al = new Alignment(sprods);
4723             AlignedCodonFrame[] cf = prods.getCodonFrames();
4724             al.setDataset(ds);
4725             for (int s = 0; cf != null && s < cf.length; s++)
4726             {
4727               al.addCodonFrame(cf[s]);
4728               cf[s] = null;
4729             }
4730             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4731                     DEFAULT_HEIGHT);
4732             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4733                     + " for " + ((fisRegSel) ? "selected region of " : "")
4734                     + getTitle();
4735             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4736                     DEFAULT_HEIGHT);
4737           }
4738           else
4739           {
4740             System.err.println("No Sequences generated for xRef type "
4741                     + fsrc);
4742           }
4743         } catch (Exception e)
4744         {
4745           jalview.bin.Cache.log.error(
4746                   "Exception when finding crossreferences", e);
4747         } catch (OutOfMemoryError e)
4748         {
4749           new OOMWarning("whilst fetching crossreferences", e);
4750         } catch (Error e)
4751         {
4752           jalview.bin.Cache.log.error("Error when finding crossreferences",
4753                   e);
4754         }
4755         ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
4756                 sttime);
4757       }
4758
4759     };
4760     Thread frunner = new Thread(foo);
4761     frunner.start();
4762   }
4763
4764   public boolean canShowTranslationProducts(SequenceI[] selection,
4765           AlignmentI alignment)
4766   {
4767     // old way
4768     try
4769     {
4770       return (jalview.analysis.Dna.canTranslate(selection,
4771               viewport.getViewAsVisibleContigs(true)));
4772     } catch (Exception e)
4773     {
4774       jalview.bin.Cache.log
4775               .warn("canTranslate threw an exception - please report to help@jalview.org",
4776                       e);
4777       return false;
4778     }
4779   }
4780
4781   @Override
4782   public void showProducts_actionPerformed(ActionEvent e)
4783   {
4784     // /////////////////////////////
4785     // Collect Data to be translated/transferred
4786
4787     SequenceI[] selection = viewport.getSequenceSelection();
4788     AlignmentI al = null;
4789     try
4790     {
4791       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4792               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4793               viewport.getAlignment().getDataset());
4794     } catch (Exception ex)
4795     {
4796       al = null;
4797       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4798     }
4799     if (al == null)
4800     {
4801       JOptionPane
4802               .showMessageDialog(
4803                       Desktop.desktop,
4804                       MessageManager
4805                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
4806                       MessageManager.getString("label.translation_failed"),
4807                       JOptionPane.WARNING_MESSAGE);
4808     }
4809     else
4810     {
4811       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4812       Desktop.addInternalFrame(af, MessageManager.formatMessage(
4813               "label.translation_of_params", new String[]
4814               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
4815     }
4816   }
4817
4818   @Override
4819   public void showTranslation_actionPerformed(ActionEvent e)
4820   {
4821     // /////////////////////////////
4822     // Collect Data to be translated/transferred
4823
4824     SequenceI[] selection = viewport.getSequenceSelection();
4825     String[] seqstring = viewport.getViewAsString(true);
4826     AlignmentI al = null;
4827     try
4828     {
4829       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4830               viewport.getViewAsVisibleContigs(true), viewport
4831                       .getGapCharacter(), viewport.getAlignment()
4832                       .getAlignmentAnnotation(), viewport.getAlignment()
4833                       .getWidth(), viewport.getAlignment().getDataset());
4834     } catch (Exception ex)
4835     {
4836       al = null;
4837       jalview.bin.Cache.log.error(
4838               "Exception during translation. Please report this !", ex);
4839       JOptionPane
4840               .showMessageDialog(
4841                       Desktop.desktop,
4842                       MessageManager
4843                               .getString("label.error_when_translating_sequences_submit_bug_report"),
4844                       MessageManager
4845                               .getString("label.implementation_error")
4846                               + MessageManager
4847                                       .getString("translation_failed"),
4848                       JOptionPane.ERROR_MESSAGE);
4849       return;
4850     }
4851     if (al == null)
4852     {
4853       JOptionPane
4854               .showMessageDialog(
4855                       Desktop.desktop,
4856                       MessageManager
4857                               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation"),
4858                       MessageManager.getString("label.translation_failed"),
4859                       JOptionPane.WARNING_MESSAGE);
4860     }
4861     else
4862     {
4863       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4864       Desktop.addInternalFrame(af, MessageManager.formatMessage(
4865               "label.translation_of_params", new String[]
4866               { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
4867     }
4868   }
4869
4870   /**
4871    * Try to load a features file onto the alignment.
4872    * 
4873    * @param file
4874    *          contents or path to retrieve file
4875    * @param type
4876    *          access mode of file (see jalview.io.AlignFile)
4877    * @return true if features file was parsed corectly.
4878    */
4879   public boolean parseFeaturesFile(String file, String type)
4880   {
4881     boolean featuresFile = false;
4882     try
4883     {
4884       featuresFile = new FeaturesFile(file, type).parse(viewport
4885               .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
4886               .getFeatureRenderer().featureColours, false,
4887               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4888     } catch (Exception ex)
4889     {
4890       ex.printStackTrace();
4891     }
4892
4893     if (featuresFile)
4894     {
4895       viewport.showSequenceFeatures = true;
4896       showSeqFeatures.setSelected(true);
4897       if (alignPanel.seqPanel.seqCanvas.fr != null)
4898       {
4899         // update the min/max ranges where necessary
4900         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4901       }
4902       if (featureSettings != null)
4903       {
4904         featureSettings.setTableData();
4905       }
4906       alignPanel.paintAlignment(true);
4907     }
4908
4909     return featuresFile;
4910   }
4911
4912   @Override
4913   public void dragEnter(DropTargetDragEvent evt)
4914   {
4915   }
4916
4917   @Override
4918   public void dragExit(DropTargetEvent evt)
4919   {
4920   }
4921
4922   @Override
4923   public void dragOver(DropTargetDragEvent evt)
4924   {
4925   }
4926
4927   @Override
4928   public void dropActionChanged(DropTargetDragEvent evt)
4929   {
4930   }
4931
4932   @Override
4933   public void drop(DropTargetDropEvent evt)
4934   {
4935     Transferable t = evt.getTransferable();
4936     java.util.List files = null;
4937
4938     try
4939     {
4940       DataFlavor uriListFlavor = new DataFlavor(
4941               "text/uri-list;class=java.lang.String");
4942       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4943       {
4944         // Works on Windows and MacOSX
4945         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4946         files = (java.util.List) t
4947                 .getTransferData(DataFlavor.javaFileListFlavor);
4948       }
4949       else if (t.isDataFlavorSupported(uriListFlavor))
4950       {
4951         // This is used by Unix drag system
4952         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4953         String data = (String) t.getTransferData(uriListFlavor);
4954         files = new java.util.ArrayList(1);
4955         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4956                 data, "\r\n"); st.hasMoreTokens();)
4957         {
4958           String s = st.nextToken();
4959           if (s.startsWith("#"))
4960           {
4961             // the line is a comment (as per the RFC 2483)
4962             continue;
4963           }
4964
4965           java.net.URI uri = new java.net.URI(s);
4966           // check to see if we can handle this kind of URI
4967           if (uri.getScheme().toLowerCase().startsWith("http"))
4968           {
4969             files.add(uri.toString());
4970           }
4971           else
4972           {
4973             // otherwise preserve old behaviour: catch all for file objects
4974             java.io.File file = new java.io.File(uri);
4975             files.add(file.toString());
4976           }
4977         }
4978       }
4979     } catch (Exception e)
4980     {
4981       e.printStackTrace();
4982     }
4983     if (files != null)
4984     {
4985       try
4986       {
4987         // check to see if any of these files have names matching sequences in
4988         // the alignment
4989         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4990                 .getAlignment().getSequencesArray());
4991         /**
4992          * Object[] { String,SequenceI}
4993          */
4994         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4995         ArrayList<String> filesnotmatched = new ArrayList<String>();
4996         for (int i = 0; i < files.size(); i++)
4997         {
4998           String file = files.get(i).toString();
4999           String pdbfn = "";
5000           String protocol = FormatAdapter.checkProtocol(file);
5001           if (protocol == jalview.io.FormatAdapter.FILE)
5002           {
5003             File fl = new File(file);
5004             pdbfn = fl.getName();
5005           }
5006           else if (protocol == jalview.io.FormatAdapter.URL)
5007           {
5008             URL url = new URL(file);
5009             pdbfn = url.getFile();
5010           }
5011           if (pdbfn.length() > 0)
5012           {
5013             // attempt to find a match in the alignment
5014             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
5015             int l = 0, c = pdbfn.indexOf(".");
5016             while (mtch == null && c != -1)
5017             {
5018               do
5019               {
5020                 l = c;
5021               } while ((c = pdbfn.indexOf(".", l)) > l);
5022               if (l > -1)
5023               {
5024                 pdbfn = pdbfn.substring(0, l);
5025               }
5026               mtch = idm.findAllIdMatches(pdbfn);
5027             }
5028             if (mtch != null)
5029             {
5030               String type = null;
5031               try
5032               {
5033                 type = new IdentifyFile().Identify(file, protocol);
5034               } catch (Exception ex)
5035               {
5036                 type = null;
5037               }
5038               if (type != null)
5039               {
5040                 if (type.equalsIgnoreCase("PDB"))
5041                 {
5042                   filesmatched.add(new Object[]
5043                   { file, protocol, mtch });
5044                   continue;
5045                 }
5046               }
5047             }
5048             // File wasn't named like one of the sequences or wasn't a PDB file.
5049             filesnotmatched.add(file);
5050           }
5051         }
5052         int assocfiles = 0;
5053         if (filesmatched.size() > 0)
5054         {
5055           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
5056                   || JOptionPane
5057                           .showConfirmDialog(
5058                                   this,
5059                                   MessageManager
5060                                           .formatMessage(
5061                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
5062                                                   new String[]
5063                                                   { Integer.valueOf(
5064                                                           filesmatched
5065                                                                   .size())
5066                                                           .toString() }),
5067                                   MessageManager
5068                                           .getString("label.automatically_associate_pdb_files_by_name"),
5069                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
5070
5071           {
5072             for (Object[] fm : filesmatched)
5073             {
5074               // try and associate
5075               // TODO: may want to set a standard ID naming formalism for
5076               // associating PDB files which have no IDs.
5077               for (SequenceI toassoc : (SequenceI[]) fm[2])
5078               {
5079                 PDBEntry pe = new AssociatePdbFileWithSeq()
5080                         .associatePdbWithSeq((String) fm[0],
5081                                 (String) fm[1], toassoc, false,
5082                                 Desktop.instance);
5083                 if (pe != null)
5084                 {
5085                   System.err.println("Associated file : "
5086                           + ((String) fm[0]) + " with "
5087                           + toassoc.getDisplayId(true));
5088                   assocfiles++;
5089                 }
5090               }
5091               alignPanel.paintAlignment(true);
5092             }
5093           }
5094         }
5095         if (filesnotmatched.size() > 0)
5096         {
5097           if (assocfiles > 0
5098                   && (Cache.getDefault(
5099                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
5100                           .showConfirmDialog(
5101                                   this,
5102                                   "<html>"+MessageManager
5103                                           .formatMessage(
5104                                                   "label.ignore_unmatched_dropped_files_info",
5105                                                   new String[]
5106                                                   { Integer.valueOf(
5107                                                           filesnotmatched
5108                                                                   .size())
5109                                                           .toString() })+"</html>",
5110                                   MessageManager
5111                                           .getString("label.ignore_unmatched_dropped_files"),
5112                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
5113           {
5114             return;
5115           }
5116           for (String fn : filesnotmatched)
5117           {
5118             loadJalviewDataFile(fn, null, null, null);
5119           }
5120
5121         }
5122       } catch (Exception ex)
5123       {
5124         ex.printStackTrace();
5125       }
5126     }
5127   }
5128
5129   /**
5130    * Attempt to load a "dropped" file or URL string: First by testing whether
5131    * it's and Annotation file, then a JNet file, and finally a features file. If
5132    * all are false then the user may have dropped an alignment file onto this
5133    * AlignFrame.
5134    * 
5135    * @param file
5136    *          either a filename or a URL string.
5137    */
5138   public void loadJalviewDataFile(String file, String protocol,
5139           String format, SequenceI assocSeq)
5140   {
5141     try
5142     {
5143       if (protocol == null)
5144       {
5145         protocol = jalview.io.FormatAdapter.checkProtocol(file);
5146       }
5147       // if the file isn't identified, or not positively identified as some
5148       // other filetype (PFAM is default unidentified alignment file type) then
5149       // try to parse as annotation.
5150       boolean isAnnotation = (format == null || format
5151               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
5152               .readAnnotationFile(viewport.getAlignment(), file, protocol)
5153               : false;
5154
5155       if (!isAnnotation)
5156       {
5157         // first see if its a T-COFFEE score file
5158         TCoffeeScoreFile tcf = null;
5159         try
5160         {
5161           tcf = new TCoffeeScoreFile(file, protocol);
5162           if (tcf.isValid())
5163           {
5164             if (tcf.annotateAlignment(viewport.getAlignment(), true))
5165             {
5166               tcoffeeColour.setEnabled(true);
5167               tcoffeeColour.setSelected(true);
5168               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
5169               isAnnotation = true;
5170               statusBar
5171                       .setText(MessageManager
5172                               .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
5173             }
5174             else
5175             {
5176               // some problem - if no warning its probable that the ID matching
5177               // process didn't work
5178               JOptionPane
5179                       .showMessageDialog(
5180                               Desktop.desktop,
5181                               tcf.getWarningMessage() == null ? MessageManager
5182                                       .getString("label.check_file_matches_sequence_ids_alignment")
5183                                       : tcf.getWarningMessage(),
5184                               MessageManager
5185                                       .getString("label.problem_reading_tcoffee_score_file"),
5186                               JOptionPane.WARNING_MESSAGE);
5187             }
5188           }
5189           else
5190           {
5191             tcf = null;
5192           }
5193         } catch (Exception x)
5194         {
5195           Cache.log
5196                   .debug("Exception when processing data source as T-COFFEE score file",
5197                           x);
5198           tcf = null;
5199         }
5200         if (tcf == null)
5201         {
5202           // try to see if its a JNet 'concise' style annotation file *before*
5203           // we
5204           // try to parse it as a features file
5205           if (format == null)
5206           {
5207             format = new IdentifyFile().Identify(file, protocol);
5208           }
5209           if (format.equalsIgnoreCase("JnetFile"))
5210           {
5211             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
5212                     file, protocol);
5213             new JnetAnnotationMaker().add_annotation(predictions,
5214                     viewport.getAlignment(), 0, false);
5215             isAnnotation = true;
5216           }
5217           else
5218           {
5219             /*
5220              * if (format.equalsIgnoreCase("PDB")) {
5221              * 
5222              * String pdbfn = ""; // try to match up filename with sequence id
5223              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
5224              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
5225              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
5226              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
5227              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5228              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
5229              * // attempt to find a match in the alignment SequenceI mtch =
5230              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
5231              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
5232              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
5233              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
5234              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
5235              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
5236              * { System.err.println("Associated file : " + file + " with " +
5237              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
5238              * TODO: maybe need to load as normal otherwise return; } }
5239              */
5240             // try to parse it as a features file
5241             boolean isGroupsFile = parseFeaturesFile(file, protocol);
5242             // if it wasn't a features file then we just treat it as a general
5243             // alignment file to load into the current view.
5244             if (!isGroupsFile)
5245             {
5246               new FileLoader().LoadFile(viewport, file, protocol, format);
5247             }
5248             else
5249             {
5250               alignPanel.paintAlignment(true);
5251             }
5252           }
5253         }
5254       }
5255       if (isAnnotation)
5256       {
5257
5258         alignPanel.adjustAnnotationHeight();
5259         viewport.updateSequenceIdColours();
5260         buildSortByAnnotationScoresMenu();
5261         alignPanel.paintAlignment(true);
5262       }
5263     } catch (Exception ex)
5264     {
5265       ex.printStackTrace();
5266     } catch (OutOfMemoryError oom)
5267     {
5268       try
5269       {
5270         System.gc();
5271       } catch (Exception x)
5272       {
5273       }
5274       ;
5275       new OOMWarning(
5276               "loading data "
5277                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5278                               : "using " + protocol + " from " + file)
5279                               : ".")
5280                       + (format != null ? "(parsing as '" + format
5281                               + "' file)" : ""), oom, Desktop.desktop);
5282     }
5283   }
5284
5285   @Override
5286   public void tabSelectionChanged(int index)
5287   {
5288     if (index > -1)
5289     {
5290       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
5291       viewport = alignPanel.av;
5292       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
5293       setMenusFromViewport(viewport);
5294     }
5295   }
5296
5297   @Override
5298   public void tabbedPane_mousePressed(MouseEvent e)
5299   {
5300     if (SwingUtilities.isRightMouseButton(e))
5301     {
5302       String reply = JOptionPane.showInternalInputDialog(this,
5303               MessageManager.getString("label.enter_view_name"),
5304               MessageManager.getString("label.enter_view_name"),
5305               JOptionPane.QUESTION_MESSAGE);
5306
5307       if (reply != null)
5308       {
5309         viewport.viewName = reply;
5310         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5311       }
5312     }
5313   }
5314
5315   public AlignViewport getCurrentView()
5316   {
5317     return viewport;
5318   }
5319
5320   /**
5321    * Open the dialog for regex description parsing.
5322    */
5323   @Override
5324   protected void extractScores_actionPerformed(ActionEvent e)
5325   {
5326     ParseProperties pp = new jalview.analysis.ParseProperties(
5327             viewport.getAlignment());
5328     // TODO: verify regex and introduce GUI dialog for version 2.5
5329     // if (pp.getScoresFromDescription("col", "score column ",
5330     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5331     // true)>0)
5332     if (pp.getScoresFromDescription("description column",
5333             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5334     {
5335       buildSortByAnnotationScoresMenu();
5336     }
5337   }
5338
5339   /*
5340    * (non-Javadoc)
5341    * 
5342    * @see
5343    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5344    * )
5345    */
5346   @Override
5347   protected void showDbRefs_actionPerformed(ActionEvent e)
5348   {
5349     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
5350   }
5351
5352   /*
5353    * (non-Javadoc)
5354    * 
5355    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5356    * ActionEvent)
5357    */
5358   @Override
5359   protected void showNpFeats_actionPerformed(ActionEvent e)
5360   {
5361     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
5362   }
5363
5364   /**
5365    * find the viewport amongst the tabs in this alignment frame and close that
5366    * tab
5367    * 
5368    * @param av
5369    */
5370   public boolean closeView(AlignViewport av)
5371   {
5372     if (viewport == av)
5373     {
5374       this.closeMenuItem_actionPerformed(false);
5375       return true;
5376     }
5377     Component[] comp = tabbedPane.getComponents();
5378     for (int i = 0; comp != null && i < comp.length; i++)
5379     {
5380       if (comp[i] instanceof AlignmentPanel)
5381       {
5382         if (((AlignmentPanel) comp[i]).av == av)
5383         {
5384           // close the view.
5385           closeView((AlignmentPanel) comp[i]);
5386           return true;
5387         }
5388       }
5389     }
5390     return false;
5391   }
5392
5393   protected void build_fetchdbmenu(JMenu webService)
5394   {
5395     // Temporary hack - DBRef Fetcher always top level ws entry.
5396     // TODO We probably want to store a sequence database checklist in
5397     // preferences and have checkboxes.. rather than individual sources selected
5398     // here
5399     final JMenu rfetch = new JMenu(
5400             MessageManager.getString("action.fetch_db_references"));
5401     rfetch.setToolTipText(MessageManager
5402             .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
5403     webService.add(rfetch);
5404
5405     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
5406             MessageManager.getString("option.trim_retrieved_seqs"));
5407     trimrs.setToolTipText(MessageManager
5408             .getString("label.trim_retrieved_sequences"));
5409     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
5410     trimrs.addActionListener(new ActionListener()
5411     {
5412       @Override
5413       public void actionPerformed(ActionEvent e)
5414       {
5415         trimrs.setSelected(trimrs.isSelected());
5416         Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
5417                 Boolean.valueOf(trimrs.isSelected()).toString());
5418       };
5419     });
5420     rfetch.add(trimrs);
5421     JMenuItem fetchr = new JMenuItem(
5422             MessageManager.getString("label.standard_databases"));
5423     fetchr.setToolTipText(MessageManager
5424             .getString("label.fetch_embl_uniprot"));
5425     fetchr.addActionListener(new ActionListener()
5426     {
5427
5428       @Override
5429       public void actionPerformed(ActionEvent e)
5430       {
5431         new Thread(new Runnable()
5432         {
5433
5434           @Override
5435           public void run()
5436           {
5437             new jalview.ws.DBRefFetcher(alignPanel.av
5438                     .getSequenceSelection(), alignPanel.alignFrame)
5439                     .fetchDBRefs(false);
5440           }
5441         }).start();
5442
5443       }
5444
5445     });
5446     rfetch.add(fetchr);
5447     final AlignFrame me = this;
5448     new Thread(new Runnable()
5449     {
5450       @Override
5451       public void run()
5452       {
5453         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5454                 .getSequenceFetcherSingleton(me);
5455         javax.swing.SwingUtilities.invokeLater(new Runnable()
5456         {
5457           @Override
5458           public void run()
5459           {
5460             String[] dbclasses = sf.getOrderedSupportedSources();
5461             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5462             // jalview.util.QuickSort.sort(otherdb, otherdb);
5463             List<DbSourceProxy> otherdb;
5464             JMenu dfetch = new JMenu();
5465             JMenu ifetch = new JMenu();
5466             JMenuItem fetchr = null;
5467             int comp = 0, icomp = 0, mcomp = 15;
5468             String mname = null;
5469             int dbi = 0;
5470             for (String dbclass : dbclasses)
5471             {
5472               otherdb = sf.getSourceProxy(dbclass);
5473               // add a single entry for this class, or submenu allowing 'fetch
5474               // all' or pick one
5475               if (otherdb == null || otherdb.size() < 1)
5476               {
5477                 continue;
5478               }
5479               // List<DbSourceProxy> dbs=otherdb;
5480               // otherdb=new ArrayList<DbSourceProxy>();
5481               // for (DbSourceProxy db:dbs)
5482               // {
5483               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5484               // }
5485               if (mname == null)
5486               {
5487                 mname = "From " + dbclass;
5488               }
5489               if (otherdb.size() == 1)
5490               {
5491                 final DbSourceProxy[] dassource = otherdb
5492                         .toArray(new DbSourceProxy[0]);
5493                 DbSourceProxy src = otherdb.get(0);
5494                 fetchr = new JMenuItem(src.getDbSource());
5495                 fetchr.addActionListener(new ActionListener()
5496                 {
5497
5498                   @Override
5499                   public void actionPerformed(ActionEvent e)
5500                   {
5501                     new Thread(new Runnable()
5502                     {
5503
5504                       @Override
5505                       public void run()
5506                       {
5507                         new jalview.ws.DBRefFetcher(alignPanel.av
5508                                 .getSequenceSelection(),
5509                                 alignPanel.alignFrame, dassource)
5510                                 .fetchDBRefs(false);
5511                       }
5512                     }).start();
5513                   }
5514
5515                 });
5516                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
5517                 dfetch.add(fetchr);
5518                 comp++;
5519               }
5520               else
5521               {
5522                 final DbSourceProxy[] dassource = otherdb
5523                         .toArray(new DbSourceProxy[0]);
5524                 // fetch all entry
5525                 DbSourceProxy src = otherdb.get(0);
5526                 fetchr = new JMenuItem(MessageManager.formatMessage(
5527                         "label.fetch_all_param", new String[]
5528                         { src.getDbSource() }));
5529                 fetchr.addActionListener(new ActionListener()
5530                 {
5531                   @Override
5532                   public void actionPerformed(ActionEvent e)
5533                   {
5534                     new Thread(new Runnable()
5535                     {
5536
5537                       @Override
5538                       public void run()
5539                       {
5540                         new jalview.ws.DBRefFetcher(alignPanel.av
5541                                 .getSequenceSelection(),
5542                                 alignPanel.alignFrame, dassource)
5543                                 .fetchDBRefs(false);
5544                       }
5545                     }).start();
5546                   }
5547                 });
5548
5549                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
5550                 dfetch.add(fetchr);
5551                 comp++;
5552                 // and then build the rest of the individual menus
5553                 ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()}));
5554                 icomp = 0;
5555                 String imname = null;
5556                 int i = 0;
5557                 for (DbSourceProxy sproxy : otherdb)
5558                 {
5559                   String dbname = sproxy.getDbName();
5560                   String sname = dbname.length() > 5 ? dbname.substring(0,
5561                           5) + "..." : dbname;
5562                   String msname = dbname.length() > 10 ? dbname.substring(
5563                           0, 10) + "..." : dbname;
5564                   if (imname == null)
5565                   {
5566                     imname = MessageManager.formatMessage("label.from_msname", new String[]{sname});
5567                   }
5568                   fetchr = new JMenuItem(msname);
5569                   final DbSourceProxy[] dassrc =
5570                   { sproxy };
5571                   fetchr.addActionListener(new ActionListener()
5572                   {
5573
5574                     @Override
5575                     public void actionPerformed(ActionEvent e)
5576                     {
5577                       new Thread(new Runnable()
5578                       {
5579
5580                         @Override
5581                         public void run()
5582                         {
5583                           new jalview.ws.DBRefFetcher(alignPanel.av
5584                                   .getSequenceSelection(),
5585                                   alignPanel.alignFrame, dassrc)
5586                                   .fetchDBRefs(false);
5587                         }
5588                       }).start();
5589                     }
5590
5591                   });
5592                   fetchr.setToolTipText("<html>"
5593                           + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
5594                   ifetch.add(fetchr);
5595                   ++i;
5596                   if (++icomp >= mcomp || i == (otherdb.size()))
5597                   {
5598                     ifetch.setText(MessageManager.formatMessage(
5599                             "label.source_to_target", imname, sname));
5600                     dfetch.add(ifetch);
5601                     ifetch = new JMenu();
5602                     imname = null;
5603                     icomp = 0;
5604                     comp++;
5605                   }
5606                 }
5607               }
5608               ++dbi;
5609               if (comp >= mcomp || dbi >= (dbclasses.length))
5610               {
5611                 dfetch.setText(MessageManager.formatMessage(
5612                         "label.source_to_target", mname, dbclass));
5613                 rfetch.add(dfetch);
5614                 dfetch = new JMenu();
5615                 mname = null;
5616                 comp = 0;
5617               }
5618             }
5619           }
5620         });
5621       }
5622     }).start();
5623
5624   }
5625
5626   /**
5627    * Left justify the whole alignment.
5628    */
5629   @Override
5630   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5631   {
5632     AlignmentI al = viewport.getAlignment();
5633     al.justify(false);
5634     viewport.firePropertyChange("alignment", null, al);
5635   }
5636
5637   /**
5638    * Right justify the whole alignment.
5639    */
5640   @Override
5641   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5642   {
5643     AlignmentI al = viewport.getAlignment();
5644     al.justify(true);
5645     viewport.firePropertyChange("alignment", null, al);
5646   }
5647
5648   public void setShowSeqFeatures(boolean b)
5649   {
5650     showSeqFeatures.setSelected(true);
5651     viewport.setShowSequenceFeatures(true);
5652   }
5653
5654   /*
5655    * (non-Javadoc)
5656    * 
5657    * @see
5658    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5659    * awt.event.ActionEvent)
5660    */
5661   @Override
5662   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5663   {
5664     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5665     alignPanel.paintAlignment(true);
5666   }
5667
5668   /*
5669    * (non-Javadoc)
5670    * 
5671    * @see
5672    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5673    * .ActionEvent)
5674    */
5675   @Override
5676   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5677   {
5678     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5679     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5680
5681   }
5682
5683   /*
5684    * (non-Javadoc)
5685    * 
5686    * @see
5687    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5688    * .event.ActionEvent)
5689    */
5690   @Override
5691   protected void showGroupConservation_actionPerformed(ActionEvent e)
5692   {
5693     viewport.setShowGroupConservation(showGroupConservation.getState());
5694     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5695   }
5696
5697   /*
5698    * (non-Javadoc)
5699    * 
5700    * @see
5701    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5702    * .event.ActionEvent)
5703    */
5704   @Override
5705   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5706   {
5707     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5708     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5709   }
5710
5711   /*
5712    * (non-Javadoc)
5713    * 
5714    * @see
5715    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5716    * .event.ActionEvent)
5717    */
5718   @Override
5719   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5720   {
5721     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5722     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5723   }
5724
5725   @Override
5726   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5727   {
5728     showSequenceLogo.setState(true);
5729     viewport.setShowSequenceLogo(true);
5730     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5731     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5732   }
5733
5734   @Override
5735   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5736   {
5737     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5738   }
5739
5740   /*
5741    * (non-Javadoc)
5742    * 
5743    * @see
5744    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5745    * .event.ActionEvent)
5746    */
5747   @Override
5748   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5749   {
5750     if (avc.makeGroupsFromSelection())
5751     {
5752       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5753       alignPanel.updateAnnotation();
5754       alignPanel.paintAlignment(true);
5755     }
5756   }
5757
5758   @Override
5759   protected void createGroup_actionPerformed(ActionEvent e)
5760   {
5761     if (avc.createGroup())
5762     {
5763       alignPanel.alignmentChanged();
5764     }
5765   }
5766
5767   @Override
5768   protected void unGroup_actionPerformed(ActionEvent e)
5769   {
5770     if (avc.unGroup())
5771     {
5772       alignPanel.alignmentChanged();
5773     }
5774   }
5775
5776   /**
5777    * make the given alignmentPanel the currently selected tab
5778    * 
5779    * @param alignmentPanel
5780    */
5781   public void setDisplayedView(AlignmentPanel alignmentPanel)
5782   {
5783     if (!viewport.getSequenceSetId().equals(
5784             alignmentPanel.av.getSequenceSetId()))
5785     {
5786       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
5787     }
5788     if (tabbedPane != null
5789             & alignPanels.indexOf(alignmentPanel) != tabbedPane
5790                     .getSelectedIndex())
5791     {
5792       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5793     }
5794   }
5795
5796   /**
5797    * Action on selection of menu options to Show or Hide annotations.
5798    * 
5799    * @param visible
5800    * @param forSequences
5801    *          update sequence-related annotations
5802    * @param forAlignment
5803    *          update non-sequence-related annotations
5804    */
5805   @Override
5806   protected void setAnnotationsVisibility(boolean visible,
5807           boolean forSequences, boolean forAlignment)
5808   {
5809     for (AlignmentAnnotation aa : alignPanel.getAlignment()
5810             .getAlignmentAnnotation())
5811     {
5812       boolean apply = (aa.sequenceRef == null && forAlignment)
5813               || (aa.sequenceRef != null && forSequences);
5814       if (apply)
5815       {
5816         aa.visible = visible;
5817       }
5818     }
5819     alignPanel.validateAnnotationDimensions(false);
5820     alignPanel.alignmentChanged();
5821   }
5822
5823   /**
5824    * Store selected annotation sort order for the view and repaint.
5825    */
5826   @Override
5827   protected void sortAnnotations_actionPerformed()
5828   {
5829     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
5830     this.alignPanel.av
5831             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
5832     alignPanel.paintAlignment(true);
5833   }
5834 }
5835
5836 class PrintThread extends Thread
5837 {
5838   AlignmentPanel ap;
5839
5840   public PrintThread(AlignmentPanel ap)
5841   {
5842     this.ap = ap;
5843   }
5844
5845   static PageFormat pf;
5846
5847   @Override
5848   public void run()
5849   {
5850     PrinterJob printJob = PrinterJob.getPrinterJob();
5851
5852     if (pf != null)
5853     {
5854       printJob.setPrintable(ap, pf);
5855     }
5856     else
5857     {
5858       printJob.setPrintable(ap);
5859     }
5860
5861     if (printJob.printDialog())
5862     {
5863       try
5864       {
5865         printJob.print();
5866       } catch (Exception PrintException)
5867       {
5868         PrintException.printStackTrace();
5869       }
5870     }
5871   }
5872 }