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