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