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