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