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