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