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