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