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