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