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