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