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