JAL-1078 - removed 'Load Score File' menu item and extended annotation file loader...
[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   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
3932   {
3933     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
3934   }
3935
3936   public TreePanel ShowNewickTree(NewickFile nf, String title)
3937   {
3938     return ShowNewickTree(nf, title, 600, 500, 4, 5);
3939   }
3940
3941   public TreePanel ShowNewickTree(NewickFile nf, String title,
3942           AlignmentView input)
3943   {
3944     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
3945   }
3946
3947   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
3948           int h, int x, int y)
3949   {
3950     return ShowNewickTree(nf, title, null, w, h, x, y);
3951   }
3952
3953   /**
3954    * Add a treeviewer for the tree extracted from a newick file object to the
3955    * current alignment view
3956    * 
3957    * @param nf
3958    *          the tree
3959    * @param title
3960    *          tree viewer title
3961    * @param input
3962    *          Associated alignment input data (or null)
3963    * @param w
3964    *          width
3965    * @param h
3966    *          height
3967    * @param x
3968    *          position
3969    * @param y
3970    *          position
3971    * @return TreePanel handle
3972    */
3973   public TreePanel ShowNewickTree(NewickFile nf, String title,
3974           AlignmentView input, int w, int h, int x, int y)
3975   {
3976     TreePanel tp = null;
3977
3978     try
3979     {
3980       nf.parse();
3981
3982       if (nf.getTree() != null)
3983       {
3984         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
3985
3986         tp.setSize(w, h);
3987
3988         if (x > 0 && y > 0)
3989         {
3990           tp.setLocation(x, y);
3991         }
3992
3993         Desktop.addInternalFrame(tp, title, w, h);
3994       }
3995     } catch (Exception ex)
3996     {
3997       ex.printStackTrace();
3998     }
3999
4000     return tp;
4001   }
4002
4003   private boolean buildingMenu = false;
4004
4005   /**
4006    * Generates menu items and listener event actions for web service clients
4007    * 
4008    */
4009   public void BuildWebServiceMenu()
4010   {
4011     while (buildingMenu)
4012     {
4013       try
4014       {
4015         System.err.println("Waiting for building menu to finish.");
4016         Thread.sleep(10);
4017       } catch (Exception e)
4018       {
4019       }
4020       ;
4021     }
4022     final AlignFrame me = this;
4023     buildingMenu = true;
4024     new Thread(new Runnable()
4025     {
4026       public void run()
4027       {
4028         try
4029         {
4030           System.err.println("Building ws menu again "
4031                   + Thread.currentThread());
4032           // TODO: add support for context dependent disabling of services based
4033           // on
4034           // alignment and current selection
4035           // TODO: add additional serviceHandle parameter to specify abstract
4036           // handler
4037           // class independently of AbstractName
4038           // TODO: add in rediscovery GUI function to restart discoverer
4039           // TODO: group services by location as well as function and/or
4040           // introduce
4041           // object broker mechanism.
4042           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4043           final IProgressIndicator af = me;
4044           final JMenu msawsmenu = new JMenu("Alignment");
4045           final JMenu secstrmenu = new JMenu(
4046                   "Secondary Structure Prediction");
4047           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4048           final JMenu analymenu = new JMenu("Analysis");
4049           final JMenu dismenu = new JMenu("Disorder");
4050           // JAL-940 - only show secondary structure prediction services from
4051           // the legacy server
4052           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4053               // &&
4054           Discoverer.services != null && (Discoverer.services.size() > 0))
4055           {
4056             // TODO: refactor to allow list of AbstractName/Handler bindings to
4057             // be
4058             // stored or retrieved from elsewhere
4059             Vector msaws = null; // (Vector) Discoverer.services.get("MsaWS");
4060             Vector secstrpr = (Vector) Discoverer.services
4061                     .get("SecStrPred");
4062             Vector seqsrch = null; // (Vector)
4063                                    // Discoverer.services.get("SeqSearch");
4064             // TODO: move GUI generation code onto service implementation - so a
4065             // client instance attaches itself to the GUI with method call like
4066             // jalview.ws.MsaWSClient.bind(servicehandle, Desktop.instance,
4067             // alignframe)
4068             if (msaws != null)
4069             {
4070               // Add any Multiple Sequence Alignment Services
4071               for (int i = 0, j = msaws.size(); i < j; i++)
4072               {
4073                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws
4074                         .get(i);
4075                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4076                         .getServiceClient(sh);
4077                 impl.attachWSMenuEntry(msawsmenu, me);
4078
4079               }
4080             }
4081             if (secstrpr != null)
4082             {
4083               // Add any secondary structure prediction services
4084               for (int i = 0, j = secstrpr.size(); i < j; i++)
4085               {
4086                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4087                         .get(i);
4088                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4089                         .getServiceClient(sh);
4090                 impl.attachWSMenuEntry(secstrmenu, me);
4091               }
4092             }
4093             if (seqsrch != null)
4094             {
4095               // Add any sequence search services
4096               for (int i = 0, j = seqsrch.size(); i < j; i++)
4097               {
4098                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch
4099                         .elementAt(i);
4100                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4101                         .getServiceClient(sh);
4102                 impl.attachWSMenuEntry(seqsrchmenu, me);
4103               }
4104             }
4105           }
4106
4107           // Add all submenus in the order they should appear on the web
4108           // services menu
4109           wsmenu.add(msawsmenu);
4110           wsmenu.add(secstrmenu);
4111           wsmenu.add(dismenu);
4112           wsmenu.add(analymenu);
4113           // final ArrayList<JMenu> submens=new ArrayList<JMenu>();
4114           // submens.add(msawsmenu);
4115           // submens.add(secstrmenu);
4116           // submens.add(dismenu);
4117           // submens.add(analymenu);
4118
4119           // No search services yet
4120           // wsmenu.add(seqsrchmenu);
4121
4122           javax.swing.SwingUtilities.invokeLater(new Runnable()
4123           {
4124             public void run()
4125             {
4126               try
4127               {
4128                 webService.removeAll();
4129                 // first, add discovered services onto the webservices menu
4130                 if (wsmenu.size() > 0)
4131                 {
4132                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4133                   {
4134                     webService.add((JMenu) wsmenu.get(i));
4135                   }
4136                 }
4137                 else
4138                 {
4139                   webService.add(me.webServiceNoServices);
4140                 }
4141                 // TODO: move into separate menu builder class.
4142                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4143                 {
4144                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4145                   if (jws2servs != null)
4146                   {
4147                     if (jws2servs.hasServices())
4148                     {
4149                       jws2servs.attachWSMenuEntry(webService, me);
4150                     }
4151                     if (jws2servs.isRunning())
4152                     {
4153                       JMenuItem tm = new JMenuItem(
4154                               "Still discovering JABA Services");
4155                       tm.setEnabled(false);
4156                       webService.add(tm);
4157                     }
4158                   }
4159                 }
4160
4161                 build_urlServiceMenu(me.webService);
4162                 build_fetchdbmenu(webService);
4163                 for (JMenu item : wsmenu)
4164                 {
4165                   if (item.getItemCount() == 0)
4166                   {
4167                     item.setEnabled(false);
4168                   }
4169                   else
4170                   {
4171                     item.setEnabled(true);
4172                   }
4173                 }
4174               } catch (Exception e)
4175               {
4176               }
4177               ;
4178             }
4179           });
4180         } catch (Exception e)
4181         {
4182         }
4183         ;
4184
4185         buildingMenu = false;
4186       }
4187     }).start();
4188
4189   }
4190
4191   /**
4192    * construct any groupURL type service menu entries.
4193    * 
4194    * @param webService
4195    */
4196   private void build_urlServiceMenu(JMenu webService)
4197   {
4198     // TODO: remove this code when 2.7 is released
4199     // DEBUG - alignmentView
4200     /*
4201      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4202      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4203      * 
4204      * @Override public void actionPerformed(ActionEvent e) {
4205      * jalview.datamodel.AlignmentView
4206      * .testSelectionViews(af.viewport.getAlignment(),
4207      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4208      * 
4209      * }); webService.add(testAlView);
4210      */
4211     // TODO: refactor to RestClient discoverer and merge menu entries for
4212     // rest-style services with other types of analysis/calculation service
4213     // SHmmr test client - still being implemented.
4214     // DEBUG - alignmentView
4215
4216     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4217             .getRestClients())
4218     {
4219       client.attachWSMenuEntry(
4220               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4221               this);
4222     }
4223
4224     if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
4225     {
4226       jalview.ws.EnfinEnvision2OneWay.getInstance().attachWSMenuEntry(
4227               webService, this);
4228     }
4229   }
4230
4231   /*
4232    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4233    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4234    * getProperty("LAST_DIRECTORY"));
4235    * 
4236    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4237    * to Vamsas file"); chooser.setToolTipText("Export");
4238    * 
4239    * int value = chooser.showSaveDialog(this);
4240    * 
4241    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4242    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4243    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4244    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4245    */
4246   /**
4247    * prototype of an automatically enabled/disabled analysis function
4248    * 
4249    */
4250   protected void setShowProductsEnabled()
4251   {
4252     SequenceI[] selection = viewport.getSequenceSelection();
4253     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4254             viewport.getAlignment().getDataset()))
4255     {
4256       showProducts.setEnabled(true);
4257
4258     }
4259     else
4260     {
4261       showProducts.setEnabled(false);
4262     }
4263   }
4264
4265   /**
4266    * search selection for sequence xRef products and build the show products
4267    * menu.
4268    * 
4269    * @param selection
4270    * @param dataset
4271    * @return true if showProducts menu should be enabled.
4272    */
4273   public boolean canShowProducts(SequenceI[] selection,
4274           boolean isRegionSelection, Alignment dataset)
4275   {
4276     boolean showp = false;
4277     try
4278     {
4279       showProducts.removeAll();
4280       final boolean dna = viewport.getAlignment().isNucleotide();
4281       final Alignment ds = dataset;
4282       String[] ptypes = (selection == null || selection.length == 0) ? null
4283               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4284       // Object[] prods =
4285       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4286       // selection, dataset, true);
4287       final SequenceI[] sel = selection;
4288       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4289       {
4290         showp = true;
4291         final boolean isRegSel = isRegionSelection;
4292         final AlignFrame af = this;
4293         final String source = ptypes[t];
4294         JMenuItem xtype = new JMenuItem(ptypes[t]);
4295         xtype.addActionListener(new ActionListener()
4296         {
4297
4298           public void actionPerformed(ActionEvent e)
4299           {
4300             // TODO: new thread for this call with vis-delay
4301             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4302                     isRegSel, dna, source);
4303           }
4304
4305         });
4306         showProducts.add(xtype);
4307       }
4308       showProducts.setVisible(showp);
4309       showProducts.setEnabled(showp);
4310     } catch (Exception e)
4311     {
4312       jalview.bin.Cache.log
4313               .warn("canTranslate threw an exception - please report to help@jalview.org",
4314                       e);
4315       return false;
4316     }
4317     return showp;
4318   }
4319
4320   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4321           boolean isRegSel, boolean dna, String source)
4322   {
4323     final boolean fisRegSel = isRegSel;
4324     final boolean fdna = dna;
4325     final String fsrc = source;
4326     final AlignFrame ths = this;
4327     final SequenceI[] fsel = sel;
4328     Runnable foo = new Runnable()
4329     {
4330
4331       public void run()
4332       {
4333         final long sttime = System.currentTimeMillis();
4334         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
4335         try
4336         {
4337           Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
4338           // our local
4339           // dataset
4340           // reference
4341           Alignment prods = CrossRef
4342                   .findXrefSequences(fsel, fdna, fsrc, ds);
4343           if (prods != null)
4344           {
4345             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4346             for (int s = 0; s < sprods.length; s++)
4347             {
4348               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4349               if (ds.getSequences() == null
4350                       || !ds.getSequences().contains(
4351                               sprods[s].getDatasetSequence()))
4352                 ds.addSequence(sprods[s].getDatasetSequence());
4353               sprods[s].updatePDBIds();
4354             }
4355             Alignment al = new Alignment(sprods);
4356             AlignedCodonFrame[] cf = prods.getCodonFrames();
4357             al.setDataset(ds);
4358             for (int s = 0; cf != null && s < cf.length; s++)
4359             {
4360               al.addCodonFrame(cf[s]);
4361               cf[s] = null;
4362             }
4363             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4364                     DEFAULT_HEIGHT);
4365             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4366                     + " for " + ((fisRegSel) ? "selected region of " : "")
4367                     + getTitle();
4368             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4369                     DEFAULT_HEIGHT);
4370           }
4371           else
4372           {
4373             System.err.println("No Sequences generated for xRef type "
4374                     + fsrc);
4375           }
4376         } catch (Exception e)
4377         {
4378           jalview.bin.Cache.log.error(
4379                   "Exception when finding crossreferences", e);
4380         } catch (OutOfMemoryError e)
4381         {
4382           new OOMWarning("whilst fetching crossreferences", e);
4383         } catch (Error e)
4384         {
4385           jalview.bin.Cache.log.error("Error when finding crossreferences",
4386                   e);
4387         }
4388         ths.setProgressBar("Finished searching for sequences from " + fsrc,
4389                 sttime);
4390       }
4391
4392     };
4393     Thread frunner = new Thread(foo);
4394     frunner.start();
4395   }
4396
4397   public boolean canShowTranslationProducts(SequenceI[] selection,
4398           AlignmentI alignment)
4399   {
4400     // old way
4401     try
4402     {
4403       return (jalview.analysis.Dna.canTranslate(selection,
4404               viewport.getViewAsVisibleContigs(true)));
4405     } catch (Exception e)
4406     {
4407       jalview.bin.Cache.log
4408               .warn("canTranslate threw an exception - please report to help@jalview.org",
4409                       e);
4410       return false;
4411     }
4412   }
4413
4414   public void showProducts_actionPerformed(ActionEvent e)
4415   {
4416     // /////////////////////////////
4417     // Collect Data to be translated/transferred
4418
4419     SequenceI[] selection = viewport.getSequenceSelection();
4420     AlignmentI al = null;
4421     try
4422     {
4423       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4424               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4425               viewport.getAlignment().getDataset());
4426     } catch (Exception ex)
4427     {
4428       al = null;
4429       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4430     }
4431     if (al == null)
4432     {
4433       JOptionPane
4434               .showMessageDialog(
4435                       Desktop.desktop,
4436                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4437                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4438     }
4439     else
4440     {
4441       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4442       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4443               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4444     }
4445   }
4446
4447   public void showTranslation_actionPerformed(ActionEvent e)
4448   {
4449     // /////////////////////////////
4450     // Collect Data to be translated/transferred
4451
4452     SequenceI[] selection = viewport.getSequenceSelection();
4453     String[] seqstring = viewport.getViewAsString(true);
4454     AlignmentI al = null;
4455     try
4456     {
4457       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4458               viewport.getViewAsVisibleContigs(true), viewport
4459                       .getGapCharacter(), viewport.getAlignment()
4460                       .getAlignmentAnnotation(), viewport.getAlignment()
4461                       .getWidth(), viewport.getAlignment().getDataset());
4462     } catch (Exception ex)
4463     {
4464       al = null;
4465       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4466     }
4467     if (al == null)
4468     {
4469       JOptionPane
4470               .showMessageDialog(
4471                       Desktop.desktop,
4472                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4473                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4474     }
4475     else
4476     {
4477       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4478       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4479               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4480     }
4481   }
4482
4483   /**
4484    * Try to load a features file onto the alignment.
4485    * 
4486    * @param file
4487    *          contents or path to retrieve file
4488    * @param type
4489    *          access mode of file (see jalview.io.AlignFile)
4490    * @return true if features file was parsed corectly.
4491    */
4492   public boolean parseFeaturesFile(String file, String type)
4493   {
4494     boolean featuresFile = false;
4495     try
4496     {
4497       featuresFile = new FeaturesFile(file, type).parse(viewport
4498               .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
4499               .getFeatureRenderer().featureColours, false,
4500               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4501     } catch (Exception ex)
4502     {
4503       ex.printStackTrace();
4504     }
4505
4506     if (featuresFile)
4507     {
4508       viewport.showSequenceFeatures = true;
4509       showSeqFeatures.setSelected(true);
4510       if (alignPanel.seqPanel.seqCanvas.fr != null)
4511       {
4512         // update the min/max ranges where necessary
4513         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4514       }
4515       if (featureSettings != null)
4516       {
4517         featureSettings.setTableData();
4518       }
4519       alignPanel.paintAlignment(true);
4520     }
4521
4522     return featuresFile;
4523   }
4524
4525   public void dragEnter(DropTargetDragEvent evt)
4526   {
4527   }
4528
4529   public void dragExit(DropTargetEvent evt)
4530   {
4531   }
4532
4533   public void dragOver(DropTargetDragEvent evt)
4534   {
4535   }
4536
4537   public void dropActionChanged(DropTargetDragEvent evt)
4538   {
4539   }
4540
4541   public void drop(DropTargetDropEvent evt)
4542   {
4543     Transferable t = evt.getTransferable();
4544     java.util.List files = null;
4545
4546     try
4547     {
4548       DataFlavor uriListFlavor = new DataFlavor(
4549               "text/uri-list;class=java.lang.String");
4550       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4551       {
4552         // Works on Windows and MacOSX
4553         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4554         files = (java.util.List) t
4555                 .getTransferData(DataFlavor.javaFileListFlavor);
4556       }
4557       else if (t.isDataFlavorSupported(uriListFlavor))
4558       {
4559         // This is used by Unix drag system
4560         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4561         String data = (String) t.getTransferData(uriListFlavor);
4562         files = new java.util.ArrayList(1);
4563         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4564                 data, "\r\n"); st.hasMoreTokens();)
4565         {
4566           String s = st.nextToken();
4567           if (s.startsWith("#"))
4568           {
4569             // the line is a comment (as per the RFC 2483)
4570             continue;
4571           }
4572
4573           java.net.URI uri = new java.net.URI(s);
4574           // check to see if we can handle this kind of URI
4575           if (uri.getScheme().toLowerCase().startsWith("http"))
4576           {
4577             files.add(uri.toString());
4578           }
4579           else
4580           {
4581             // otherwise preserve old behaviour: catch all for file objects
4582             java.io.File file = new java.io.File(uri);
4583             files.add(file.toString());
4584           }
4585         }
4586       }
4587     } catch (Exception e)
4588     {
4589       e.printStackTrace();
4590     }
4591     if (files != null)
4592     {
4593       try
4594       {
4595         // check to see if any of these files have names matching sequences in
4596         // the alignment
4597         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4598                 .getAlignment().getSequencesArray());
4599         /**
4600          * Object[] { String,SequenceI}
4601          */
4602         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4603         ArrayList<String> filesnotmatched = new ArrayList<String>();
4604         for (int i = 0; i < files.size(); i++)
4605         {
4606           String file = files.get(i).toString();
4607           String pdbfn = "";
4608           String protocol = FormatAdapter.checkProtocol(file);
4609           if (protocol == jalview.io.FormatAdapter.FILE)
4610           {
4611             File fl = new File(file);
4612             pdbfn = fl.getName();
4613           }
4614           else if (protocol == jalview.io.FormatAdapter.URL)
4615           {
4616             URL url = new URL(file);
4617             pdbfn = url.getFile();
4618           }
4619           if (pdbfn.length() > 0)
4620           {
4621             // attempt to find a match in the alignment
4622             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
4623             int l = 0, c = pdbfn.indexOf(".");
4624             while (mtch == null && c != -1)
4625             {
4626               do
4627               {
4628                 l = c;
4629               } while ((c = pdbfn.indexOf(".", l)) > l);
4630               if (l > -1)
4631               {
4632                 pdbfn = pdbfn.substring(0, l);
4633               }
4634               mtch = idm.findAllIdMatches(pdbfn);
4635             }
4636             if (mtch != null)
4637             {
4638               String type = null;
4639               try
4640               {
4641                 type = new IdentifyFile().Identify(file, protocol);
4642               } catch (Exception ex)
4643               {
4644                 type = null;
4645               }
4646               if (type != null)
4647               {
4648                 if (type.equalsIgnoreCase("PDB"))
4649                 {
4650                   filesmatched.add(new Object[]
4651                   { file, protocol, mtch });
4652                   continue;
4653                 }
4654               }
4655             }
4656             // File wasn't named like one of the sequences or wasn't a PDB file.
4657             filesnotmatched.add(file);
4658           }
4659         }
4660         int assocfiles = 0;
4661         if (filesmatched.size() > 0)
4662         {
4663           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
4664                   || JOptionPane
4665                           .showConfirmDialog(
4666                                   this,
4667                                   "Do you want to automatically associate the "
4668                                           + filesmatched.size()
4669                                           + " PDB files with sequences in the alignment that have the same name ?",
4670                                   "Automatically Associate PDB files by name",
4671                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
4672
4673           {
4674             for (Object[] fm : filesmatched)
4675             {
4676               // try and associate
4677               // TODO: may want to set a standard ID naming formalism for
4678               // associating PDB files which have no IDs.
4679               for (SequenceI toassoc : (SequenceI[]) fm[2])
4680               {
4681                 PDBEntry pe = new AssociatePdbFileWithSeq()
4682                         .associatePdbWithSeq((String) fm[0],
4683                                 (String) fm[1], toassoc, false);
4684                 if (pe != null)
4685                 {
4686                   System.err.println("Associated file : "
4687                           + ((String) fm[0]) + " with "
4688                           + toassoc.getDisplayId(true));
4689                   assocfiles++;
4690                 }
4691               }
4692               alignPanel.paintAlignment(true);
4693             }
4694           }
4695         }
4696         if (filesnotmatched.size() > 0)
4697         {
4698           if (assocfiles > 0
4699                   && (Cache.getDefault(
4700                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
4701                           .showConfirmDialog(
4702                                   this,
4703                                   "<html>Do you want to <em>ignore</em> the "
4704                                           + filesnotmatched.size()
4705                                           + " files whose names did not match any sequence IDs ?</html>",
4706                                   "Ignore unmatched dropped files ?",
4707                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
4708           {
4709             return;
4710           }
4711           for (String fn : filesnotmatched)
4712           {
4713             loadJalviewDataFile(fn, null, null, null);
4714           }
4715
4716         }
4717       } catch (Exception ex)
4718       {
4719         ex.printStackTrace();
4720       }
4721     }
4722   }
4723
4724   /**
4725    * Attempt to load a "dropped" file or URL string: First by testing whether
4726    * it's and Annotation file, then a JNet file, and finally a features file. If
4727    * all are false then the user may have dropped an alignment file onto this
4728    * AlignFrame.
4729    * 
4730    * @param file
4731    *          either a filename or a URL string.
4732    */
4733   public void loadJalviewDataFile(String file, String protocol,
4734           String format, SequenceI assocSeq)
4735   {
4736     try
4737     {
4738       if (protocol == null)
4739       {
4740         protocol = jalview.io.FormatAdapter.checkProtocol(file);
4741       }
4742       // if the file isn't identified, or not positively identified as some
4743       // other filetype (PFAM is default unidentified alignment file type) then
4744       // try to parse as annotation.
4745       boolean isAnnotation = (format == null || format
4746               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
4747               .readAnnotationFile(viewport.getAlignment(), file, protocol)
4748               : false;
4749
4750       if (!isAnnotation)
4751       {
4752         // first see if its a T-COFFEE score file
4753         TCoffeeScoreFile tcf = null;
4754         try
4755         {
4756           tcf = new TCoffeeScoreFile(file, protocol);
4757           if (tcf.isValid())
4758           {
4759             if (tcf.annotateAlignment(viewport.getAlignment(), true))
4760             {
4761               tcoffeeColour.setEnabled(true);
4762               tcoffeeColour.setSelected(true);
4763               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
4764               isAnnotation = true;
4765               statusBar
4766                       .setText("Successfully pasted T-Coffee scores to alignment.");
4767             }
4768             else
4769             {
4770               // some problem - if no warning its probable that the ID matching process didn't work
4771               JOptionPane.showMessageDialog(Desktop.desktop,
4772                       tcf.getWarningMessage()==null ? "Check that the file matches sequence IDs in the alignment." : tcf.getWarningMessage(),
4773                       "Problem reading T-COFFEE score file",
4774                       JOptionPane.WARNING_MESSAGE);
4775             }
4776           }
4777           else
4778           {
4779             tcf = null;
4780           }
4781         } catch (Exception x)
4782         {
4783           Cache.log.debug("Exception when processing data source as T-COFFEE score file",x);
4784           tcf = null;
4785         }
4786         if (tcf == null)
4787         {
4788           // try to see if its a JNet 'concise' style annotation file *before*
4789           // we
4790           // try to parse it as a features file
4791           if (format == null)
4792           {
4793             format = new IdentifyFile().Identify(file, protocol);
4794           }
4795           if (format.equalsIgnoreCase("JnetFile"))
4796           {
4797             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
4798                     file, protocol);
4799             new JnetAnnotationMaker().add_annotation(predictions,
4800                     viewport.getAlignment(), 0, false);
4801             isAnnotation = true;
4802           }
4803           else
4804           {
4805             /*
4806              * if (format.equalsIgnoreCase("PDB")) {
4807              * 
4808              * String pdbfn = ""; // try to match up filename with sequence id
4809              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
4810              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
4811              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
4812              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
4813              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4814              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
4815              * // attempt to find a match in the alignment SequenceI mtch =
4816              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
4817              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
4818              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
4819              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
4820              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
4821              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
4822              * { System.err.println("Associated file : " + file + " with " +
4823              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
4824              * TODO: maybe need to load as normal otherwise return; } }
4825              */
4826             // try to parse it as a features file
4827             boolean isGroupsFile = parseFeaturesFile(file, protocol);
4828             // if it wasn't a features file then we just treat it as a general
4829             // alignment file to load into the current view.
4830             if (!isGroupsFile)
4831             {
4832               new FileLoader().LoadFile(viewport, file, protocol, format);
4833             }
4834             else
4835             {
4836               alignPanel.paintAlignment(true);
4837             }
4838           }
4839         }
4840       }
4841       if (isAnnotation)
4842       {
4843
4844         alignPanel.adjustAnnotationHeight();
4845         viewport.updateSequenceIdColours();
4846         buildSortByAnnotationScoresMenu();
4847         alignPanel.paintAlignment(true);
4848       }
4849     } catch (Exception ex)
4850     {
4851       ex.printStackTrace();
4852     }
4853   }
4854
4855   public void tabSelectionChanged(int index)
4856   {
4857     if (index > -1)
4858     {
4859       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
4860       viewport = alignPanel.av;
4861       setMenusFromViewport(viewport);
4862     }
4863   }
4864
4865   public void tabbedPane_mousePressed(MouseEvent e)
4866   {
4867     if (SwingUtilities.isRightMouseButton(e))
4868     {
4869       String reply = JOptionPane.showInternalInputDialog(this,
4870               "Enter View Name", "Edit View Name",
4871               JOptionPane.QUESTION_MESSAGE);
4872
4873       if (reply != null)
4874       {
4875         viewport.viewName = reply;
4876         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
4877       }
4878     }
4879   }
4880
4881   public AlignViewport getCurrentView()
4882   {
4883     return viewport;
4884   }
4885
4886   /**
4887    * Open the dialog for regex description parsing.
4888    */
4889   protected void extractScores_actionPerformed(ActionEvent e)
4890   {
4891     ParseProperties pp = new jalview.analysis.ParseProperties(
4892             viewport.getAlignment());
4893     // TODO: verify regex and introduce GUI dialog for version 2.5
4894     // if (pp.getScoresFromDescription("col", "score column ",
4895     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
4896     // true)>0)
4897     if (pp.getScoresFromDescription("description column",
4898             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
4899     {
4900       buildSortByAnnotationScoresMenu();
4901     }
4902   }
4903
4904   /*
4905    * (non-Javadoc)
4906    * 
4907    * @see
4908    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
4909    * )
4910    */
4911   protected void showDbRefs_actionPerformed(ActionEvent e)
4912   {
4913     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
4914   }
4915
4916   /*
4917    * (non-Javadoc)
4918    * 
4919    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
4920    * ActionEvent)
4921    */
4922   protected void showNpFeats_actionPerformed(ActionEvent e)
4923   {
4924     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
4925   }
4926
4927   /**
4928    * find the viewport amongst the tabs in this alignment frame and close that
4929    * tab
4930    * 
4931    * @param av
4932    */
4933   public boolean closeView(AlignViewport av)
4934   {
4935     if (viewport == av)
4936     {
4937       this.closeMenuItem_actionPerformed(false);
4938       return true;
4939     }
4940     Component[] comp = tabbedPane.getComponents();
4941     for (int i = 0; comp != null && i < comp.length; i++)
4942     {
4943       if (comp[i] instanceof AlignmentPanel)
4944       {
4945         if (((AlignmentPanel) comp[i]).av == av)
4946         {
4947           // close the view.
4948           closeView((AlignmentPanel) comp[i]);
4949           return true;
4950         }
4951       }
4952     }
4953     return false;
4954   }
4955
4956   protected void build_fetchdbmenu(JMenu webService)
4957   {
4958     // Temporary hack - DBRef Fetcher always top level ws entry.
4959     // TODO We probably want to store a sequence database checklist in
4960     // preferences and have checkboxes.. rather than individual sources selected
4961     // here
4962     final JMenu rfetch = new JMenu("Fetch DB References");
4963     rfetch.setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
4964     webService.add(rfetch);
4965
4966     JMenuItem fetchr = new JMenuItem("Standard Databases");
4967     fetchr.setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
4968     fetchr.addActionListener(new ActionListener()
4969     {
4970
4971       public void actionPerformed(ActionEvent e)
4972       {
4973         new Thread(new Runnable()
4974         {
4975
4976           public void run()
4977           {
4978             new jalview.ws.DBRefFetcher(alignPanel.av
4979                     .getSequenceSelection(), alignPanel.alignFrame)
4980                     .fetchDBRefs(false);
4981           }
4982         }).start();
4983
4984       }
4985
4986     });
4987     rfetch.add(fetchr);
4988     final AlignFrame me = this;
4989     new Thread(new Runnable()
4990     {
4991       public void run()
4992       {
4993         final jalview.ws.SequenceFetcher sf = SequenceFetcher
4994                 .getSequenceFetcherSingleton(me);
4995         javax.swing.SwingUtilities.invokeLater(new Runnable()
4996         {
4997           public void run()
4998           {
4999             String[] dbclasses = sf.getOrderedSupportedSources();
5000             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5001             // jalview.util.QuickSort.sort(otherdb, otherdb);
5002             List<DbSourceProxy> otherdb;
5003             JMenu dfetch = new JMenu();
5004             JMenu ifetch = new JMenu();
5005             JMenuItem fetchr = null;
5006             int comp = 0, icomp = 0, mcomp = 15;
5007             String mname = null;
5008             int dbi = 0;
5009             for (String dbclass : dbclasses)
5010             {
5011               otherdb = sf.getSourceProxy(dbclass);
5012               // add a single entry for this class, or submenu allowing 'fetch
5013               // all' or pick one
5014               if (otherdb == null || otherdb.size() < 1)
5015               {
5016                 continue;
5017               }
5018               // List<DbSourceProxy> dbs=otherdb;
5019               // otherdb=new ArrayList<DbSourceProxy>();
5020               // for (DbSourceProxy db:dbs)
5021               // {
5022               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5023               // }
5024               if (mname == null)
5025               {
5026                 mname = "From " + dbclass;
5027               }
5028               if (otherdb.size() == 1)
5029               {
5030                 final DbSourceProxy[] dassource = otherdb
5031                         .toArray(new DbSourceProxy[0]);
5032                 DbSourceProxy src = otherdb.get(0);
5033                 fetchr = new JMenuItem(src.getDbSource());
5034                 fetchr.addActionListener(new ActionListener()
5035                 {
5036
5037                   public void actionPerformed(ActionEvent e)
5038                   {
5039                     new Thread(new Runnable()
5040                     {
5041
5042                       public void run()
5043                       {
5044                         new jalview.ws.DBRefFetcher(alignPanel.av
5045                                 .getSequenceSelection(),
5046                                 alignPanel.alignFrame, dassource)
5047                                 .fetchDBRefs(false);
5048                       }
5049                     }).start();
5050                   }
5051
5052                 });
5053                 fetchr.setToolTipText("<html>"
5054                         + JvSwingUtils.wrapTooltip("Retrieve from "
5055                                 + src.getDbName()) + "<html>");
5056                 dfetch.add(fetchr);
5057                 comp++;
5058               }
5059               else
5060               {
5061                 final DbSourceProxy[] dassource = otherdb
5062                         .toArray(new DbSourceProxy[0]);
5063                 // fetch all entry
5064                 DbSourceProxy src = otherdb.get(0);
5065                 fetchr = new JMenuItem("Fetch All '" + src.getDbSource()
5066                         + "'");
5067                 fetchr.addActionListener(new ActionListener()
5068                 {
5069                   public void actionPerformed(ActionEvent e)
5070                   {
5071                     new Thread(new Runnable()
5072                     {
5073
5074                       public void run()
5075                       {
5076                         new jalview.ws.DBRefFetcher(alignPanel.av
5077                                 .getSequenceSelection(),
5078                                 alignPanel.alignFrame, dassource)
5079                                 .fetchDBRefs(false);
5080                       }
5081                     }).start();
5082                   }
5083                 });
5084
5085                 fetchr.setToolTipText("<html>"
5086                         + JvSwingUtils.wrapTooltip("Retrieve from all "
5087                                 + otherdb.size() + " sources in "
5088                                 + src.getDbSource() + "<br>First is :"
5089                                 + src.getDbName()) + "<html>");
5090                 dfetch.add(fetchr);
5091                 comp++;
5092                 // and then build the rest of the individual menus
5093                 ifetch = new JMenu("Sources from " + src.getDbSource());
5094                 icomp = 0;
5095                 String imname = null;
5096                 int i = 0;
5097                 for (DbSourceProxy sproxy : otherdb)
5098                 {
5099                   String dbname = sproxy.getDbName();
5100                   String sname = dbname.length() > 5 ? dbname.substring(0,
5101                           5) + "..." : dbname;
5102                   String msname = dbname.length() > 10 ? dbname.substring(
5103                           0, 10) + "..." : dbname;
5104                   if (imname == null)
5105                   {
5106                     imname = "from '" + sname + "'";
5107                   }
5108                   fetchr = new JMenuItem(msname);
5109                   final DbSourceProxy[] dassrc =
5110                   { sproxy };
5111                   fetchr.addActionListener(new ActionListener()
5112                   {
5113
5114                     public void actionPerformed(ActionEvent e)
5115                     {
5116                       new Thread(new Runnable()
5117                       {
5118
5119                         public void run()
5120                         {
5121                           new jalview.ws.DBRefFetcher(alignPanel.av
5122                                   .getSequenceSelection(),
5123                                   alignPanel.alignFrame, dassrc)
5124                                   .fetchDBRefs(false);
5125                         }
5126                       }).start();
5127                     }
5128
5129                   });
5130                   fetchr.setToolTipText("<html>"
5131                           + JvSwingUtils.wrapTooltip("Retrieve from "
5132                                   + dbname) + "</html>");
5133                   ifetch.add(fetchr);
5134                   ++i;
5135                   if (++icomp >= mcomp || i == (otherdb.size()))
5136                   {
5137                     ifetch.setText(imname + " to '" + sname + "'");
5138                     dfetch.add(ifetch);
5139                     ifetch = new JMenu();
5140                     imname = null;
5141                     icomp = 0;
5142                     comp++;
5143                   }
5144                 }
5145               }
5146               ++dbi;
5147               if (comp >= mcomp || dbi >= (dbclasses.length))
5148               {
5149                 dfetch.setText(mname + " to '" + dbclass + "'");
5150                 rfetch.add(dfetch);
5151                 dfetch = new JMenu();
5152                 mname = null;
5153                 comp = 0;
5154               }
5155             }
5156           }
5157         });
5158       }
5159     }).start();
5160
5161   }
5162
5163   /**
5164    * Left justify the whole alignment.
5165    */
5166   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5167   {
5168     AlignmentI al = viewport.getAlignment();
5169     al.justify(false);
5170     viewport.firePropertyChange("alignment", null, al);
5171   }
5172
5173   /**
5174    * Right justify the whole alignment.
5175    */
5176   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5177   {
5178     AlignmentI al = viewport.getAlignment();
5179     al.justify(true);
5180     viewport.firePropertyChange("alignment", null, al);
5181   }
5182
5183   public void setShowSeqFeatures(boolean b)
5184   {
5185     showSeqFeatures.setSelected(true);
5186     viewport.setShowSequenceFeatures(true);
5187   }
5188
5189   /*
5190    * (non-Javadoc)
5191    * 
5192    * @see
5193    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5194    * awt.event.ActionEvent)
5195    */
5196   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5197   {
5198     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5199     alignPanel.paintAlignment(true);
5200   }
5201
5202   /*
5203    * (non-Javadoc)
5204    * 
5205    * @see
5206    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5207    * .ActionEvent)
5208    */
5209   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5210   {
5211     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5212     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5213
5214   }
5215
5216   /*
5217    * (non-Javadoc)
5218    * 
5219    * @see
5220    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5221    * .event.ActionEvent)
5222    */
5223   protected void showGroupConservation_actionPerformed(ActionEvent e)
5224   {
5225     viewport.setShowGroupConservation(showGroupConservation.getState());
5226     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5227   }
5228
5229   /*
5230    * (non-Javadoc)
5231    * 
5232    * @see
5233    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5234    * .event.ActionEvent)
5235    */
5236   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5237   {
5238     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5239     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5240   }
5241
5242   /*
5243    * (non-Javadoc)
5244    * 
5245    * @see
5246    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5247    * .event.ActionEvent)
5248    */
5249   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5250   {
5251     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5252     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5253   }
5254
5255   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5256   {
5257     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5258     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5259   }
5260
5261   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5262   {
5263     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5264   }
5265
5266   /*
5267    * (non-Javadoc)
5268    * 
5269    * @see
5270    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5271    * .event.ActionEvent)
5272    */
5273   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5274   {
5275     if (viewport.getSelectionGroup() != null)
5276     {
5277       SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
5278               viewport.getSequenceSelection(),
5279               viewport.getAlignmentView(true).getSequenceStrings(
5280                       viewport.getGapCharacter()), viewport.getAlignment()
5281                       .getGroups());
5282       viewport.getAlignment().deleteAllGroups();
5283       viewport.sequenceColours = null;
5284       viewport.setSelectionGroup(null);
5285       // set view properties for each group
5286       for (int g = 0; g < gps.length; g++)
5287       {
5288         gps[g].setShowNonconserved(viewport.getShowUnconserved());
5289         gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
5290         viewport.getAlignment().addGroup(gps[g]);
5291         Color col = new Color((int) (Math.random() * 255),
5292                 (int) (Math.random() * 255), (int) (Math.random() * 255));
5293         col = col.brighter();
5294         for (Enumeration sq = gps[g].getSequences(null).elements(); sq
5295                 .hasMoreElements(); viewport.setSequenceColour(
5296                 (SequenceI) sq.nextElement(), col))
5297           ;
5298       }
5299       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5300       alignPanel.updateAnnotation();
5301       alignPanel.paintAlignment(true);
5302     }
5303   }
5304
5305   /**
5306    * make the given alignmentPanel the currently selected tab
5307    * 
5308    * @param alignmentPanel
5309    */
5310   public void setDisplayedView(AlignmentPanel alignmentPanel)
5311   {
5312     if (!viewport.getSequenceSetId().equals(
5313             alignmentPanel.av.getSequenceSetId()))
5314     {
5315       throw new Error(
5316               "Implementation error: cannot show a view from another alignment in an AlignFrame.");
5317     }
5318     if (tabbedPane != null
5319             & alignPanels.indexOf(alignmentPanel) != tabbedPane
5320                     .getSelectedIndex())
5321     {
5322       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5323     }
5324   }
5325 }
5326
5327 class PrintThread extends Thread
5328 {
5329   AlignmentPanel ap;
5330
5331   public PrintThread(AlignmentPanel ap)
5332   {
5333     this.ap = ap;
5334   }
5335
5336   static PageFormat pf;
5337
5338   public void run()
5339   {
5340     PrinterJob printJob = PrinterJob.getPrinterJob();
5341
5342     if (pf != null)
5343     {
5344       printJob.setPrintable(ap, pf);
5345     }
5346     else
5347     {
5348       printJob.setPrintable(ap);
5349     }
5350
5351     if (printJob.printDialog())
5352     {
5353       try
5354       {
5355         printJob.print();
5356       } catch (Exception PrintException)
5357       {
5358         PrintException.printStackTrace();
5359       }
5360     }
5361   }
5362 }