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