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