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