JAL-1432 updated copyright notices
[jalview.git] / src / jalview / gui / AlignFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
18  */
19 package jalview.gui;
20
21 import jalview.analysis.AAFrequency;
22 import jalview.analysis.AlignmentSorter;
23 import jalview.analysis.Conservation;
24 import jalview.analysis.CrossRef;
25 import jalview.analysis.NJTree;
26 import jalview.analysis.ParseProperties;
27 import jalview.analysis.SequenceIdMatcher;
28 import jalview.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.ws.jws1.Discoverer;
80 import jalview.ws.jws2.Jws2Discoverer;
81 import jalview.ws.seqfetcher.DbSourceProxy;
82
83 import java.awt.BorderLayout;
84 import java.awt.Color;
85 import java.awt.Component;
86 import java.awt.GridLayout;
87 import java.awt.Rectangle;
88 import java.awt.Toolkit;
89 import java.awt.datatransfer.Clipboard;
90 import java.awt.datatransfer.DataFlavor;
91 import java.awt.datatransfer.StringSelection;
92 import java.awt.datatransfer.Transferable;
93 import java.awt.dnd.DnDConstants;
94 import java.awt.dnd.DropTargetDragEvent;
95 import java.awt.dnd.DropTargetDropEvent;
96 import java.awt.dnd.DropTargetEvent;
97 import java.awt.dnd.DropTargetListener;
98 import java.awt.event.ActionEvent;
99 import java.awt.event.ActionListener;
100 import java.awt.event.KeyAdapter;
101 import java.awt.event.KeyEvent;
102 import java.awt.event.MouseAdapter;
103 import java.awt.event.MouseEvent;
104 import java.awt.print.PageFormat;
105 import java.awt.print.PrinterJob;
106 import java.beans.PropertyChangeEvent;
107 import java.io.File;
108 import java.net.URL;
109 import java.util.ArrayList;
110 import java.util.Enumeration;
111 import java.util.Hashtable;
112 import java.util.List;
113 import java.util.Vector;
114
115 import javax.swing.JButton;
116 import javax.swing.JEditorPane;
117 import javax.swing.JInternalFrame;
118 import javax.swing.JLabel;
119 import javax.swing.JLayeredPane;
120 import javax.swing.JMenu;
121 import javax.swing.JMenuItem;
122 import javax.swing.JOptionPane;
123 import javax.swing.JPanel;
124 import javax.swing.JProgressBar;
125 import javax.swing.JRadioButtonMenuItem;
126 import javax.swing.JScrollPane;
127 import javax.swing.SwingUtilities;
128
129 /**
130  * DOCUMENT ME!
131  * 
132  * @author $author$
133  * @version $Revision$
134  */
135 public class AlignFrame extends GAlignFrame implements DropTargetListener,
136         IProgressIndicator
137 {
138
139   /** DOCUMENT ME!! */
140   public static final int DEFAULT_WIDTH = 700;
141
142   /** DOCUMENT ME!! */
143   public static final int DEFAULT_HEIGHT = 500;
144
145   public AlignmentPanel alignPanel;
146
147   AlignViewport viewport;
148
149   Vector alignPanels = new Vector();
150
151   /**
152    * Last format used to load or save alignments in this window
153    */
154   String currentFileFormat = null;
155
156   /**
157    * Current filename for this alignment
158    */
159   String fileName = null;
160
161   /**
162    * Creates a new AlignFrame object with specific width and height.
163    * 
164    * @param al
165    * @param width
166    * @param height
167    */
168   public AlignFrame(AlignmentI al, int width, int height)
169   {
170     this(al, null, width, height);
171   }
172
173   /**
174    * Creates a new AlignFrame object with specific width, height and
175    * sequenceSetId
176    * 
177    * @param al
178    * @param width
179    * @param height
180    * @param sequenceSetId
181    */
182   public AlignFrame(AlignmentI al, int width, int height,
183           String sequenceSetId)
184   {
185     this(al, null, width, height, sequenceSetId);
186   }
187
188   /**
189    * Creates a new AlignFrame object with specific width, height and
190    * sequenceSetId
191    * 
192    * @param al
193    * @param width
194    * @param height
195    * @param sequenceSetId
196    * @param viewId
197    */
198   public AlignFrame(AlignmentI al, int width, int height,
199           String sequenceSetId, String viewId)
200   {
201     this(al, null, width, height, sequenceSetId, viewId);
202   }
203
204   /**
205    * new alignment window with hidden columns
206    * 
207    * @param al
208    *          AlignmentI
209    * @param hiddenColumns
210    *          ColumnSelection or null
211    * @param width
212    *          Width of alignment frame
213    * @param height
214    *          height of frame.
215    */
216   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
217           int width, int height)
218   {
219     this(al, hiddenColumns, width, height, null);
220   }
221
222   /**
223    * Create alignment frame for al with hiddenColumns, a specific width and
224    * height, and specific sequenceId
225    * 
226    * @param al
227    * @param hiddenColumns
228    * @param width
229    * @param height
230    * @param sequenceSetId
231    *          (may be null)
232    */
233   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
234           int width, int height, String sequenceSetId)
235   {
236     this(al, hiddenColumns, width, height, sequenceSetId, null);
237   }
238
239   /**
240    * Create alignment frame for al with hiddenColumns, a specific width and
241    * height, and specific sequenceId
242    * 
243    * @param al
244    * @param hiddenColumns
245    * @param width
246    * @param height
247    * @param sequenceSetId
248    *          (may be null)
249    * @param viewId
250    *          (may be null)
251    */
252   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
253           int width, int height, String sequenceSetId, String viewId)
254   {
255     setSize(width, height);
256     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
257
258     alignPanel = new AlignmentPanel(this, viewport);
259
260     if (al.getDataset() == null)
261     {
262       al.setDataset(null);
263     }
264
265     addAlignmentPanel(alignPanel, true);
266     init();
267   }
268
269   /**
270    * Make a new AlignFrame from exisiting alignmentPanels
271    * 
272    * @param ap
273    *          AlignmentPanel
274    * @param av
275    *          AlignViewport
276    */
277   public AlignFrame(AlignmentPanel ap)
278   {
279     viewport = ap.av;
280     alignPanel = ap;
281     addAlignmentPanel(ap, false);
282     init();
283   }
284
285   /**
286    * initalise the alignframe from the underlying viewport data and the
287    * configurations
288    */
289   void init()
290   {
291     if (viewport.getAlignmentConservationAnnotation() == null)
292     {
293       BLOSUM62Colour.setEnabled(false);
294       conservationMenuItem.setEnabled(false);
295       modifyConservation.setEnabled(false);
296       // PIDColour.setEnabled(false);
297       // abovePIDThreshold.setEnabled(false);
298       // modifyPID.setEnabled(false);
299     }
300
301     String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
302             "No sort");
303
304     if (sortby.equals("Id"))
305     {
306       sortIDMenuItem_actionPerformed(null);
307     }
308     else if (sortby.equals("Pairwise Identity"))
309     {
310       sortPairwiseMenuItem_actionPerformed(null);
311     }
312
313     if (Desktop.desktop != null)
314     {
315       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
316       addServiceListeners();
317       setGUINucleotide(viewport.getAlignment().isNucleotide());
318     }
319
320     setMenusFromViewport(viewport);
321     buildSortByAnnotationScoresMenu();
322     if (viewport.wrapAlignment)
323     {
324       wrapMenuItem_actionPerformed(null);
325     }
326
327     if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
328     {
329       this.overviewMenuItem_actionPerformed(null);
330     }
331
332     addKeyListener();
333
334   }
335
336   /**
337    * Change the filename and format for the alignment, and enable the 'reload'
338    * button functionality.
339    * 
340    * @param file
341    *          valid filename
342    * @param format
343    *          format of file
344    */
345   public void setFileName(String file, String format)
346   {
347     fileName = file;
348     currentFileFormat = format;
349     reload.setEnabled(true);
350   }
351
352   void addKeyListener()
353   {
354     addKeyListener(new KeyAdapter()
355     {
356       @Override
357       public void keyPressed(KeyEvent evt)
358       {
359         if (viewport.cursorMode
360                 && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
361                         .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
362                         .getKeyCode() <= KeyEvent.VK_NUMPAD9))
363                 && Character.isDigit(evt.getKeyChar()))
364           alignPanel.seqPanel.numberPressed(evt.getKeyChar());
365
366         switch (evt.getKeyCode())
367         {
368
369         case 27: // escape key
370           deselectAllSequenceMenuItem_actionPerformed(null);
371
372           break;
373
374         case KeyEvent.VK_DOWN:
375           if (evt.isAltDown() || !viewport.cursorMode)
376             moveSelectedSequences(false);
377           if (viewport.cursorMode)
378             alignPanel.seqPanel.moveCursor(0, 1);
379           break;
380
381         case KeyEvent.VK_UP:
382           if (evt.isAltDown() || !viewport.cursorMode)
383             moveSelectedSequences(true);
384           if (viewport.cursorMode)
385             alignPanel.seqPanel.moveCursor(0, -1);
386
387           break;
388
389         case KeyEvent.VK_LEFT:
390           if (evt.isAltDown() || !viewport.cursorMode)
391             slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
392           else
393             alignPanel.seqPanel.moveCursor(-1, 0);
394
395           break;
396
397         case KeyEvent.VK_RIGHT:
398           if (evt.isAltDown() || !viewport.cursorMode)
399             slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
400           else
401             alignPanel.seqPanel.moveCursor(1, 0);
402           break;
403
404         case KeyEvent.VK_SPACE:
405           if (viewport.cursorMode)
406           {
407             alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
408                     || evt.isShiftDown() || evt.isAltDown());
409           }
410           break;
411
412         // case KeyEvent.VK_A:
413         // if (viewport.cursorMode)
414         // {
415         // alignPanel.seqPanel.insertNucAtCursor(false,"A");
416         // //System.out.println("A");
417         // }
418         // break;
419         /*
420          * case KeyEvent.VK_CLOSE_BRACKET: if (viewport.cursorMode) {
421          * System.out.println("closing bracket"); } break;
422          */
423         case KeyEvent.VK_DELETE:
424         case KeyEvent.VK_BACK_SPACE:
425           if (!viewport.cursorMode)
426           {
427             cut_actionPerformed(null);
428           }
429           else
430           {
431             alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
432                     || evt.isShiftDown() || evt.isAltDown());
433           }
434
435           break;
436
437         case KeyEvent.VK_S:
438           if (viewport.cursorMode)
439           {
440             alignPanel.seqPanel.setCursorRow();
441           }
442           break;
443         case KeyEvent.VK_C:
444           if (viewport.cursorMode && !evt.isControlDown())
445           {
446             alignPanel.seqPanel.setCursorColumn();
447           }
448           break;
449         case KeyEvent.VK_P:
450           if (viewport.cursorMode)
451           {
452             alignPanel.seqPanel.setCursorPosition();
453           }
454           break;
455
456         case KeyEvent.VK_ENTER:
457         case KeyEvent.VK_COMMA:
458           if (viewport.cursorMode)
459           {
460             alignPanel.seqPanel.setCursorRowAndColumn();
461           }
462           break;
463
464         case KeyEvent.VK_Q:
465           if (viewport.cursorMode)
466           {
467             alignPanel.seqPanel.setSelectionAreaAtCursor(true);
468           }
469           break;
470         case KeyEvent.VK_M:
471           if (viewport.cursorMode)
472           {
473             alignPanel.seqPanel.setSelectionAreaAtCursor(false);
474           }
475           break;
476
477         case KeyEvent.VK_F2:
478           viewport.cursorMode = !viewport.cursorMode;
479           statusBar.setText("Keyboard editing mode is "
480                   + (viewport.cursorMode ? "on" : "off"));
481           if (viewport.cursorMode)
482           {
483             alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
484             alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
485           }
486           alignPanel.seqPanel.seqCanvas.repaint();
487           break;
488
489         case KeyEvent.VK_F1:
490           try
491           {
492             ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
493             java.net.URL url = javax.help.HelpSet.findHelpSet(cl,
494                     "help/help");
495             javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
496
497             javax.help.HelpBroker hb = hs.createHelpBroker();
498             hb.setCurrentID("home");
499             hb.setDisplayed(true);
500           } catch (Exception ex)
501           {
502             ex.printStackTrace();
503           }
504           break;
505         case KeyEvent.VK_H:
506         {
507           boolean toggleSeqs = !evt.isControlDown();
508           boolean toggleCols = !evt.isShiftDown();
509           toggleHiddenRegions(toggleSeqs, toggleCols);
510           break;
511         }
512         case KeyEvent.VK_PAGE_UP:
513           if (viewport.wrapAlignment)
514           {
515             alignPanel.scrollUp(true);
516           }
517           else
518           {
519             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
520                     - viewport.endSeq + viewport.startSeq);
521           }
522           break;
523         case KeyEvent.VK_PAGE_DOWN:
524           if (viewport.wrapAlignment)
525           {
526             alignPanel.scrollUp(false);
527           }
528           else
529           {
530             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
531                     + viewport.endSeq - viewport.startSeq);
532           }
533           break;
534         }
535       }
536
537       @Override
538       public void keyReleased(KeyEvent evt)
539       {
540         switch (evt.getKeyCode())
541         {
542         case KeyEvent.VK_LEFT:
543           if (evt.isAltDown() || !viewport.cursorMode)
544             viewport.firePropertyChange("alignment", null, viewport
545                     .getAlignment().getSequences());
546           break;
547
548         case KeyEvent.VK_RIGHT:
549           if (evt.isAltDown() || !viewport.cursorMode)
550             viewport.firePropertyChange("alignment", null, viewport
551                     .getAlignment().getSequences());
552           break;
553         }
554       }
555     });
556   }
557
558   public void addAlignmentPanel(final AlignmentPanel ap, boolean newPanel)
559   {
560     ap.alignFrame = this;
561
562     alignPanels.addElement(ap);
563
564     PaintRefresher.Register(ap, ap.av.getSequenceSetId());
565
566     int aSize = alignPanels.size();
567
568     tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
569
570     if (aSize == 1 && ap.av.viewName == null)
571     {
572       this.getContentPane().add(ap, BorderLayout.CENTER);
573     }
574     else
575     {
576       if (aSize == 2)
577       {
578         setInitialTabVisible();
579       }
580
581       expandViews.setEnabled(true);
582       gatherViews.setEnabled(true);
583       tabbedPane.addTab(ap.av.viewName, ap);
584
585       ap.setVisible(false);
586     }
587
588     if (newPanel)
589     {
590       if (ap.av.isPadGaps())
591       {
592         ap.av.getAlignment().padGaps();
593       }
594       ap.av.updateConservation(ap);
595       ap.av.updateConsensus(ap);
596       ap.av.updateStrucConsensus(ap);
597     }
598   }
599
600   public void setInitialTabVisible()
601   {
602     expandViews.setEnabled(true);
603     gatherViews.setEnabled(true);
604     tabbedPane.setVisible(true);
605     AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
606     tabbedPane.addTab(first.av.viewName, first);
607     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
608   }
609
610   public AlignViewport getViewport()
611   {
612     return viewport;
613   }
614
615   /* Set up intrinsic listeners for dynamically generated GUI bits. */
616   private void addServiceListeners()
617   {
618     final java.beans.PropertyChangeListener thisListener;
619     Desktop.instance.addJalviewPropertyChangeListener("services",
620             thisListener = new java.beans.PropertyChangeListener()
621             {
622               @Override
623               public void propertyChange(PropertyChangeEvent evt)
624               {
625                 // // System.out.println("Discoverer property change.");
626                 // if (evt.getPropertyName().equals("services"))
627                 {
628                   SwingUtilities.invokeLater(new Runnable()
629                   {
630
631                     @Override
632                     public void run()
633                     {
634                       System.err
635                               .println("Rebuild WS Menu for service change");
636                       BuildWebServiceMenu();
637                     }
638
639                   });
640                 }
641               }
642             });
643     addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
644     {
645       @Override
646       public void internalFrameClosed(
647               javax.swing.event.InternalFrameEvent evt)
648       {
649         System.out.println("deregistering discoverer listener");
650         Desktop.instance.removeJalviewPropertyChangeListener("services",
651                 thisListener);
652         closeMenuItem_actionPerformed(true);
653       };
654     });
655     // Finally, build the menu once to get current service state
656     new Thread(new Runnable()
657     {
658       @Override
659       public void run()
660       {
661         BuildWebServiceMenu();
662       }
663     }).start();
664   }
665
666   public void setGUINucleotide(boolean nucleotide)
667   {
668     showTranslation.setVisible(nucleotide);
669     conservationMenuItem.setEnabled(!nucleotide);
670     modifyConservation.setEnabled(!nucleotide);
671     showGroupConservation.setEnabled(!nucleotide);
672     rnahelicesColour.setEnabled(nucleotide);
673     purinePyrimidineColour.setEnabled(nucleotide);
674     // Remember AlignFrame always starts as protein
675     // if (!nucleotide)
676     // {
677     // showTr
678     // calculateMenu.remove(calculateMenu.getItemCount() - 2);
679     // }
680   }
681
682   /**
683    * set up menus for the currently viewport. This may be called after any
684    * operation that affects the data in the current view (selection changed,
685    * etc) to update the menus to reflect the new state.
686    */
687   public void setMenusForViewport()
688   {
689     setMenusFromViewport(viewport);
690   }
691
692   /**
693    * Need to call this method when tabs are selected for multiple views, or when
694    * loading from Jalview2XML.java
695    * 
696    * @param av
697    *          AlignViewport
698    */
699   void setMenusFromViewport(AlignViewport av)
700   {
701     padGapsMenuitem.setSelected(av.isPadGaps());
702     colourTextMenuItem.setSelected(av.showColourText);
703     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
704     conservationMenuItem.setSelected(av.getConservationSelected());
705     seqLimits.setSelected(av.getShowJVSuffix());
706     idRightAlign.setSelected(av.rightAlignIds);
707     centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
708     renderGapsMenuItem.setSelected(av.renderGaps);
709     wrapMenuItem.setSelected(av.wrapAlignment);
710     scaleAbove.setVisible(av.wrapAlignment);
711     scaleLeft.setVisible(av.wrapAlignment);
712     scaleRight.setVisible(av.wrapAlignment);
713     annotationPanelMenuItem.setState(av.showAnnotation);
714     viewBoxesMenuItem.setSelected(av.showBoxes);
715     viewTextMenuItem.setSelected(av.showText);
716     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
717     showGroupConsensus.setSelected(av.isShowGroupConsensus());
718     showGroupConservation.setSelected(av.isShowGroupConservation());
719     showConsensusHistogram.setSelected(av.isShowConsensusHistogram());
720     showSequenceLogo.setSelected(av.isShowSequenceLogo());
721     normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
722
723     setColourSelected(ColourSchemeProperty.getColourName(av
724             .getGlobalColourScheme()));
725
726     showSeqFeatures.setSelected(av.showSequenceFeatures);
727     hiddenMarkers.setState(av.showHiddenMarkers);
728     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
729     showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
730     showDbRefsMenuitem.setSelected(av.isShowDbRefs());
731     autoCalculate.setSelected(av.autoCalculateConsensus);
732     sortByTree.setSelected(av.sortByTree);
733     listenToViewSelections.setSelected(av.followSelection);
734     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
735     rnahelicesColour
736             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
737     setShowProductsEnabled();
738
739     updateEditMenuBar();
740   }
741
742   // methods for implementing IProgressIndicator
743   // need to refactor to a reusable stub class
744   Hashtable progressBars, progressBarHandlers;
745
746   /*
747    * (non-Javadoc)
748    * 
749    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
750    */
751   @Override
752   public void setProgressBar(String message, long id)
753   {
754     if (progressBars == null)
755     {
756       progressBars = new Hashtable();
757       progressBarHandlers = new Hashtable();
758     }
759
760     JPanel progressPanel;
761     Long lId = new Long(id);
762     GridLayout layout = (GridLayout) statusPanel.getLayout();
763     if (progressBars.get(lId) != null)
764     {
765       progressPanel = (JPanel) progressBars.get(new Long(id));
766       statusPanel.remove(progressPanel);
767       progressBars.remove(lId);
768       progressPanel = null;
769       if (message != null)
770       {
771         statusBar.setText(message);
772       }
773       if (progressBarHandlers.contains(lId))
774       {
775         progressBarHandlers.remove(lId);
776       }
777       layout.setRows(layout.getRows() - 1);
778     }
779     else
780     {
781       progressPanel = new JPanel(new BorderLayout(10, 5));
782
783       JProgressBar progressBar = new JProgressBar();
784       progressBar.setIndeterminate(true);
785
786       progressPanel.add(new JLabel(message), BorderLayout.WEST);
787       progressPanel.add(progressBar, BorderLayout.CENTER);
788
789       layout.setRows(layout.getRows() + 1);
790       statusPanel.add(progressPanel);
791
792       progressBars.put(lId, progressPanel);
793     }
794     // update GUI
795     // setMenusForViewport();
796     validate();
797   }
798
799   @Override
800   public void registerHandler(final long id,
801           final IProgressIndicatorHandler handler)
802   {
803     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
804     {
805       throw new Error(
806               "call setProgressBar before registering the progress bar's handler.");
807     }
808     progressBarHandlers.put(new Long(id), handler);
809     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
810     if (handler.canCancel())
811     {
812       JButton cancel = new JButton("Cancel");
813       final IProgressIndicator us = this;
814       cancel.addActionListener(new ActionListener()
815       {
816
817         @Override
818         public void actionPerformed(ActionEvent e)
819         {
820           handler.cancelActivity(id);
821           us.setProgressBar(
822                   "Cancelled "
823                           + ((JLabel) progressPanel.getComponent(0))
824                                   .getText(), id);
825         }
826       });
827       progressPanel.add(cancel, BorderLayout.EAST);
828     }
829   }
830
831   /**
832    * 
833    * @return true if any progress bars are still active
834    */
835   @Override
836   public boolean operationInProgress()
837   {
838     if (progressBars != null && progressBars.size() > 0)
839     {
840       return true;
841     }
842     return false;
843   }
844
845   /*
846    * Added so Castor Mapping file can obtain Jalview Version
847    */
848   public String getVersion()
849   {
850     return jalview.bin.Cache.getProperty("VERSION");
851   }
852
853   public FeatureRenderer getFeatureRenderer()
854   {
855     return alignPanel.seqPanel.seqCanvas.getFeatureRenderer();
856   }
857
858   @Override
859   public void fetchSequence_actionPerformed(ActionEvent e)
860   {
861     new SequenceFetcher(this);
862   }
863
864   @Override
865   public void addFromFile_actionPerformed(ActionEvent e)
866   {
867     Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
868   }
869
870   @Override
871   public void reload_actionPerformed(ActionEvent e)
872   {
873     if (fileName != null)
874     {
875       // TODO: JAL-1108 - ensure all associated frames are closed regardless of
876       // originating file's format
877       // TODO: work out how to recover feature settings for correct view(s) when
878       // file is reloaded.
879       if (currentFileFormat.equals("Jalview"))
880       {
881         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
882         for (int i = 0; i < frames.length; i++)
883         {
884           if (frames[i] instanceof AlignFrame && frames[i] != this
885                   && ((AlignFrame) frames[i]).fileName != null
886                   && ((AlignFrame) frames[i]).fileName.equals(fileName))
887           {
888             try
889             {
890               frames[i].setSelected(true);
891               Desktop.instance.closeAssociatedWindows();
892             } catch (java.beans.PropertyVetoException ex)
893             {
894             }
895           }
896
897         }
898         Desktop.instance.closeAssociatedWindows();
899
900         FileLoader loader = new FileLoader();
901         String protocol = fileName.startsWith("http:") ? "URL" : "File";
902         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
903       }
904       else
905       {
906         Rectangle bounds = this.getBounds();
907
908         FileLoader loader = new FileLoader();
909         String protocol = fileName.startsWith("http:") ? "URL" : "File";
910         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
911                 protocol, currentFileFormat);
912
913         newframe.setBounds(bounds);
914         if (featureSettings != null && featureSettings.isShowing())
915         {
916           final Rectangle fspos = featureSettings.frame.getBounds();
917           // TODO: need a 'show feature settings' function that takes bounds -
918           // need to refactor Desktop.addFrame
919           newframe.featureSettings_actionPerformed(null);
920           final FeatureSettings nfs = newframe.featureSettings;
921           SwingUtilities.invokeLater(new Runnable()
922           {
923             @Override
924             public void run()
925             {
926               nfs.frame.setBounds(fspos);
927             }
928           });
929           this.featureSettings.close();
930           this.featureSettings = null;
931         }
932         this.closeMenuItem_actionPerformed(true);
933       }
934     }
935   }
936
937   @Override
938   public void addFromText_actionPerformed(ActionEvent e)
939   {
940     Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport);
941   }
942
943   @Override
944   public void addFromURL_actionPerformed(ActionEvent e)
945   {
946     Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
947   }
948
949   @Override
950   public void save_actionPerformed(ActionEvent e)
951   {
952     if (fileName == null
953             || (currentFileFormat == null || !jalview.io.FormatAdapter
954                     .isValidIOFormat(currentFileFormat, true))
955             || fileName.startsWith("http"))
956     {
957       saveAs_actionPerformed(null);
958     }
959     else
960     {
961       saveAlignment(fileName, currentFileFormat);
962     }
963   }
964
965   /**
966    * DOCUMENT ME!
967    * 
968    * @param e
969    *          DOCUMENT ME!
970    */
971   @Override
972   public void saveAs_actionPerformed(ActionEvent e)
973   {
974     JalviewFileChooser chooser = new JalviewFileChooser(
975             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
976             jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
977             jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
978             currentFileFormat, false);
979
980     chooser.setFileView(new JalviewFileView());
981     chooser.setDialogTitle("Save Alignment to file");
982     chooser.setToolTipText("Save");
983
984     int value = chooser.showSaveDialog(this);
985
986     if (value == JalviewFileChooser.APPROVE_OPTION)
987     {
988       currentFileFormat = chooser.getSelectedFormat();
989       if (currentFileFormat == null)
990       {
991         JOptionPane.showInternalMessageDialog(Desktop.desktop,
992                 "You must select a file format before saving!",
993                 "File format not specified", JOptionPane.WARNING_MESSAGE);
994         value = chooser.showSaveDialog(this);
995         return;
996       }
997
998       fileName = chooser.getSelectedFile().getPath();
999
1000       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
1001               currentFileFormat);
1002
1003       jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
1004       if (currentFileFormat.indexOf(" ") > -1)
1005       {
1006         currentFileFormat = currentFileFormat.substring(0,
1007                 currentFileFormat.indexOf(" "));
1008       }
1009       saveAlignment(fileName, currentFileFormat);
1010     }
1011   }
1012
1013   public boolean saveAlignment(String file, String format)
1014   {
1015     boolean success = true;
1016
1017     if (format.equalsIgnoreCase("Jalview"))
1018     {
1019       String shortName = title;
1020
1021       if (shortName.indexOf(java.io.File.separatorChar) > -1)
1022       {
1023         shortName = shortName.substring(shortName
1024                 .lastIndexOf(java.io.File.separatorChar) + 1);
1025       }
1026
1027       success = new Jalview2XML().SaveAlignment(this, file, shortName);
1028
1029       statusBar.setText("Successfully saved to file: " + fileName + " in "
1030               + format + " format.");
1031
1032     }
1033     else
1034     {
1035       if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
1036       {
1037         warningMessage("Cannot save file " + fileName + " using format "
1038                 + format, "Alignment output format not supported");
1039         saveAs_actionPerformed(null);
1040         // JBPNote need to have a raise_gui flag here
1041         return false;
1042       }
1043
1044       String[] omitHidden = null;
1045
1046       if (viewport.hasHiddenColumns())
1047       {
1048         int reply = JOptionPane
1049                 .showInternalConfirmDialog(
1050                         Desktop.desktop,
1051                         "The Alignment contains hidden columns."
1052                                 + "\nDo you want to save only the visible alignment?",
1053                         "Save / Omit Hidden Columns",
1054                         JOptionPane.YES_NO_OPTION,
1055                         JOptionPane.QUESTION_MESSAGE);
1056
1057         if (reply == JOptionPane.YES_OPTION)
1058         {
1059           omitHidden = viewport.getViewAsString(false);
1060         }
1061       }
1062       FormatAdapter f = new FormatAdapter();
1063       String output = f.formatSequences(format,
1064               viewport.getAlignment(), // class cast exceptions will
1065               // occur in the distant future
1066               omitHidden, f.getCacheSuffixDefault(format),
1067               viewport.getColumnSelection());
1068
1069       if (output == null)
1070       {
1071         success = false;
1072       }
1073       else
1074       {
1075         try
1076         {
1077           java.io.PrintWriter out = new java.io.PrintWriter(
1078                   new java.io.FileWriter(file));
1079
1080           out.print(output);
1081           out.close();
1082           this.setTitle(file);
1083           statusBar.setText("Successfully saved to file: " + fileName
1084                   + " in " + format + " format.");
1085         } catch (Exception ex)
1086         {
1087           success = false;
1088           ex.printStackTrace();
1089         }
1090       }
1091     }
1092
1093     if (!success)
1094     {
1095       JOptionPane.showInternalMessageDialog(this, "Couldn't save file: "
1096               + fileName, "Error Saving File", JOptionPane.WARNING_MESSAGE);
1097     }
1098
1099     return success;
1100   }
1101
1102   private void warningMessage(String warning, String title)
1103   {
1104     if (new jalview.util.Platform().isHeadless())
1105     {
1106       System.err.println("Warning: " + title + "\nWarning: " + warning);
1107
1108     }
1109     else
1110     {
1111       JOptionPane.showInternalMessageDialog(this, warning, title,
1112               JOptionPane.WARNING_MESSAGE);
1113     }
1114     return;
1115   }
1116
1117   /**
1118    * DOCUMENT ME!
1119    * 
1120    * @param e
1121    *          DOCUMENT ME!
1122    */
1123   @Override
1124   protected void outputText_actionPerformed(ActionEvent e)
1125   {
1126     String[] omitHidden = null;
1127
1128     if (viewport.hasHiddenColumns())
1129     {
1130       int reply = JOptionPane
1131               .showInternalConfirmDialog(
1132                       Desktop.desktop,
1133                       "The Alignment contains hidden columns."
1134                               + "\nDo you want to output only the visible alignment?",
1135                       "Save / Omit Hidden Columns",
1136                       JOptionPane.YES_NO_OPTION,
1137                       JOptionPane.QUESTION_MESSAGE);
1138
1139       if (reply == JOptionPane.YES_OPTION)
1140       {
1141         omitHidden = viewport.getViewAsString(false);
1142       }
1143     }
1144
1145     CutAndPasteTransfer cap = new CutAndPasteTransfer();
1146     cap.setForInput(null);
1147
1148     try
1149     {
1150       cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
1151               viewport.getAlignment(), omitHidden,
1152               viewport.getColumnSelection()));
1153       Desktop.addInternalFrame(cap,
1154               "Alignment output - " + e.getActionCommand(), 600, 500);
1155     } catch (OutOfMemoryError oom)
1156     {
1157       new OOMWarning("Outputting alignment as " + e.getActionCommand(), oom);
1158       cap.dispose();
1159     }
1160
1161   }
1162
1163   /**
1164    * DOCUMENT ME!
1165    * 
1166    * @param e
1167    *          DOCUMENT ME!
1168    */
1169   @Override
1170   protected void htmlMenuItem_actionPerformed(ActionEvent e)
1171   {
1172     new HTMLOutput(alignPanel,
1173             alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
1174             alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
1175   }
1176
1177   public void createImageMap(File file, String image)
1178   {
1179     alignPanel.makePNGImageMap(file, image);
1180   }
1181
1182   /**
1183    * DOCUMENT ME!
1184    * 
1185    * @param e
1186    *          DOCUMENT ME!
1187    */
1188   @Override
1189   public void createPNG(File f)
1190   {
1191     alignPanel.makePNG(f);
1192   }
1193
1194   /**
1195    * DOCUMENT ME!
1196    * 
1197    * @param e
1198    *          DOCUMENT ME!
1199    */
1200   @Override
1201   public void createEPS(File f)
1202   {
1203     alignPanel.makeEPS(f);
1204   }
1205
1206   @Override
1207   public void pageSetup_actionPerformed(ActionEvent e)
1208   {
1209     PrinterJob printJob = PrinterJob.getPrinterJob();
1210     PrintThread.pf = printJob.pageDialog(printJob.defaultPage());
1211   }
1212
1213   /**
1214    * DOCUMENT ME!
1215    * 
1216    * @param e
1217    *          DOCUMENT ME!
1218    */
1219   @Override
1220   public void printMenuItem_actionPerformed(ActionEvent e)
1221   {
1222     // Putting in a thread avoids Swing painting problems
1223     PrintThread thread = new PrintThread(alignPanel);
1224     thread.start();
1225   }
1226
1227   @Override
1228   public void exportFeatures_actionPerformed(ActionEvent e)
1229   {
1230     new AnnotationExporter().exportFeatures(alignPanel);
1231   }
1232
1233   @Override
1234   public void exportAnnotations_actionPerformed(ActionEvent e)
1235   {
1236     new AnnotationExporter().exportAnnotations(alignPanel,
1237             viewport.showAnnotation ? viewport.getAlignment()
1238                     .getAlignmentAnnotation() : null, viewport
1239                     .getAlignment().getGroups(), ((Alignment) viewport
1240                     .getAlignment()).alignmentProperties);
1241   }
1242
1243   @Override
1244   public void associatedData_actionPerformed(ActionEvent e)
1245   {
1246     // Pick the tree file
1247     JalviewFileChooser chooser = new JalviewFileChooser(
1248             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1249     chooser.setFileView(new JalviewFileView());
1250     chooser.setDialogTitle("Load Jalview Annotations or Features File");
1251     chooser.setToolTipText("Load Jalview Annotations / Features file");
1252
1253     int value = chooser.showOpenDialog(null);
1254
1255     if (value == JalviewFileChooser.APPROVE_OPTION)
1256     {
1257       String choice = chooser.getSelectedFile().getPath();
1258       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
1259       loadJalviewDataFile(choice, null, null, null);
1260     }
1261
1262   }
1263
1264   /**
1265    * Close the current view or all views in the alignment frame. If the frame
1266    * only contains one view then the alignment will be removed from memory.
1267    * 
1268    * @param closeAllTabs
1269    */
1270   @Override
1271   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1272   {
1273     if (alignPanels != null && alignPanels.size() < 2)
1274     {
1275       closeAllTabs = true;
1276     }
1277
1278     try
1279     {
1280       if (alignPanels != null)
1281       {
1282         if (closeAllTabs)
1283         {
1284           if (this.isClosed())
1285           {
1286             // really close all the windows - otherwise wait till
1287             // setClosed(true) is called
1288             for (int i = 0; i < alignPanels.size(); i++)
1289             {
1290               AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
1291               ap.closePanel();
1292             }
1293           }
1294         }
1295         else
1296         {
1297           closeView(alignPanel);
1298         }
1299       }
1300
1301       if (closeAllTabs)
1302       {
1303         this.setClosed(true);
1304       }
1305     } catch (Exception ex)
1306     {
1307       ex.printStackTrace();
1308     }
1309   }
1310
1311   /**
1312    * close alignPanel2 and shuffle tabs appropriately.
1313    * 
1314    * @param alignPanel2
1315    */
1316   public void closeView(AlignmentPanel alignPanel2)
1317   {
1318     int index = tabbedPane.getSelectedIndex();
1319     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
1320     alignPanels.removeElement(alignPanel2);
1321     // Unnecessary
1322     // if (viewport == alignPanel2.av)
1323     // {
1324     // viewport = null;
1325     // }
1326     alignPanel2.closePanel();
1327     alignPanel2 = null;
1328
1329     tabbedPane.removeTabAt(closedindex);
1330     tabbedPane.validate();
1331
1332     if (index > closedindex || index == tabbedPane.getTabCount())
1333     {
1334       // modify currently selected tab index if necessary.
1335       index--;
1336     }
1337
1338     this.tabSelectionChanged(index);
1339   }
1340
1341   /**
1342    * DOCUMENT ME!
1343    */
1344   void updateEditMenuBar()
1345   {
1346
1347     if (viewport.historyList.size() > 0)
1348     {
1349       undoMenuItem.setEnabled(true);
1350       CommandI command = (CommandI) viewport.historyList.peek();
1351       undoMenuItem.setText("Undo " + command.getDescription());
1352     }
1353     else
1354     {
1355       undoMenuItem.setEnabled(false);
1356       undoMenuItem.setText("Undo");
1357     }
1358
1359     if (viewport.redoList.size() > 0)
1360     {
1361       redoMenuItem.setEnabled(true);
1362
1363       CommandI command = (CommandI) viewport.redoList.peek();
1364       redoMenuItem.setText("Redo " + command.getDescription());
1365     }
1366     else
1367     {
1368       redoMenuItem.setEnabled(false);
1369       redoMenuItem.setText("Redo");
1370     }
1371   }
1372
1373   public void addHistoryItem(CommandI command)
1374   {
1375     if (command.getSize() > 0)
1376     {
1377       viewport.historyList.push(command);
1378       viewport.redoList.clear();
1379       updateEditMenuBar();
1380       viewport.updateHiddenColumns();
1381       // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
1382       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1383       // viewport.getColumnSelection()
1384       // .getHiddenColumns().size() > 0);
1385     }
1386   }
1387
1388   /**
1389    * 
1390    * @return alignment objects for all views
1391    */
1392   AlignmentI[] getViewAlignments()
1393   {
1394     if (alignPanels != null)
1395     {
1396       Enumeration e = alignPanels.elements();
1397       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1398       for (int i = 0; e.hasMoreElements(); i++)
1399       {
1400         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1401       }
1402       return als;
1403     }
1404     if (viewport != null)
1405     {
1406       return new AlignmentI[]
1407       { viewport.getAlignment() };
1408     }
1409     return null;
1410   }
1411
1412   /**
1413    * DOCUMENT ME!
1414    * 
1415    * @param e
1416    *          DOCUMENT ME!
1417    */
1418   @Override
1419   protected void undoMenuItem_actionPerformed(ActionEvent e)
1420   {
1421     if (viewport.historyList.empty())
1422       return;
1423     CommandI command = (CommandI) viewport.historyList.pop();
1424     viewport.redoList.push(command);
1425     command.undoCommand(getViewAlignments());
1426
1427     AlignViewport originalSource = getOriginatingSource(command);
1428     updateEditMenuBar();
1429
1430     if (originalSource != null)
1431     {
1432       if (originalSource != viewport)
1433       {
1434         Cache.log
1435                 .warn("Implementation worry: mismatch of viewport origin for undo");
1436       }
1437       originalSource.updateHiddenColumns();
1438       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1439       // null
1440       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1441       // viewport.getColumnSelection()
1442       // .getHiddenColumns().size() > 0);
1443       originalSource.firePropertyChange("alignment", null, originalSource
1444               .getAlignment().getSequences());
1445     }
1446   }
1447
1448   /**
1449    * DOCUMENT ME!
1450    * 
1451    * @param e
1452    *          DOCUMENT ME!
1453    */
1454   @Override
1455   protected void redoMenuItem_actionPerformed(ActionEvent e)
1456   {
1457     if (viewport.redoList.size() < 1)
1458     {
1459       return;
1460     }
1461
1462     CommandI command = (CommandI) viewport.redoList.pop();
1463     viewport.historyList.push(command);
1464     command.doCommand(getViewAlignments());
1465
1466     AlignViewport originalSource = getOriginatingSource(command);
1467     updateEditMenuBar();
1468
1469     if (originalSource != null)
1470     {
1471
1472       if (originalSource != viewport)
1473       {
1474         Cache.log
1475                 .warn("Implementation worry: mismatch of viewport origin for redo");
1476       }
1477       originalSource.updateHiddenColumns();
1478       // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1479       // null
1480       // && viewport.getColumnSelection().getHiddenColumns() != null &&
1481       // viewport.getColumnSelection()
1482       // .getHiddenColumns().size() > 0);
1483       originalSource.firePropertyChange("alignment", null, originalSource
1484               .getAlignment().getSequences());
1485     }
1486   }
1487
1488   AlignViewport getOriginatingSource(CommandI command)
1489   {
1490     AlignViewport originalSource = null;
1491     // For sequence removal and addition, we need to fire
1492     // the property change event FROM the viewport where the
1493     // original alignment was altered
1494     AlignmentI al = null;
1495     if (command instanceof EditCommand)
1496     {
1497       EditCommand editCommand = (EditCommand) command;
1498       al = editCommand.getAlignment();
1499       Vector comps = (Vector) PaintRefresher.components.get(viewport
1500               .getSequenceSetId());
1501
1502       for (int i = 0; i < comps.size(); i++)
1503       {
1504         if (comps.elementAt(i) instanceof AlignmentPanel)
1505         {
1506           if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
1507           {
1508             originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
1509             break;
1510           }
1511         }
1512       }
1513     }
1514
1515     if (originalSource == null)
1516     {
1517       // The original view is closed, we must validate
1518       // the current view against the closed view first
1519       if (al != null)
1520       {
1521         PaintRefresher.validateSequences(al, viewport.getAlignment());
1522       }
1523
1524       originalSource = viewport;
1525     }
1526
1527     return originalSource;
1528   }
1529
1530   /**
1531    * DOCUMENT ME!
1532    * 
1533    * @param up
1534    *          DOCUMENT ME!
1535    */
1536   public void moveSelectedSequences(boolean up)
1537   {
1538     SequenceGroup sg = viewport.getSelectionGroup();
1539
1540     if (sg == null)
1541     {
1542       return;
1543     }
1544     viewport.getAlignment().moveSelectedSequencesByOne(sg,
1545             viewport.getHiddenRepSequences(), up);
1546     alignPanel.paintAlignment(true);
1547   }
1548
1549   synchronized void slideSequences(boolean right, int size)
1550   {
1551     List<SequenceI> sg = new Vector();
1552     if (viewport.cursorMode)
1553     {
1554       sg.add(viewport.getAlignment().getSequenceAt(
1555               alignPanel.seqPanel.seqCanvas.cursorY));
1556     }
1557     else if (viewport.getSelectionGroup() != null
1558             && viewport.getSelectionGroup().getSize() != viewport
1559                     .getAlignment().getHeight())
1560     {
1561       sg = viewport.getSelectionGroup().getSequences(
1562               viewport.getHiddenRepSequences());
1563     }
1564
1565     if (sg.size() < 1)
1566     {
1567       return;
1568     }
1569
1570     Vector invertGroup = new Vector();
1571
1572     for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
1573     {
1574       if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
1575         invertGroup.add(viewport.getAlignment().getSequenceAt(i));
1576     }
1577
1578     SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
1579
1580     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1581     for (int i = 0; i < invertGroup.size(); i++)
1582       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1583
1584     SlideSequencesCommand ssc;
1585     if (right)
1586       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
1587               size, viewport.getGapCharacter());
1588     else
1589       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
1590               size, viewport.getGapCharacter());
1591
1592     int groupAdjustment = 0;
1593     if (ssc.getGapsInsertedBegin() && right)
1594     {
1595       if (viewport.cursorMode)
1596         alignPanel.seqPanel.moveCursor(size, 0);
1597       else
1598         groupAdjustment = size;
1599     }
1600     else if (!ssc.getGapsInsertedBegin() && !right)
1601     {
1602       if (viewport.cursorMode)
1603         alignPanel.seqPanel.moveCursor(-size, 0);
1604       else
1605         groupAdjustment = -size;
1606     }
1607
1608     if (groupAdjustment != 0)
1609     {
1610       viewport.getSelectionGroup().setStartRes(
1611               viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1612       viewport.getSelectionGroup().setEndRes(
1613               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1614     }
1615
1616     boolean appendHistoryItem = false;
1617     if (viewport.historyList != null && viewport.historyList.size() > 0
1618             && viewport.historyList.peek() instanceof SlideSequencesCommand)
1619     {
1620       appendHistoryItem = ssc
1621               .appendSlideCommand((SlideSequencesCommand) viewport.historyList
1622                       .peek());
1623     }
1624
1625     if (!appendHistoryItem)
1626       addHistoryItem(ssc);
1627
1628     repaint();
1629   }
1630
1631   /**
1632    * DOCUMENT ME!
1633    * 
1634    * @param e
1635    *          DOCUMENT ME!
1636    */
1637   @Override
1638   protected void copy_actionPerformed(ActionEvent e)
1639   {
1640     System.gc();
1641     if (viewport.getSelectionGroup() == null)
1642     {
1643       return;
1644     }
1645     // TODO: preserve the ordering of displayed alignment annotation in any
1646     // internal paste (particularly sequence associated annotation)
1647     SequenceI[] seqs = viewport.getSelectionAsNewSequence();
1648     String[] omitHidden = null;
1649
1650     if (viewport.hasHiddenColumns())
1651     {
1652       omitHidden = viewport.getViewAsString(true);
1653     }
1654
1655     String output = new FormatAdapter().formatSequences("Fasta", seqs,
1656             omitHidden);
1657
1658     StringSelection ss = new StringSelection(output);
1659
1660     try
1661     {
1662       jalview.gui.Desktop.internalCopy = true;
1663       // Its really worth setting the clipboard contents
1664       // to empty before setting the large StringSelection!!
1665       Toolkit.getDefaultToolkit().getSystemClipboard()
1666               .setContents(new StringSelection(""), null);
1667
1668       Toolkit.getDefaultToolkit().getSystemClipboard()
1669               .setContents(ss, Desktop.instance);
1670     } catch (OutOfMemoryError er)
1671     {
1672       new OOMWarning("copying region", er);
1673       return;
1674     }
1675
1676     Vector hiddenColumns = null;
1677     if (viewport.hasHiddenColumns())
1678     {
1679       hiddenColumns = new Vector();
1680       int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport
1681               .getSelectionGroup().getEndRes();
1682       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
1683               .size(); i++)
1684       {
1685         int[] region = (int[]) viewport.getColumnSelection()
1686                 .getHiddenColumns().elementAt(i);
1687         if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff)
1688         {
1689           hiddenColumns.addElement(new int[]
1690           { region[0] - hiddenOffset, region[1] - hiddenOffset });
1691         }
1692       }
1693     }
1694
1695     Desktop.jalviewClipboard = new Object[]
1696     { seqs, viewport.getAlignment().getDataset(), hiddenColumns };
1697     statusBar.setText("Copied " + seqs.length + " sequences to clipboard.");
1698   }
1699
1700   /**
1701    * DOCUMENT ME!
1702    * 
1703    * @param e
1704    *          DOCUMENT ME!
1705    */
1706   @Override
1707   protected void pasteNew_actionPerformed(ActionEvent e)
1708   {
1709     paste(true);
1710   }
1711
1712   /**
1713    * DOCUMENT ME!
1714    * 
1715    * @param e
1716    *          DOCUMENT ME!
1717    */
1718   @Override
1719   protected void pasteThis_actionPerformed(ActionEvent e)
1720   {
1721     paste(false);
1722   }
1723
1724   /**
1725    * Paste contents of Jalview clipboard
1726    * 
1727    * @param newAlignment
1728    *          true to paste to a new alignment, otherwise add to this.
1729    */
1730   void paste(boolean newAlignment)
1731   {
1732     boolean externalPaste = true;
1733     try
1734     {
1735       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1736       Transferable contents = c.getContents(this);
1737
1738       if (contents == null)
1739       {
1740         return;
1741       }
1742
1743       String str, format;
1744       try
1745       {
1746         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1747         if (str.length() < 1)
1748         {
1749           return;
1750         }
1751
1752         format = new IdentifyFile().Identify(str, "Paste");
1753
1754       } catch (OutOfMemoryError er)
1755       {
1756         new OOMWarning("Out of memory pasting sequences!!", er);
1757         return;
1758       }
1759
1760       SequenceI[] sequences;
1761       boolean annotationAdded = false;
1762       AlignmentI alignment = null;
1763
1764       if (Desktop.jalviewClipboard != null)
1765       {
1766         // The clipboard was filled from within Jalview, we must use the
1767         // sequences
1768         // And dataset from the copied alignment
1769         SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
1770         // be doubly sure that we create *new* sequence objects.
1771         sequences = new SequenceI[newseq.length];
1772         for (int i = 0; i < newseq.length; i++)
1773         {
1774           sequences[i] = new Sequence(newseq[i]);
1775         }
1776         alignment = new Alignment(sequences);
1777         externalPaste = false;
1778       }
1779       else
1780       {
1781         // parse the clipboard as an alignment.
1782         alignment = new FormatAdapter().readFile(str, "Paste", format);
1783         sequences = alignment.getSequencesArray();
1784       }
1785
1786       int alwidth = 0;
1787       ArrayList<Integer> newGraphGroups = new ArrayList<Integer>();
1788       int fgroup = -1;
1789
1790       if (newAlignment)
1791       {
1792
1793         if (Desktop.jalviewClipboard != null)
1794         {
1795           // dataset is inherited
1796           alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
1797         }
1798         else
1799         {
1800           // new dataset is constructed
1801           alignment.setDataset(null);
1802         }
1803         alwidth = alignment.getWidth() + 1;
1804       }
1805       else
1806       {
1807         AlignmentI pastedal = alignment; // preserve pasted alignment object
1808         // Add pasted sequences and dataset into existing alignment.
1809         alignment = viewport.getAlignment();
1810         alwidth = alignment.getWidth() + 1;
1811         // decide if we need to import sequences from an existing dataset
1812         boolean importDs = Desktop.jalviewClipboard != null
1813                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1814         // importDs==true instructs us to copy over new dataset sequences from
1815         // an existing alignment
1816         Vector newDs = (importDs) ? new Vector() : null; // used to create
1817         // minimum dataset set
1818
1819         for (int i = 0; i < sequences.length; i++)
1820         {
1821           if (importDs)
1822           {
1823             newDs.addElement(null);
1824           }
1825           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1826           // paste
1827           if (importDs && ds != null)
1828           {
1829             if (!newDs.contains(ds))
1830             {
1831               newDs.setElementAt(ds, i);
1832               ds = new Sequence(ds);
1833               // update with new dataset sequence
1834               sequences[i].setDatasetSequence(ds);
1835             }
1836             else
1837             {
1838               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1839             }
1840           }
1841           else
1842           {
1843             // copy and derive new dataset sequence
1844             sequences[i] = sequences[i].deriveSequence();
1845             alignment.getDataset().addSequence(
1846                     sequences[i].getDatasetSequence());
1847             // TODO: avoid creation of duplicate dataset sequences with a
1848             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1849           }
1850           alignment.addSequence(sequences[i]); // merges dataset
1851         }
1852         if (newDs != null)
1853         {
1854           newDs.clear(); // tidy up
1855         }
1856         if (alignment.getAlignmentAnnotation() != null)
1857         {
1858           for (AlignmentAnnotation alan : alignment
1859                   .getAlignmentAnnotation())
1860           {
1861             if (alan.graphGroup > fgroup)
1862             {
1863               fgroup = alan.graphGroup;
1864             }
1865           }
1866         }
1867         if (pastedal.getAlignmentAnnotation() != null)
1868         {
1869           // Add any annotation attached to alignment.
1870           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1871           for (int i = 0; i < alann.length; i++)
1872           {
1873             annotationAdded = true;
1874             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
1875             {
1876               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1877               if (newann.graphGroup > -1)
1878               {
1879                 if (newGraphGroups.size() <= newann.graphGroup
1880                         || newGraphGroups.get(newann.graphGroup) == null)
1881                 {
1882                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
1883                   {
1884                     newGraphGroups.add(q, null);
1885                   }
1886                   newGraphGroups.set(newann.graphGroup, new Integer(
1887                           ++fgroup));
1888                 }
1889                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
1890                         .intValue();
1891               }
1892
1893               newann.padAnnotation(alwidth);
1894               alignment.addAnnotation(newann);
1895             }
1896           }
1897         }
1898       }
1899       if (!newAlignment)
1900       {
1901         // /////
1902         // ADD HISTORY ITEM
1903         //
1904         addHistoryItem(new EditCommand("Add sequences", EditCommand.PASTE,
1905                 sequences, 0, alignment.getWidth(), alignment));
1906       }
1907       // Add any annotations attached to sequences
1908       for (int i = 0; i < sequences.length; i++)
1909       {
1910         if (sequences[i].getAnnotation() != null)
1911         {
1912           AlignmentAnnotation newann;
1913           for (int a = 0; a < sequences[i].getAnnotation().length; a++)
1914           {
1915             annotationAdded = true;
1916             newann = sequences[i].getAnnotation()[a];
1917             newann.adjustForAlignment();
1918             newann.padAnnotation(alwidth);
1919             if (newann.graphGroup > -1)
1920             {
1921               if (newann.graphGroup > -1)
1922               {
1923                 if (newGraphGroups.size() <= newann.graphGroup
1924                         || newGraphGroups.get(newann.graphGroup) == null)
1925                 {
1926                   for (int q = newGraphGroups.size(); q <= newann.graphGroup; q++)
1927                   {
1928                     newGraphGroups.add(q, null);
1929                   }
1930                   newGraphGroups.set(newann.graphGroup, new Integer(
1931                           ++fgroup));
1932                 }
1933                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
1934                         .intValue();
1935               }
1936             }
1937             alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
1938             // was
1939             // duplicated
1940             // earlier
1941             alignment
1942                     .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
1943           }
1944         }
1945       }
1946       if (!newAlignment)
1947       {
1948
1949         // propagate alignment changed.
1950         viewport.setEndSeq(alignment.getHeight());
1951         if (annotationAdded)
1952         {
1953           // Duplicate sequence annotation in all views.
1954           AlignmentI[] alview = this.getViewAlignments();
1955           for (int i = 0; i < sequences.length; i++)
1956           {
1957             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
1958             if (sann == null)
1959               continue;
1960             for (int avnum = 0; avnum < alview.length; avnum++)
1961             {
1962               if (alview[avnum] != alignment)
1963               {
1964                 // duplicate in a view other than the one with input focus
1965                 int avwidth = alview[avnum].getWidth() + 1;
1966                 // this relies on sann being preserved after we
1967                 // modify the sequence's annotation array for each duplication
1968                 for (int a = 0; a < sann.length; a++)
1969                 {
1970                   AlignmentAnnotation newann = new AlignmentAnnotation(
1971                           sann[a]);
1972                   sequences[i].addAlignmentAnnotation(newann);
1973                   newann.padAnnotation(avwidth);
1974                   alview[avnum].addAnnotation(newann); // annotation was
1975                   // duplicated earlier
1976                   // TODO JAL-1145 graphGroups are not updated for sequence
1977                   // annotation added to several views. This may cause
1978                   // strangeness
1979                   alview[avnum].setAnnotationIndex(newann, a);
1980                 }
1981               }
1982             }
1983           }
1984           buildSortByAnnotationScoresMenu();
1985         }
1986         viewport.firePropertyChange("alignment", null,
1987                 alignment.getSequences());
1988         if (alignPanels != null)
1989         {
1990           for (AlignmentPanel ap : ((Vector<AlignmentPanel>) alignPanels))
1991           {
1992             ap.validateAnnotationDimensions(false);
1993           }
1994         }
1995         else
1996         {
1997           alignPanel.validateAnnotationDimensions(false);
1998         }
1999
2000       }
2001       else
2002       {
2003         AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2004                 DEFAULT_HEIGHT);
2005         String newtitle = new String("Copied sequences");
2006
2007         if (Desktop.jalviewClipboard != null
2008                 && Desktop.jalviewClipboard[2] != null)
2009         {
2010           Vector hc = (Vector) Desktop.jalviewClipboard[2];
2011           for (int i = 0; i < hc.size(); i++)
2012           {
2013             int[] region = (int[]) hc.elementAt(i);
2014             af.viewport.hideColumns(region[0], region[1]);
2015           }
2016         }
2017
2018         // >>>This is a fix for the moment, until a better solution is
2019         // found!!<<<
2020         af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
2021                 .transferSettings(
2022                         alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
2023
2024         // TODO: maintain provenance of an alignment, rather than just make the
2025         // title a concatenation of operations.
2026         if (!externalPaste)
2027         {
2028           if (title.startsWith("Copied sequences"))
2029           {
2030             newtitle = title;
2031           }
2032           else
2033           {
2034             newtitle = newtitle.concat("- from " + title);
2035           }
2036         }
2037         else
2038         {
2039           newtitle = new String("Pasted sequences");
2040         }
2041
2042         Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
2043                 DEFAULT_HEIGHT);
2044
2045       }
2046
2047     } catch (Exception ex)
2048     {
2049       ex.printStackTrace();
2050       System.out.println("Exception whilst pasting: " + ex);
2051       // could be anything being pasted in here
2052     }
2053
2054   }
2055
2056   /**
2057    * DOCUMENT ME!
2058    * 
2059    * @param e
2060    *          DOCUMENT ME!
2061    */
2062   @Override
2063   protected void cut_actionPerformed(ActionEvent e)
2064   {
2065     copy_actionPerformed(null);
2066     delete_actionPerformed(null);
2067   }
2068
2069   /**
2070    * DOCUMENT ME!
2071    * 
2072    * @param e
2073    *          DOCUMENT ME!
2074    */
2075   @Override
2076   protected void delete_actionPerformed(ActionEvent evt)
2077   {
2078
2079     SequenceGroup sg = viewport.getSelectionGroup();
2080     if (sg == null)
2081     {
2082       return;
2083     }
2084
2085     Vector seqs = new Vector();
2086     SequenceI seq;
2087     for (int i = 0; i < sg.getSize(); i++)
2088     {
2089       seq = sg.getSequenceAt(i);
2090       seqs.addElement(seq);
2091     }
2092
2093     // If the cut affects all sequences, remove highlighted columns
2094     if (sg.getSize() == viewport.getAlignment().getHeight())
2095     {
2096       viewport.getColumnSelection().removeElements(sg.getStartRes(),
2097               sg.getEndRes() + 1);
2098     }
2099
2100     SequenceI[] cut = new SequenceI[seqs.size()];
2101     for (int i = 0; i < seqs.size(); i++)
2102     {
2103       cut[i] = (SequenceI) seqs.elementAt(i);
2104     }
2105
2106     /*
2107      * //ADD HISTORY ITEM
2108      */
2109     addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, cut,
2110             sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
2111             viewport.getAlignment()));
2112
2113     viewport.setSelectionGroup(null);
2114     viewport.sendSelection();
2115     viewport.getAlignment().deleteGroup(sg);
2116
2117     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2118             .getSequences());
2119     if (viewport.getAlignment().getHeight() < 1)
2120     {
2121       try
2122       {
2123         this.setClosed(true);
2124       } catch (Exception ex)
2125       {
2126       }
2127     }
2128   }
2129
2130   /**
2131    * DOCUMENT ME!
2132    * 
2133    * @param e
2134    *          DOCUMENT ME!
2135    */
2136   @Override
2137   protected void deleteGroups_actionPerformed(ActionEvent e)
2138   {
2139     viewport.getAlignment().deleteAllGroups();
2140     viewport.sequenceColours = null;
2141     viewport.setSelectionGroup(null);
2142     PaintRefresher.Refresh(this, viewport.getSequenceSetId());
2143     alignPanel.updateAnnotation();
2144     alignPanel.paintAlignment(true);
2145   }
2146
2147   /**
2148    * DOCUMENT ME!
2149    * 
2150    * @param e
2151    *          DOCUMENT ME!
2152    */
2153   @Override
2154   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2155   {
2156     SequenceGroup sg = new SequenceGroup();
2157
2158     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2159     {
2160       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
2161     }
2162
2163     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
2164     viewport.setSelectionGroup(sg);
2165     viewport.sendSelection();
2166     alignPanel.paintAlignment(true);
2167     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2168   }
2169
2170   /**
2171    * DOCUMENT ME!
2172    * 
2173    * @param e
2174    *          DOCUMENT ME!
2175    */
2176   @Override
2177   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2178   {
2179     if (viewport.cursorMode)
2180     {
2181       alignPanel.seqPanel.keyboardNo1 = null;
2182       alignPanel.seqPanel.keyboardNo2 = null;
2183     }
2184     viewport.setSelectionGroup(null);
2185     viewport.getColumnSelection().clear();
2186     viewport.setSelectionGroup(null);
2187     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
2188     alignPanel.idPanel.idCanvas.searchResults = null;
2189     alignPanel.paintAlignment(true);
2190     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2191     viewport.sendSelection();
2192   }
2193
2194   /**
2195    * DOCUMENT ME!
2196    * 
2197    * @param e
2198    *          DOCUMENT ME!
2199    */
2200   @Override
2201   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
2202   {
2203     SequenceGroup sg = viewport.getSelectionGroup();
2204
2205     if (sg == null)
2206     {
2207       selectAllSequenceMenuItem_actionPerformed(null);
2208
2209       return;
2210     }
2211
2212     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2213     {
2214       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
2215     }
2216
2217     alignPanel.paintAlignment(true);
2218     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2219     viewport.sendSelection();
2220   }
2221
2222   @Override
2223   public void invertColSel_actionPerformed(ActionEvent e)
2224   {
2225     viewport.invertColumnSelection();
2226     alignPanel.paintAlignment(true);
2227     viewport.sendSelection();
2228   }
2229
2230   /**
2231    * DOCUMENT ME!
2232    * 
2233    * @param e
2234    *          DOCUMENT ME!
2235    */
2236   @Override
2237   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
2238   {
2239     trimAlignment(true);
2240   }
2241
2242   /**
2243    * DOCUMENT ME!
2244    * 
2245    * @param e
2246    *          DOCUMENT ME!
2247    */
2248   @Override
2249   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
2250   {
2251     trimAlignment(false);
2252   }
2253
2254   void trimAlignment(boolean trimLeft)
2255   {
2256     ColumnSelection colSel = viewport.getColumnSelection();
2257     int column;
2258
2259     if (colSel.size() > 0)
2260     {
2261       if (trimLeft)
2262       {
2263         column = colSel.getMin();
2264       }
2265       else
2266       {
2267         column = colSel.getMax();
2268       }
2269
2270       SequenceI[] seqs;
2271       if (viewport.getSelectionGroup() != null)
2272       {
2273         seqs = viewport.getSelectionGroup().getSequencesAsArray(
2274                 viewport.getHiddenRepSequences());
2275       }
2276       else
2277       {
2278         seqs = viewport.getAlignment().getSequencesArray();
2279       }
2280
2281       TrimRegionCommand trimRegion;
2282       if (trimLeft)
2283       {
2284         trimRegion = new TrimRegionCommand("Remove Left",
2285                 TrimRegionCommand.TRIM_LEFT, seqs, column,
2286                 viewport.getAlignment(), viewport.getColumnSelection(),
2287                 viewport.getSelectionGroup());
2288         viewport.setStartRes(0);
2289       }
2290       else
2291       {
2292         trimRegion = new TrimRegionCommand("Remove Right",
2293                 TrimRegionCommand.TRIM_RIGHT, seqs, column,
2294                 viewport.getAlignment(), viewport.getColumnSelection(),
2295                 viewport.getSelectionGroup());
2296       }
2297
2298       statusBar.setText("Removed " + trimRegion.getSize() + " columns.");
2299
2300       addHistoryItem(trimRegion);
2301
2302       for (SequenceGroup sg : viewport.getAlignment().getGroups())
2303       {
2304         if ((trimLeft && !sg.adjustForRemoveLeft(column))
2305                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
2306         {
2307           viewport.getAlignment().deleteGroup(sg);
2308         }
2309       }
2310
2311       viewport.firePropertyChange("alignment", null, viewport
2312               .getAlignment().getSequences());
2313     }
2314   }
2315
2316   /**
2317    * DOCUMENT ME!
2318    * 
2319    * @param e
2320    *          DOCUMENT ME!
2321    */
2322   @Override
2323   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2324   {
2325     int start = 0, end = viewport.getAlignment().getWidth() - 1;
2326
2327     SequenceI[] seqs;
2328     if (viewport.getSelectionGroup() != null)
2329     {
2330       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2331               viewport.getHiddenRepSequences());
2332       start = viewport.getSelectionGroup().getStartRes();
2333       end = viewport.getSelectionGroup().getEndRes();
2334     }
2335     else
2336     {
2337       seqs = viewport.getAlignment().getSequencesArray();
2338     }
2339
2340     RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2341             "Remove Gapped Columns", seqs, start, end,
2342             viewport.getAlignment());
2343
2344     addHistoryItem(removeGapCols);
2345
2346     statusBar.setText("Removed " + removeGapCols.getSize()
2347             + " empty columns.");
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("<html>" + contents.toString() + "</html>");
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                       "Remove from default list?",
3416                       "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               "You must select at least 2 sequences.", "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               "Principal component analysis must take\n"
3595                       + "at least 4 input sequences.",
3596               "Sequence selection insufficient",
3597               JOptionPane.WARNING_MESSAGE);
3598
3599       return;
3600     }
3601
3602     new PCAPanel(alignPanel);
3603   }
3604
3605   @Override
3606   public void autoCalculate_actionPerformed(ActionEvent e)
3607   {
3608     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3609     if (viewport.autoCalculateConsensus)
3610     {
3611       viewport.firePropertyChange("alignment", null, viewport
3612               .getAlignment().getSequences());
3613     }
3614   }
3615
3616   @Override
3617   public void sortByTreeOption_actionPerformed(ActionEvent e)
3618   {
3619     viewport.sortByTree = sortByTree.isSelected();
3620   }
3621
3622   @Override
3623   protected void listenToViewSelections_actionPerformed(ActionEvent e)
3624   {
3625     viewport.followSelection = listenToViewSelections.isSelected();
3626   }
3627
3628   /**
3629    * DOCUMENT ME!
3630    * 
3631    * @param e
3632    *          DOCUMENT ME!
3633    */
3634   @Override
3635   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3636   {
3637     NewTreePanel("AV", "PID", "Average distance tree using PID");
3638   }
3639
3640   /**
3641    * DOCUMENT ME!
3642    * 
3643    * @param e
3644    *          DOCUMENT ME!
3645    */
3646   @Override
3647   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3648   {
3649     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3650   }
3651
3652   /**
3653    * DOCUMENT ME!
3654    * 
3655    * @param e
3656    *          DOCUMENT ME!
3657    */
3658   @Override
3659   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3660   {
3661     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3662   }
3663
3664   /**
3665    * DOCUMENT ME!
3666    * 
3667    * @param e
3668    *          DOCUMENT ME!
3669    */
3670   @Override
3671   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3672   {
3673     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3674   }
3675
3676   /**
3677    * DOCUMENT ME!
3678    * 
3679    * @param type
3680    *          DOCUMENT ME!
3681    * @param pwType
3682    *          DOCUMENT ME!
3683    * @param title
3684    *          DOCUMENT ME!
3685    */
3686   void NewTreePanel(String type, String pwType, String title)
3687   {
3688     TreePanel tp;
3689
3690     if (viewport.getSelectionGroup() != null
3691             && viewport.getSelectionGroup().getSize() > 0)
3692     {
3693       if (viewport.getSelectionGroup().getSize() < 3)
3694       {
3695         JOptionPane
3696                 .showMessageDialog(
3697                         Desktop.desktop,
3698                         "You need to have more than two sequences selected to build a tree!",
3699                         "Not enough sequences", JOptionPane.WARNING_MESSAGE);
3700         return;
3701       }
3702
3703       SequenceGroup sg = viewport.getSelectionGroup();
3704
3705       /* Decide if the selection is a column region */
3706       for (SequenceI _s : sg.getSequences())
3707       {
3708         if (_s.getLength() < sg.getEndRes())
3709         {
3710           JOptionPane
3711                   .showMessageDialog(
3712                           Desktop.desktop,
3713                           "The selected region to create a tree may\nonly contain residues or gaps.\n"
3714                                   + "Try using the Pad function in the edit menu,\n"
3715                                   + "or one of the multiple sequence alignment web services.",
3716                           "Sequences in selection are not aligned",
3717                           JOptionPane.WARNING_MESSAGE);
3718
3719           return;
3720         }
3721       }
3722
3723       title = title + " on region";
3724       tp = new TreePanel(alignPanel, type, pwType);
3725     }
3726     else
3727     {
3728       // are the visible sequences aligned?
3729       if (!viewport.getAlignment().isAligned(false))
3730       {
3731         JOptionPane
3732                 .showMessageDialog(
3733                         Desktop.desktop,
3734                         "The sequences must be aligned before creating a tree.\n"
3735                                 + "Try using the Pad function in the edit menu,\n"
3736                                 + "or one of the multiple sequence alignment web services.",
3737                         "Sequences not aligned",
3738                         JOptionPane.WARNING_MESSAGE);
3739
3740         return;
3741       }
3742
3743       if (viewport.getAlignment().getHeight() < 2)
3744       {
3745         return;
3746       }
3747
3748       tp = new TreePanel(alignPanel, type, pwType);
3749     }
3750
3751     title += " from ";
3752
3753     if (viewport.viewName != null)
3754     {
3755       title += viewport.viewName + " of ";
3756     }
3757
3758     title += this.title;
3759
3760     Desktop.addInternalFrame(tp, title, 600, 500);
3761   }
3762
3763   /**
3764    * DOCUMENT ME!
3765    * 
3766    * @param title
3767    *          DOCUMENT ME!
3768    * @param order
3769    *          DOCUMENT ME!
3770    */
3771   public void addSortByOrderMenuItem(String title,
3772           final AlignmentOrder order)
3773   {
3774     final JMenuItem item = new JMenuItem("by " + title);
3775     sort.add(item);
3776     item.addActionListener(new java.awt.event.ActionListener()
3777     {
3778       @Override
3779       public void actionPerformed(ActionEvent e)
3780       {
3781         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3782
3783         // TODO: JBPNote - have to map order entries to curent SequenceI
3784         // pointers
3785         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3786
3787         addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
3788                 .getAlignment()));
3789
3790         alignPanel.paintAlignment(true);
3791       }
3792     });
3793   }
3794
3795   /**
3796    * Add a new sort by annotation score menu item
3797    * 
3798    * @param sort
3799    *          the menu to add the option to
3800    * @param scoreLabel
3801    *          the label used to retrieve scores for each sequence on the
3802    *          alignment
3803    */
3804   public void addSortByAnnotScoreMenuItem(JMenu sort,
3805           final String scoreLabel)
3806   {
3807     final JMenuItem item = new JMenuItem(scoreLabel);
3808     sort.add(item);
3809     item.addActionListener(new java.awt.event.ActionListener()
3810     {
3811       @Override
3812       public void actionPerformed(ActionEvent e)
3813       {
3814         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3815         AlignmentSorter.sortByAnnotationScore(scoreLabel,
3816                 viewport.getAlignment());// ,viewport.getSelectionGroup());
3817         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
3818                 viewport.getAlignment()));
3819         alignPanel.paintAlignment(true);
3820       }
3821     });
3822   }
3823
3824   /**
3825    * last hash for alignment's annotation array - used to minimise cost of
3826    * rebuild.
3827    */
3828   protected int _annotationScoreVectorHash;
3829
3830   /**
3831    * search the alignment and rebuild the sort by annotation score submenu the
3832    * last alignment annotation vector hash is stored to minimize cost of
3833    * rebuilding in subsequence calls.
3834    * 
3835    */
3836   @Override
3837   public void buildSortByAnnotationScoresMenu()
3838   {
3839     if (viewport.getAlignment().getAlignmentAnnotation() == null)
3840     {
3841       return;
3842     }
3843
3844     if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
3845     {
3846       sortByAnnotScore.removeAll();
3847       // almost certainly a quicker way to do this - but we keep it simple
3848       Hashtable scoreSorts = new Hashtable();
3849       AlignmentAnnotation aann[];
3850       for (SequenceI sqa : viewport.getAlignment().getSequences())
3851       {
3852         aann = sqa.getAnnotation();
3853         for (int i = 0; aann != null && i < aann.length; i++)
3854         {
3855           if (aann[i].hasScore() && aann[i].sequenceRef != null)
3856           {
3857             scoreSorts.put(aann[i].label, aann[i].label);
3858           }
3859         }
3860       }
3861       Enumeration labels = scoreSorts.keys();
3862       while (labels.hasMoreElements())
3863       {
3864         addSortByAnnotScoreMenuItem(sortByAnnotScore,
3865                 (String) labels.nextElement());
3866       }
3867       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
3868       scoreSorts.clear();
3869
3870       _annotationScoreVectorHash = viewport.getAlignment()
3871               .getAlignmentAnnotation().hashCode();
3872     }
3873   }
3874
3875   /**
3876    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
3877    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
3878    * call. Listeners are added to remove the menu item when the treePanel is
3879    * closed, and adjust the tree leaf to sequence mapping when the alignment is
3880    * modified.
3881    * 
3882    * @param treePanel
3883    *          Displayed tree window.
3884    * @param title
3885    *          SortBy menu item title.
3886    */
3887   @Override
3888   public void buildTreeMenu()
3889   {
3890     sortByTreeMenu.removeAll();
3891
3892     Vector comps = (Vector) PaintRefresher.components.get(viewport
3893             .getSequenceSetId());
3894     Vector treePanels = new Vector();
3895     int i, iSize = comps.size();
3896     for (i = 0; i < iSize; i++)
3897     {
3898       if (comps.elementAt(i) instanceof TreePanel)
3899       {
3900         treePanels.add(comps.elementAt(i));
3901       }
3902     }
3903
3904     iSize = treePanels.size();
3905
3906     if (iSize < 1)
3907     {
3908       sortByTreeMenu.setVisible(false);
3909       return;
3910     }
3911
3912     sortByTreeMenu.setVisible(true);
3913
3914     for (i = 0; i < treePanels.size(); i++)
3915     {
3916       final TreePanel tp = (TreePanel) treePanels.elementAt(i);
3917       final JMenuItem item = new JMenuItem(tp.getTitle());
3918       final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
3919       item.addActionListener(new java.awt.event.ActionListener()
3920       {
3921         @Override
3922         public void actionPerformed(ActionEvent e)
3923         {
3924           tp.sortByTree_actionPerformed(null);
3925           addHistoryItem(tp.sortAlignmentIn(alignPanel));
3926
3927         }
3928       });
3929
3930       sortByTreeMenu.add(item);
3931     }
3932   }
3933
3934   public boolean sortBy(AlignmentOrder alorder, String undoname)
3935   {
3936     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3937     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
3938     if (undoname != null)
3939     {
3940       addHistoryItem(new OrderCommand(undoname, oldOrder,
3941               viewport.getAlignment()));
3942     }
3943     alignPanel.paintAlignment(true);
3944     return true;
3945   }
3946
3947   /**
3948    * Work out whether the whole set of sequences or just the selected set will
3949    * be submitted for multiple alignment.
3950    * 
3951    */
3952   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
3953   {
3954     // Now, check we have enough sequences
3955     AlignmentView msa = null;
3956
3957     if ((viewport.getSelectionGroup() != null)
3958             && (viewport.getSelectionGroup().getSize() > 1))
3959     {
3960       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
3961       // some common interface!
3962       /*
3963        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
3964        * SequenceI[sz = seqs.getSize(false)];
3965        * 
3966        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
3967        * seqs.getSequenceAt(i); }
3968        */
3969       msa = viewport.getAlignmentView(true);
3970     }
3971     else
3972     {
3973       /*
3974        * Vector seqs = viewport.getAlignment().getSequences();
3975        * 
3976        * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
3977        * 
3978        * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
3979        * seqs.elementAt(i); } }
3980        */
3981       msa = viewport.getAlignmentView(false);
3982     }
3983     return msa;
3984   }
3985
3986   /**
3987    * Decides what is submitted to a secondary structure prediction service: the
3988    * first sequence in the alignment, or in the current selection, or, if the
3989    * alignment is 'aligned' (ie padded with gaps), then the currently selected
3990    * region or the whole alignment. (where the first sequence in the set is the
3991    * one that the prediction will be for).
3992    */
3993   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
3994   {
3995     AlignmentView seqs = null;
3996
3997     if ((viewport.getSelectionGroup() != null)
3998             && (viewport.getSelectionGroup().getSize() > 0))
3999     {
4000       seqs = viewport.getAlignmentView(true);
4001     }
4002     else
4003     {
4004       seqs = viewport.getAlignmentView(false);
4005     }
4006     // limit sequences - JBPNote in future - could spawn multiple prediction
4007     // jobs
4008     // TODO: viewport.getAlignment().isAligned is a global state - the local
4009     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4010     if (!viewport.getAlignment().isAligned(false))
4011     {
4012       seqs.setSequences(new SeqCigar[]
4013       { seqs.getSequences()[0] });
4014       // TODO: if seqs.getSequences().length>1 then should really have warned
4015       // user!
4016
4017     }
4018     return seqs;
4019   }
4020
4021   /**
4022    * DOCUMENT ME!
4023    * 
4024    * @param e
4025    *          DOCUMENT ME!
4026    */
4027   @Override
4028   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
4029   {
4030     // Pick the tree file
4031     JalviewFileChooser chooser = new JalviewFileChooser(
4032             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4033     chooser.setFileView(new JalviewFileView());
4034     chooser.setDialogTitle("Select a newick-like tree file");
4035     chooser.setToolTipText("Load a tree file");
4036
4037     int value = chooser.showOpenDialog(null);
4038
4039     if (value == JalviewFileChooser.APPROVE_OPTION)
4040     {
4041       String choice = chooser.getSelectedFile().getPath();
4042       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4043       jalview.io.NewickFile fin = null;
4044       try
4045       {
4046         fin = new jalview.io.NewickFile(choice, "File");
4047         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4048       } catch (Exception ex)
4049       {
4050         JOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
4051                 "Problem reading tree file", JOptionPane.WARNING_MESSAGE);
4052         ex.printStackTrace();
4053       }
4054       if (fin != null && fin.hasWarningMessage())
4055       {
4056         JOptionPane.showMessageDialog(Desktop.desktop,
4057                 fin.getWarningMessage(), "Possible problem with tree file",
4058                 JOptionPane.WARNING_MESSAGE);
4059       }
4060     }
4061   }
4062
4063   @Override
4064   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4065   {
4066     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4067   }
4068
4069   public TreePanel ShowNewickTree(NewickFile nf, String title)
4070   {
4071     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4072   }
4073
4074   public TreePanel ShowNewickTree(NewickFile nf, String title,
4075           AlignmentView input)
4076   {
4077     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4078   }
4079
4080   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4081           int h, int x, int y)
4082   {
4083     return ShowNewickTree(nf, title, null, w, h, x, y);
4084   }
4085
4086   /**
4087    * Add a treeviewer for the tree extracted from a newick file object to the
4088    * current alignment view
4089    * 
4090    * @param nf
4091    *          the tree
4092    * @param title
4093    *          tree viewer title
4094    * @param input
4095    *          Associated alignment input data (or null)
4096    * @param w
4097    *          width
4098    * @param h
4099    *          height
4100    * @param x
4101    *          position
4102    * @param y
4103    *          position
4104    * @return TreePanel handle
4105    */
4106   public TreePanel ShowNewickTree(NewickFile nf, String title,
4107           AlignmentView input, int w, int h, int x, int y)
4108   {
4109     TreePanel tp = null;
4110
4111     try
4112     {
4113       nf.parse();
4114
4115       if (nf.getTree() != null)
4116       {
4117         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4118
4119         tp.setSize(w, h);
4120
4121         if (x > 0 && y > 0)
4122         {
4123           tp.setLocation(x, y);
4124         }
4125
4126         Desktop.addInternalFrame(tp, title, w, h);
4127       }
4128     } catch (Exception ex)
4129     {
4130       ex.printStackTrace();
4131     }
4132
4133     return tp;
4134   }
4135
4136   private boolean buildingMenu = false;
4137
4138   /**
4139    * Generates menu items and listener event actions for web service clients
4140    * 
4141    */
4142   public void BuildWebServiceMenu()
4143   {
4144     while (buildingMenu)
4145     {
4146       try
4147       {
4148         System.err.println("Waiting for building menu to finish.");
4149         Thread.sleep(10);
4150       } catch (Exception e)
4151       {
4152       }
4153       ;
4154     }
4155     final AlignFrame me = this;
4156     buildingMenu = true;
4157     new Thread(new Runnable()
4158     {
4159       @Override
4160       public void run()
4161       {
4162         try
4163         {
4164           System.err.println("Building ws menu again "
4165                   + Thread.currentThread());
4166           // TODO: add support for context dependent disabling of services based
4167           // on
4168           // alignment and current selection
4169           // TODO: add additional serviceHandle parameter to specify abstract
4170           // handler
4171           // class independently of AbstractName
4172           // TODO: add in rediscovery GUI function to restart discoverer
4173           // TODO: group services by location as well as function and/or
4174           // introduce
4175           // object broker mechanism.
4176           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4177           final IProgressIndicator af = me;
4178           final JMenu msawsmenu = new JMenu("Alignment");
4179           final JMenu secstrmenu = new JMenu(
4180                   "Secondary Structure Prediction");
4181           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4182           final JMenu analymenu = new JMenu("Analysis");
4183           final JMenu dismenu = new JMenu("Protein Disorder");
4184           // JAL-940 - only show secondary structure prediction services from
4185           // the legacy server
4186           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4187               // &&
4188           Discoverer.services != null && (Discoverer.services.size() > 0))
4189           {
4190             // TODO: refactor to allow list of AbstractName/Handler bindings to
4191             // be
4192             // stored or retrieved from elsewhere
4193             Vector msaws = null; // (Vector) Discoverer.services.get("MsaWS");
4194             Vector secstrpr = (Vector) Discoverer.services
4195                     .get("SecStrPred");
4196             Vector seqsrch = null; // (Vector)
4197                                    // Discoverer.services.get("SeqSearch");
4198             // TODO: move GUI generation code onto service implementation - so a
4199             // client instance attaches itself to the GUI with method call like
4200             // jalview.ws.MsaWSClient.bind(servicehandle, Desktop.instance,
4201             // alignframe)
4202             if (msaws != null)
4203             {
4204               // Add any Multiple Sequence Alignment Services
4205               for (int i = 0, j = msaws.size(); i < j; i++)
4206               {
4207                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws
4208                         .get(i);
4209                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4210                         .getServiceClient(sh);
4211                 impl.attachWSMenuEntry(msawsmenu, me);
4212
4213               }
4214             }
4215             if (secstrpr != null)
4216             {
4217               // Add any secondary structure prediction services
4218               for (int i = 0, j = secstrpr.size(); i < j; i++)
4219               {
4220                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4221                         .get(i);
4222                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4223                         .getServiceClient(sh);
4224                 impl.attachWSMenuEntry(secstrmenu, me);
4225               }
4226             }
4227             if (seqsrch != null)
4228             {
4229               // Add any sequence search services
4230               for (int i = 0, j = seqsrch.size(); i < j; i++)
4231               {
4232                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch
4233                         .elementAt(i);
4234                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4235                         .getServiceClient(sh);
4236                 impl.attachWSMenuEntry(seqsrchmenu, me);
4237               }
4238             }
4239           }
4240
4241           // Add all submenus in the order they should appear on the web
4242           // services menu
4243           wsmenu.add(msawsmenu);
4244           wsmenu.add(secstrmenu);
4245           wsmenu.add(dismenu);
4246           wsmenu.add(analymenu);
4247           // final ArrayList<JMenu> submens=new ArrayList<JMenu>();
4248           // submens.add(msawsmenu);
4249           // submens.add(secstrmenu);
4250           // submens.add(dismenu);
4251           // submens.add(analymenu);
4252
4253           // No search services yet
4254           // wsmenu.add(seqsrchmenu);
4255
4256           javax.swing.SwingUtilities.invokeLater(new Runnable()
4257           {
4258             @Override
4259             public void run()
4260             {
4261               try
4262               {
4263                 webService.removeAll();
4264                 // first, add discovered services onto the webservices menu
4265                 if (wsmenu.size() > 0)
4266                 {
4267                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4268                   {
4269                     webService.add(wsmenu.get(i));
4270                   }
4271                 }
4272                 else
4273                 {
4274                   webService.add(me.webServiceNoServices);
4275                 }
4276                 // TODO: move into separate menu builder class.
4277                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4278                 {
4279                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4280                   if (jws2servs != null)
4281                   {
4282                     if (jws2servs.hasServices())
4283                     {
4284                       jws2servs.attachWSMenuEntry(webService, me);
4285                     }
4286                     if (jws2servs.isRunning())
4287                     {
4288                       JMenuItem tm = new JMenuItem(
4289                               "Still discovering JABA Services");
4290                       tm.setEnabled(false);
4291                       webService.add(tm);
4292                     }
4293                   }
4294                 }
4295
4296                 build_urlServiceMenu(me.webService);
4297                 build_fetchdbmenu(webService);
4298                 for (JMenu item : wsmenu)
4299                 {
4300                   if (item.getItemCount() == 0)
4301                   {
4302                     item.setEnabled(false);
4303                   }
4304                   else
4305                   {
4306                     item.setEnabled(true);
4307                   }
4308                 }
4309               } catch (Exception e)
4310               {
4311                 Cache.log
4312                         .debug("Exception during web service menu building process.",
4313                                 e);
4314               }
4315               ;
4316             }
4317           });
4318         } catch (Exception e)
4319         {
4320         }
4321         ;
4322
4323         buildingMenu = false;
4324       }
4325     }).start();
4326
4327   }
4328
4329   /**
4330    * construct any groupURL type service menu entries.
4331    * 
4332    * @param webService
4333    */
4334   private void build_urlServiceMenu(JMenu webService)
4335   {
4336     // TODO: remove this code when 2.7 is released
4337     // DEBUG - alignmentView
4338     /*
4339      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4340      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4341      * 
4342      * @Override public void actionPerformed(ActionEvent e) {
4343      * jalview.datamodel.AlignmentView
4344      * .testSelectionViews(af.viewport.getAlignment(),
4345      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4346      * 
4347      * }); webService.add(testAlView);
4348      */
4349     // TODO: refactor to RestClient discoverer and merge menu entries for
4350     // rest-style services with other types of analysis/calculation service
4351     // SHmmr test client - still being implemented.
4352     // DEBUG - alignmentView
4353
4354     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4355             .getRestClients())
4356     {
4357       client.attachWSMenuEntry(
4358               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4359               this);
4360     }
4361
4362     if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
4363     {
4364       jalview.ws.EnfinEnvision2OneWay.getInstance().attachWSMenuEntry(
4365               webService, this);
4366     }
4367   }
4368
4369   /*
4370    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4371    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4372    * getProperty("LAST_DIRECTORY"));
4373    * 
4374    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4375    * to Vamsas file"); chooser.setToolTipText("Export");
4376    * 
4377    * int value = chooser.showSaveDialog(this);
4378    * 
4379    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4380    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4381    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4382    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4383    */
4384   /**
4385    * prototype of an automatically enabled/disabled analysis function
4386    * 
4387    */
4388   protected void setShowProductsEnabled()
4389   {
4390     SequenceI[] selection = viewport.getSequenceSelection();
4391     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4392             viewport.getAlignment().getDataset()))
4393     {
4394       showProducts.setEnabled(true);
4395
4396     }
4397     else
4398     {
4399       showProducts.setEnabled(false);
4400     }
4401   }
4402
4403   /**
4404    * search selection for sequence xRef products and build the show products
4405    * menu.
4406    * 
4407    * @param selection
4408    * @param dataset
4409    * @return true if showProducts menu should be enabled.
4410    */
4411   public boolean canShowProducts(SequenceI[] selection,
4412           boolean isRegionSelection, Alignment dataset)
4413   {
4414     boolean showp = false;
4415     try
4416     {
4417       showProducts.removeAll();
4418       final boolean dna = viewport.getAlignment().isNucleotide();
4419       final Alignment ds = dataset;
4420       String[] ptypes = (selection == null || selection.length == 0) ? null
4421               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4422       // Object[] prods =
4423       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4424       // selection, dataset, true);
4425       final SequenceI[] sel = selection;
4426       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4427       {
4428         showp = true;
4429         final boolean isRegSel = isRegionSelection;
4430         final AlignFrame af = this;
4431         final String source = ptypes[t];
4432         JMenuItem xtype = new JMenuItem(ptypes[t]);
4433         xtype.addActionListener(new ActionListener()
4434         {
4435
4436           @Override
4437           public void actionPerformed(ActionEvent e)
4438           {
4439             // TODO: new thread for this call with vis-delay
4440             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4441                     isRegSel, dna, source);
4442           }
4443
4444         });
4445         showProducts.add(xtype);
4446       }
4447       showProducts.setVisible(showp);
4448       showProducts.setEnabled(showp);
4449     } catch (Exception e)
4450     {
4451       jalview.bin.Cache.log
4452               .warn("canTranslate threw an exception - please report to help@jalview.org",
4453                       e);
4454       return false;
4455     }
4456     return showp;
4457   }
4458
4459   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4460           boolean isRegSel, boolean dna, String source)
4461   {
4462     final boolean fisRegSel = isRegSel;
4463     final boolean fdna = dna;
4464     final String fsrc = source;
4465     final AlignFrame ths = this;
4466     final SequenceI[] fsel = sel;
4467     Runnable foo = new Runnable()
4468     {
4469
4470       @Override
4471       public void run()
4472       {
4473         final long sttime = System.currentTimeMillis();
4474         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
4475         try
4476         {
4477           Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
4478           // our local
4479           // dataset
4480           // reference
4481           Alignment prods = CrossRef
4482                   .findXrefSequences(fsel, fdna, fsrc, ds);
4483           if (prods != null)
4484           {
4485             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4486             for (int s = 0; s < sprods.length; s++)
4487             {
4488               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4489               if (ds.getSequences() == null
4490                       || !ds.getSequences().contains(
4491                               sprods[s].getDatasetSequence()))
4492                 ds.addSequence(sprods[s].getDatasetSequence());
4493               sprods[s].updatePDBIds();
4494             }
4495             Alignment al = new Alignment(sprods);
4496             AlignedCodonFrame[] cf = prods.getCodonFrames();
4497             al.setDataset(ds);
4498             for (int s = 0; cf != null && s < cf.length; s++)
4499             {
4500               al.addCodonFrame(cf[s]);
4501               cf[s] = null;
4502             }
4503             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4504                     DEFAULT_HEIGHT);
4505             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4506                     + " for " + ((fisRegSel) ? "selected region of " : "")
4507                     + getTitle();
4508             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4509                     DEFAULT_HEIGHT);
4510           }
4511           else
4512           {
4513             System.err.println("No Sequences generated for xRef type "
4514                     + fsrc);
4515           }
4516         } catch (Exception e)
4517         {
4518           jalview.bin.Cache.log.error(
4519                   "Exception when finding crossreferences", e);
4520         } catch (OutOfMemoryError e)
4521         {
4522           new OOMWarning("whilst fetching crossreferences", e);
4523         } catch (Error e)
4524         {
4525           jalview.bin.Cache.log.error("Error when finding crossreferences",
4526                   e);
4527         }
4528         ths.setProgressBar("Finished searching for sequences from " + fsrc,
4529                 sttime);
4530       }
4531
4532     };
4533     Thread frunner = new Thread(foo);
4534     frunner.start();
4535   }
4536
4537   public boolean canShowTranslationProducts(SequenceI[] selection,
4538           AlignmentI alignment)
4539   {
4540     // old way
4541     try
4542     {
4543       return (jalview.analysis.Dna.canTranslate(selection,
4544               viewport.getViewAsVisibleContigs(true)));
4545     } catch (Exception e)
4546     {
4547       jalview.bin.Cache.log
4548               .warn("canTranslate threw an exception - please report to help@jalview.org",
4549                       e);
4550       return false;
4551     }
4552   }
4553
4554   @Override
4555   public void showProducts_actionPerformed(ActionEvent e)
4556   {
4557     // /////////////////////////////
4558     // Collect Data to be translated/transferred
4559
4560     SequenceI[] selection = viewport.getSequenceSelection();
4561     AlignmentI al = null;
4562     try
4563     {
4564       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4565               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4566               viewport.getAlignment().getDataset());
4567     } catch (Exception ex)
4568     {
4569       al = null;
4570       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4571     }
4572     if (al == null)
4573     {
4574       JOptionPane
4575               .showMessageDialog(
4576                       Desktop.desktop,
4577                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4578                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4579     }
4580     else
4581     {
4582       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4583       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4584               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4585     }
4586   }
4587
4588   @Override
4589   public void showTranslation_actionPerformed(ActionEvent e)
4590   {
4591     // /////////////////////////////
4592     // Collect Data to be translated/transferred
4593
4594     SequenceI[] selection = viewport.getSequenceSelection();
4595     String[] seqstring = viewport.getViewAsString(true);
4596     AlignmentI al = null;
4597     try
4598     {
4599       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4600               viewport.getViewAsVisibleContigs(true), viewport
4601                       .getGapCharacter(), viewport.getAlignment()
4602                       .getAlignmentAnnotation(), viewport.getAlignment()
4603                       .getWidth(), viewport.getAlignment().getDataset());
4604     } catch (Exception ex)
4605     {
4606       al = null;
4607       jalview.bin.Cache.log.error("Exception during translation. Please report this !", ex);
4608       JOptionPane
4609       .showMessageDialog(
4610               Desktop.desktop,
4611               "Unfortunately, something went wrong when translating your sequences.\nPlease take a look in the Jalview java console\nand submit a bug report including the stacktrace.",
4612               "Implementation error: Translation Failed", JOptionPane.ERROR_MESSAGE);
4613       return;
4614     }
4615     if (al == null)
4616     {
4617       JOptionPane
4618               .showMessageDialog(
4619                       Desktop.desktop,
4620                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4621                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4622     }
4623     else
4624     {
4625       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4626       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4627               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4628     }
4629   }
4630
4631   /**
4632    * Try to load a features file onto the alignment.
4633    * 
4634    * @param file
4635    *          contents or path to retrieve file
4636    * @param type
4637    *          access mode of file (see jalview.io.AlignFile)
4638    * @return true if features file was parsed corectly.
4639    */
4640   public boolean parseFeaturesFile(String file, String type)
4641   {
4642     boolean featuresFile = false;
4643     try
4644     {
4645       featuresFile = new FeaturesFile(file, type).parse(viewport
4646               .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
4647               .getFeatureRenderer().featureColours, false,
4648               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4649     } catch (Exception ex)
4650     {
4651       ex.printStackTrace();
4652     }
4653
4654     if (featuresFile)
4655     {
4656       viewport.showSequenceFeatures = true;
4657       showSeqFeatures.setSelected(true);
4658       if (alignPanel.seqPanel.seqCanvas.fr != null)
4659       {
4660         // update the min/max ranges where necessary
4661         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4662       }
4663       if (featureSettings != null)
4664       {
4665         featureSettings.setTableData();
4666       }
4667       alignPanel.paintAlignment(true);
4668     }
4669
4670     return featuresFile;
4671   }
4672
4673   @Override
4674   public void dragEnter(DropTargetDragEvent evt)
4675   {
4676   }
4677
4678   @Override
4679   public void dragExit(DropTargetEvent evt)
4680   {
4681   }
4682
4683   @Override
4684   public void dragOver(DropTargetDragEvent evt)
4685   {
4686   }
4687
4688   @Override
4689   public void dropActionChanged(DropTargetDragEvent evt)
4690   {
4691   }
4692
4693   @Override
4694   public void drop(DropTargetDropEvent evt)
4695   {
4696     Transferable t = evt.getTransferable();
4697     java.util.List files = null;
4698
4699     try
4700     {
4701       DataFlavor uriListFlavor = new DataFlavor(
4702               "text/uri-list;class=java.lang.String");
4703       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4704       {
4705         // Works on Windows and MacOSX
4706         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4707         files = (java.util.List) t
4708                 .getTransferData(DataFlavor.javaFileListFlavor);
4709       }
4710       else if (t.isDataFlavorSupported(uriListFlavor))
4711       {
4712         // This is used by Unix drag system
4713         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4714         String data = (String) t.getTransferData(uriListFlavor);
4715         files = new java.util.ArrayList(1);
4716         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4717                 data, "\r\n"); st.hasMoreTokens();)
4718         {
4719           String s = st.nextToken();
4720           if (s.startsWith("#"))
4721           {
4722             // the line is a comment (as per the RFC 2483)
4723             continue;
4724           }
4725
4726           java.net.URI uri = new java.net.URI(s);
4727           // check to see if we can handle this kind of URI
4728           if (uri.getScheme().toLowerCase().startsWith("http"))
4729           {
4730             files.add(uri.toString());
4731           }
4732           else
4733           {
4734             // otherwise preserve old behaviour: catch all for file objects
4735             java.io.File file = new java.io.File(uri);
4736             files.add(file.toString());
4737           }
4738         }
4739       }
4740     } catch (Exception e)
4741     {
4742       e.printStackTrace();
4743     }
4744     if (files != null)
4745     {
4746       try
4747       {
4748         // check to see if any of these files have names matching sequences in
4749         // the alignment
4750         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4751                 .getAlignment().getSequencesArray());
4752         /**
4753          * Object[] { String,SequenceI}
4754          */
4755         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4756         ArrayList<String> filesnotmatched = new ArrayList<String>();
4757         for (int i = 0; i < files.size(); i++)
4758         {
4759           String file = files.get(i).toString();
4760           String pdbfn = "";
4761           String protocol = FormatAdapter.checkProtocol(file);
4762           if (protocol == jalview.io.FormatAdapter.FILE)
4763           {
4764             File fl = new File(file);
4765             pdbfn = fl.getName();
4766           }
4767           else if (protocol == jalview.io.FormatAdapter.URL)
4768           {
4769             URL url = new URL(file);
4770             pdbfn = url.getFile();
4771           }
4772           if (pdbfn.length() > 0)
4773           {
4774             // attempt to find a match in the alignment
4775             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
4776             int l = 0, c = pdbfn.indexOf(".");
4777             while (mtch == null && c != -1)
4778             {
4779               do
4780               {
4781                 l = c;
4782               } while ((c = pdbfn.indexOf(".", l)) > l);
4783               if (l > -1)
4784               {
4785                 pdbfn = pdbfn.substring(0, l);
4786               }
4787               mtch = idm.findAllIdMatches(pdbfn);
4788             }
4789             if (mtch != null)
4790             {
4791               String type = null;
4792               try
4793               {
4794                 type = new IdentifyFile().Identify(file, protocol);
4795               } catch (Exception ex)
4796               {
4797                 type = null;
4798               }
4799               if (type != null)
4800               {
4801                 if (type.equalsIgnoreCase("PDB"))
4802                 {
4803                   filesmatched.add(new Object[]
4804                   { file, protocol, mtch });
4805                   continue;
4806                 }
4807               }
4808             }
4809             // File wasn't named like one of the sequences or wasn't a PDB file.
4810             filesnotmatched.add(file);
4811           }
4812         }
4813         int assocfiles = 0;
4814         if (filesmatched.size() > 0)
4815         {
4816           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
4817                   || JOptionPane
4818                           .showConfirmDialog(
4819                                   this,
4820                                   "Do you want to automatically associate the "
4821                                           + filesmatched.size()
4822                                           + " PDB files with sequences in the alignment that have the same name ?",
4823                                   "Automatically Associate PDB files by name",
4824                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
4825
4826           {
4827             for (Object[] fm : filesmatched)
4828             {
4829               // try and associate
4830               // TODO: may want to set a standard ID naming formalism for
4831               // associating PDB files which have no IDs.
4832               for (SequenceI toassoc : (SequenceI[]) fm[2])
4833               {
4834                 PDBEntry pe = new AssociatePdbFileWithSeq()
4835                         .associatePdbWithSeq((String) fm[0],
4836                                 (String) fm[1], toassoc, false);
4837                 if (pe != null)
4838                 {
4839                   System.err.println("Associated file : "
4840                           + ((String) fm[0]) + " with "
4841                           + toassoc.getDisplayId(true));
4842                   assocfiles++;
4843                 }
4844               }
4845               alignPanel.paintAlignment(true);
4846             }
4847           }
4848         }
4849         if (filesnotmatched.size() > 0)
4850         {
4851           if (assocfiles > 0
4852                   && (Cache.getDefault(
4853                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
4854                           .showConfirmDialog(
4855                                   this,
4856                                   "<html>Do you want to <em>ignore</em> the "
4857                                           + filesnotmatched.size()
4858                                           + " files whose names did not match any sequence IDs ?</html>",
4859                                   "Ignore unmatched dropped files ?",
4860                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
4861           {
4862             return;
4863           }
4864           for (String fn : filesnotmatched)
4865           {
4866             loadJalviewDataFile(fn, null, null, null);
4867           }
4868
4869         }
4870       } catch (Exception ex)
4871       {
4872         ex.printStackTrace();
4873       }
4874     }
4875   }
4876
4877   /**
4878    * Attempt to load a "dropped" file or URL string: First by testing whether
4879    * it's and Annotation file, then a JNet file, and finally a features file. If
4880    * all are false then the user may have dropped an alignment file onto this
4881    * AlignFrame.
4882    * 
4883    * @param file
4884    *          either a filename or a URL string.
4885    */
4886   public void loadJalviewDataFile(String file, String protocol,
4887           String format, SequenceI assocSeq)
4888   {
4889     try
4890     {
4891       if (protocol == null)
4892       {
4893         protocol = jalview.io.FormatAdapter.checkProtocol(file);
4894       }
4895       // if the file isn't identified, or not positively identified as some
4896       // other filetype (PFAM is default unidentified alignment file type) then
4897       // try to parse as annotation.
4898       boolean isAnnotation = (format == null || format
4899               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
4900               .readAnnotationFile(viewport.getAlignment(), file, protocol)
4901               : false;
4902
4903       if (!isAnnotation)
4904       {
4905         // first see if its a T-COFFEE score file
4906         TCoffeeScoreFile tcf = null;
4907         try
4908         {
4909           tcf = new TCoffeeScoreFile(file, protocol);
4910           if (tcf.isValid())
4911           {
4912             if (tcf.annotateAlignment(viewport.getAlignment(), true))
4913             {
4914               tcoffeeColour.setEnabled(true);
4915               tcoffeeColour.setSelected(true);
4916               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
4917               isAnnotation = true;
4918               statusBar
4919                       .setText("Successfully pasted T-Coffee scores to alignment.");
4920             }
4921             else
4922             {
4923               // some problem - if no warning its probable that the ID matching
4924               // process didn't work
4925               JOptionPane
4926                       .showMessageDialog(
4927                               Desktop.desktop,
4928                               tcf.getWarningMessage() == null ? "Check that the file matches sequence IDs in the alignment."
4929                                       : tcf.getWarningMessage(),
4930                               "Problem reading T-COFFEE score file",
4931                               JOptionPane.WARNING_MESSAGE);
4932             }
4933           }
4934           else
4935           {
4936             tcf = null;
4937           }
4938         } catch (Exception x)
4939         {
4940           Cache.log
4941                   .debug("Exception when processing data source as T-COFFEE score file",
4942                           x);
4943           tcf = null;
4944         }
4945         if (tcf == null)
4946         {
4947           // try to see if its a JNet 'concise' style annotation file *before*
4948           // we
4949           // try to parse it as a features file
4950           if (format == null)
4951           {
4952             format = new IdentifyFile().Identify(file, protocol);
4953           }
4954           if (format.equalsIgnoreCase("JnetFile"))
4955           {
4956             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
4957                     file, protocol);
4958             new JnetAnnotationMaker().add_annotation(predictions,
4959                     viewport.getAlignment(), 0, false);
4960             isAnnotation = true;
4961           }
4962           else
4963           {
4964             /*
4965              * if (format.equalsIgnoreCase("PDB")) {
4966              * 
4967              * String pdbfn = ""; // try to match up filename with sequence id
4968              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
4969              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
4970              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
4971              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
4972              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4973              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
4974              * // attempt to find a match in the alignment SequenceI mtch =
4975              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
4976              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
4977              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
4978              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
4979              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
4980              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
4981              * { System.err.println("Associated file : " + file + " with " +
4982              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
4983              * TODO: maybe need to load as normal otherwise return; } }
4984              */
4985             // try to parse it as a features file
4986             boolean isGroupsFile = parseFeaturesFile(file, protocol);
4987             // if it wasn't a features file then we just treat it as a general
4988             // alignment file to load into the current view.
4989             if (!isGroupsFile)
4990             {
4991               new FileLoader().LoadFile(viewport, file, protocol, format);
4992             }
4993             else
4994             {
4995               alignPanel.paintAlignment(true);
4996             }
4997           }
4998         }
4999       }
5000       if (isAnnotation)
5001       {
5002
5003         alignPanel.adjustAnnotationHeight();
5004         viewport.updateSequenceIdColours();
5005         buildSortByAnnotationScoresMenu();
5006         alignPanel.paintAlignment(true);
5007       }
5008     } catch (Exception ex)
5009     {
5010       ex.printStackTrace();
5011     } catch (OutOfMemoryError oom)
5012     {
5013       try
5014       {
5015         System.gc();
5016       } catch (Exception x)
5017       {
5018       }
5019       ;
5020       new OOMWarning(
5021               "loading data "
5022                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5023                               : "using " + protocol + " from " + file)
5024                               : ".")
5025                       + (format != null ? "(parsing as '" + format
5026                               + "' file)" : ""), oom, Desktop.desktop);
5027     }
5028   }
5029
5030   @Override
5031   public void tabSelectionChanged(int index)
5032   {
5033     if (index > -1)
5034     {
5035       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
5036       viewport = alignPanel.av;
5037       setMenusFromViewport(viewport);
5038     }
5039   }
5040
5041   @Override
5042   public void tabbedPane_mousePressed(MouseEvent e)
5043   {
5044     if (SwingUtilities.isRightMouseButton(e))
5045     {
5046       String reply = JOptionPane.showInternalInputDialog(this,
5047               "Enter View Name", "Edit View Name",
5048               JOptionPane.QUESTION_MESSAGE);
5049
5050       if (reply != null)
5051       {
5052         viewport.viewName = reply;
5053         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5054       }
5055     }
5056   }
5057
5058   public AlignViewport getCurrentView()
5059   {
5060     return viewport;
5061   }
5062
5063   /**
5064    * Open the dialog for regex description parsing.
5065    */
5066   @Override
5067   protected void extractScores_actionPerformed(ActionEvent e)
5068   {
5069     ParseProperties pp = new jalview.analysis.ParseProperties(
5070             viewport.getAlignment());
5071     // TODO: verify regex and introduce GUI dialog for version 2.5
5072     // if (pp.getScoresFromDescription("col", "score column ",
5073     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5074     // true)>0)
5075     if (pp.getScoresFromDescription("description column",
5076             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5077     {
5078       buildSortByAnnotationScoresMenu();
5079     }
5080   }
5081
5082   /*
5083    * (non-Javadoc)
5084    * 
5085    * @see
5086    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5087    * )
5088    */
5089   @Override
5090   protected void showDbRefs_actionPerformed(ActionEvent e)
5091   {
5092     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
5093   }
5094
5095   /*
5096    * (non-Javadoc)
5097    * 
5098    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5099    * ActionEvent)
5100    */
5101   @Override
5102   protected void showNpFeats_actionPerformed(ActionEvent e)
5103   {
5104     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
5105   }
5106
5107   /**
5108    * find the viewport amongst the tabs in this alignment frame and close that
5109    * tab
5110    * 
5111    * @param av
5112    */
5113   public boolean closeView(AlignViewport av)
5114   {
5115     if (viewport == av)
5116     {
5117       this.closeMenuItem_actionPerformed(false);
5118       return true;
5119     }
5120     Component[] comp = tabbedPane.getComponents();
5121     for (int i = 0; comp != null && i < comp.length; i++)
5122     {
5123       if (comp[i] instanceof AlignmentPanel)
5124       {
5125         if (((AlignmentPanel) comp[i]).av == av)
5126         {
5127           // close the view.
5128           closeView((AlignmentPanel) comp[i]);
5129           return true;
5130         }
5131       }
5132     }
5133     return false;
5134   }
5135
5136   protected void build_fetchdbmenu(JMenu webService)
5137   {
5138     // Temporary hack - DBRef Fetcher always top level ws entry.
5139     // TODO We probably want to store a sequence database checklist in
5140     // preferences and have checkboxes.. rather than individual sources selected
5141     // here
5142     final JMenu rfetch = new JMenu("Fetch DB References");
5143     rfetch.setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
5144     webService.add(rfetch);
5145
5146     JMenuItem fetchr = new JMenuItem("Standard Databases");
5147     fetchr.setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
5148     fetchr.addActionListener(new ActionListener()
5149     {
5150
5151       @Override
5152       public void actionPerformed(ActionEvent e)
5153       {
5154         new Thread(new Runnable()
5155         {
5156
5157           @Override
5158           public void run()
5159           {
5160             new jalview.ws.DBRefFetcher(alignPanel.av
5161                     .getSequenceSelection(), alignPanel.alignFrame)
5162                     .fetchDBRefs(false);
5163           }
5164         }).start();
5165
5166       }
5167
5168     });
5169     rfetch.add(fetchr);
5170     final AlignFrame me = this;
5171     new Thread(new Runnable()
5172     {
5173       @Override
5174       public void run()
5175       {
5176         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5177                 .getSequenceFetcherSingleton(me);
5178         javax.swing.SwingUtilities.invokeLater(new Runnable()
5179         {
5180           @Override
5181           public void run()
5182           {
5183             String[] dbclasses = sf.getOrderedSupportedSources();
5184             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5185             // jalview.util.QuickSort.sort(otherdb, otherdb);
5186             List<DbSourceProxy> otherdb;
5187             JMenu dfetch = new JMenu();
5188             JMenu ifetch = new JMenu();
5189             JMenuItem fetchr = null;
5190             int comp = 0, icomp = 0, mcomp = 15;
5191             String mname = null;
5192             int dbi = 0;
5193             for (String dbclass : dbclasses)
5194             {
5195               otherdb = sf.getSourceProxy(dbclass);
5196               // add a single entry for this class, or submenu allowing 'fetch
5197               // all' or pick one
5198               if (otherdb == null || otherdb.size() < 1)
5199               {
5200                 continue;
5201               }
5202               // List<DbSourceProxy> dbs=otherdb;
5203               // otherdb=new ArrayList<DbSourceProxy>();
5204               // for (DbSourceProxy db:dbs)
5205               // {
5206               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5207               // }
5208               if (mname == null)
5209               {
5210                 mname = "From " + dbclass;
5211               }
5212               if (otherdb.size() == 1)
5213               {
5214                 final DbSourceProxy[] dassource = otherdb
5215                         .toArray(new DbSourceProxy[0]);
5216                 DbSourceProxy src = otherdb.get(0);
5217                 fetchr = new JMenuItem(src.getDbSource());
5218                 fetchr.addActionListener(new ActionListener()
5219                 {
5220
5221                   @Override
5222                   public void actionPerformed(ActionEvent e)
5223                   {
5224                     new Thread(new Runnable()
5225                     {
5226
5227                       @Override
5228                       public void run()
5229                       {
5230                         new jalview.ws.DBRefFetcher(alignPanel.av
5231                                 .getSequenceSelection(),
5232                                 alignPanel.alignFrame, dassource)
5233                                 .fetchDBRefs(false);
5234                       }
5235                     }).start();
5236                   }
5237
5238                 });
5239                 fetchr.setToolTipText("<html>"
5240                         + JvSwingUtils.wrapTooltip("Retrieve from "
5241                                 + src.getDbName()) + "<html>");
5242                 dfetch.add(fetchr);
5243                 comp++;
5244               }
5245               else
5246               {
5247                 final DbSourceProxy[] dassource = otherdb
5248                         .toArray(new DbSourceProxy[0]);
5249                 // fetch all entry
5250                 DbSourceProxy src = otherdb.get(0);
5251                 fetchr = new JMenuItem("Fetch All '" + src.getDbSource()
5252                         + "'");
5253                 fetchr.addActionListener(new ActionListener()
5254                 {
5255                   @Override
5256                   public void actionPerformed(ActionEvent e)
5257                   {
5258                     new Thread(new Runnable()
5259                     {
5260
5261                       @Override
5262                       public void run()
5263                       {
5264                         new jalview.ws.DBRefFetcher(alignPanel.av
5265                                 .getSequenceSelection(),
5266                                 alignPanel.alignFrame, dassource)
5267                                 .fetchDBRefs(false);
5268                       }
5269                     }).start();
5270                   }
5271                 });
5272
5273                 fetchr.setToolTipText("<html>"
5274                         + JvSwingUtils.wrapTooltip("Retrieve from all "
5275                                 + otherdb.size() + " sources in "
5276                                 + src.getDbSource() + "<br>First is :"
5277                                 + src.getDbName()) + "<html>");
5278                 dfetch.add(fetchr);
5279                 comp++;
5280                 // and then build the rest of the individual menus
5281                 ifetch = new JMenu("Sources from " + src.getDbSource());
5282                 icomp = 0;
5283                 String imname = null;
5284                 int i = 0;
5285                 for (DbSourceProxy sproxy : otherdb)
5286                 {
5287                   String dbname = sproxy.getDbName();
5288                   String sname = dbname.length() > 5 ? dbname.substring(0,
5289                           5) + "..." : dbname;
5290                   String msname = dbname.length() > 10 ? dbname.substring(
5291                           0, 10) + "..." : dbname;
5292                   if (imname == null)
5293                   {
5294                     imname = "from '" + sname + "'";
5295                   }
5296                   fetchr = new JMenuItem(msname);
5297                   final DbSourceProxy[] dassrc =
5298                   { sproxy };
5299                   fetchr.addActionListener(new ActionListener()
5300                   {
5301
5302                     @Override
5303                     public void actionPerformed(ActionEvent e)
5304                     {
5305                       new Thread(new Runnable()
5306                       {
5307
5308                         @Override
5309                         public void run()
5310                         {
5311                           new jalview.ws.DBRefFetcher(alignPanel.av
5312                                   .getSequenceSelection(),
5313                                   alignPanel.alignFrame, dassrc)
5314                                   .fetchDBRefs(false);
5315                         }
5316                       }).start();
5317                     }
5318
5319                   });
5320                   fetchr.setToolTipText("<html>"
5321                           + JvSwingUtils.wrapTooltip("Retrieve from "
5322                                   + dbname) + "</html>");
5323                   ifetch.add(fetchr);
5324                   ++i;
5325                   if (++icomp >= mcomp || i == (otherdb.size()))
5326                   {
5327                     ifetch.setText(imname + " to '" + sname + "'");
5328                     dfetch.add(ifetch);
5329                     ifetch = new JMenu();
5330                     imname = null;
5331                     icomp = 0;
5332                     comp++;
5333                   }
5334                 }
5335               }
5336               ++dbi;
5337               if (comp >= mcomp || dbi >= (dbclasses.length))
5338               {
5339                 dfetch.setText(mname + " to '" + dbclass + "'");
5340                 rfetch.add(dfetch);
5341                 dfetch = new JMenu();
5342                 mname = null;
5343                 comp = 0;
5344               }
5345             }
5346           }
5347         });
5348       }
5349     }).start();
5350
5351   }
5352
5353   /**
5354    * Left justify the whole alignment.
5355    */
5356   @Override
5357   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5358   {
5359     AlignmentI al = viewport.getAlignment();
5360     al.justify(false);
5361     viewport.firePropertyChange("alignment", null, al);
5362   }
5363
5364   /**
5365    * Right justify the whole alignment.
5366    */
5367   @Override
5368   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5369   {
5370     AlignmentI al = viewport.getAlignment();
5371     al.justify(true);
5372     viewport.firePropertyChange("alignment", null, al);
5373   }
5374
5375   public void setShowSeqFeatures(boolean b)
5376   {
5377     showSeqFeatures.setSelected(true);
5378     viewport.setShowSequenceFeatures(true);
5379   }
5380
5381   /*
5382    * (non-Javadoc)
5383    * 
5384    * @see
5385    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5386    * awt.event.ActionEvent)
5387    */
5388   @Override
5389   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5390   {
5391     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5392     alignPanel.paintAlignment(true);
5393   }
5394
5395   /*
5396    * (non-Javadoc)
5397    * 
5398    * @see
5399    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5400    * .ActionEvent)
5401    */
5402   @Override
5403   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5404   {
5405     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5406     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5407
5408   }
5409
5410   /*
5411    * (non-Javadoc)
5412    * 
5413    * @see
5414    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5415    * .event.ActionEvent)
5416    */
5417   @Override
5418   protected void showGroupConservation_actionPerformed(ActionEvent e)
5419   {
5420     viewport.setShowGroupConservation(showGroupConservation.getState());
5421     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5422   }
5423
5424   /*
5425    * (non-Javadoc)
5426    * 
5427    * @see
5428    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5429    * .event.ActionEvent)
5430    */
5431   @Override
5432   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5433   {
5434     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5435     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5436   }
5437
5438   /*
5439    * (non-Javadoc)
5440    * 
5441    * @see
5442    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5443    * .event.ActionEvent)
5444    */
5445   @Override
5446   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5447   {
5448     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5449     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5450   }
5451
5452   @Override
5453   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5454   {
5455     showSequenceLogo.setState(true);
5456     viewport.setShowSequenceLogo(true);
5457     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5458     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5459   }
5460
5461   @Override
5462   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5463   {
5464     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5465   }
5466
5467   /*
5468    * (non-Javadoc)
5469    * 
5470    * @see
5471    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5472    * .event.ActionEvent)
5473    */
5474   @Override
5475   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5476   {
5477     if (viewport.getSelectionGroup() != null)
5478     {
5479       SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
5480               viewport.getSequenceSelection(),
5481               viewport.getAlignmentView(true).getSequenceStrings(
5482                       viewport.getGapCharacter()), viewport.getAlignment()
5483                       .getGroups());
5484       viewport.getAlignment().deleteAllGroups();
5485       viewport.sequenceColours = null;
5486       viewport.setSelectionGroup(null);
5487       // set view properties for each group
5488       for (int g = 0; g < gps.length; g++)
5489       {
5490         gps[g].setShowNonconserved(viewport.getShowUnconserved());
5491         gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
5492         viewport.getAlignment().addGroup(gps[g]);
5493         Color col = new Color((int) (Math.random() * 255),
5494                 (int) (Math.random() * 255), (int) (Math.random() * 255));
5495         col = col.brighter();
5496         for (SequenceI s : gps[g].getSequences())
5497           viewport.setSequenceColour(s, col);
5498       }
5499       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5500       alignPanel.updateAnnotation();
5501       alignPanel.paintAlignment(true);
5502     }
5503   }
5504
5505   /**
5506    * make the given alignmentPanel the currently selected tab
5507    * 
5508    * @param alignmentPanel
5509    */
5510   public void setDisplayedView(AlignmentPanel alignmentPanel)
5511   {
5512     if (!viewport.getSequenceSetId().equals(
5513             alignmentPanel.av.getSequenceSetId()))
5514     {
5515       throw new Error(
5516               "Implementation error: cannot show a view from another alignment in an AlignFrame.");
5517     }
5518     if (tabbedPane != null
5519             & alignPanels.indexOf(alignmentPanel) != tabbedPane
5520                     .getSelectedIndex())
5521     {
5522       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5523     }
5524   }
5525 }
5526
5527 class PrintThread extends Thread
5528 {
5529   AlignmentPanel ap;
5530
5531   public PrintThread(AlignmentPanel ap)
5532   {
5533     this.ap = ap;
5534   }
5535
5536   static PageFormat pf;
5537
5538   @Override
5539   public void run()
5540   {
5541     PrinterJob printJob = PrinterJob.getPrinterJob();
5542
5543     if (pf != null)
5544     {
5545       printJob.setPrintable(ap, pf);
5546     }
5547     else
5548     {
5549       printJob.setPrintable(ap);
5550     }
5551
5552     if (printJob.printDialog())
5553     {
5554       try
5555       {
5556         printJob.print();
5557       } catch (Exception PrintException)
5558       {
5559         PrintException.printStackTrace();
5560       }
5561     }
5562   }
5563 }