1320aa0bce6ef2335fbf7d09e113a7fc1e344ad7
[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       er.printStackTrace();
1412       javax.swing.SwingUtilities.invokeLater(new Runnable()
1413           {
1414             public void run()
1415             {
1416               javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
1417                   "Out of memory copying region!!"
1418                   +
1419                   "\nSee help files for increasing Java Virtual Machine memory."
1420                   , "Out of memory",
1421                   javax.swing.JOptionPane.WARNING_MESSAGE);
1422             }
1423           });
1424
1425       return;
1426     }
1427
1428     Vector hiddenColumns = null;
1429     if(viewport.hasHiddenColumns)
1430     {
1431       hiddenColumns =new Vector();
1432       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
1433       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns().size();
1434            i++)
1435       {
1436         int[] region = (int[])
1437             viewport.getColumnSelection().getHiddenColumns().elementAt(i);
1438
1439         hiddenColumns.addElement(new int[]
1440                                  {region[0] - hiddenOffset,
1441                           region[1]-hiddenOffset});
1442       }
1443     }
1444
1445     Desktop.jalviewClipboard = new Object[]
1446         {
1447         seqs,
1448         viewport.alignment.getDataset(),
1449         hiddenColumns};
1450     statusBar.setText("Copied "+seqs.length+" sequences to clipboard.");
1451   }
1452
1453   /**
1454    * DOCUMENT ME!
1455    *
1456    * @param e DOCUMENT ME!
1457    */
1458   protected void pasteNew_actionPerformed(ActionEvent e)
1459   {
1460     paste(true);
1461   }
1462
1463   /**
1464    * DOCUMENT ME!
1465    *
1466    * @param e DOCUMENT ME!
1467    */
1468   protected void pasteThis_actionPerformed(ActionEvent e)
1469   {
1470     paste(false);
1471   }
1472
1473   /**
1474    * Paste contents of Jalview clipboard
1475    *
1476    * @param newAlignment true to paste to a new alignment, otherwise add to this.
1477    */
1478   void paste(boolean newAlignment)
1479   {
1480     boolean externalPaste=true;
1481     try
1482     {
1483       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1484       Transferable contents = c.getContents(this);
1485
1486       if (contents == null)
1487       {
1488         return;
1489       }
1490
1491       String str, format;
1492       try
1493       {
1494         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1495         if (str.length() < 1)
1496         {
1497           return;
1498         }
1499
1500         format = new IdentifyFile().Identify(str, "Paste");
1501
1502       }
1503       catch (OutOfMemoryError er)
1504       {
1505         er.printStackTrace();
1506         javax.swing.SwingUtilities.invokeLater(new Runnable()
1507         {
1508           public void run()
1509           {
1510             javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
1511                 "Out of memory pasting sequences!!"
1512                 +
1513                 "\nSee help files for increasing Java Virtual Machine memory."
1514                 , "Out of memory",
1515                 javax.swing.JOptionPane.WARNING_MESSAGE);
1516           }
1517         });
1518
1519         return;
1520       }
1521
1522       SequenceI[] sequences;
1523       boolean annotationAdded = false;
1524       AlignmentI alignment = null;
1525
1526      if(Desktop.jalviewClipboard!=null)
1527      {
1528        // The clipboard was filled from within Jalview, we must use the sequences
1529        // And dataset from the copied alignment
1530        SequenceI[] newseq = (SequenceI[])Desktop.jalviewClipboard[0];
1531        // be doubly sure that we create *new* sequence objects.
1532        sequences = new SequenceI[newseq.length];
1533        for (int i=0;i<newseq.length;i++) {
1534          sequences[i] = new Sequence(newseq[i]);
1535        }
1536        alignment = new Alignment(sequences);
1537        externalPaste = false;
1538      }
1539      else
1540      {
1541        // parse the clipboard as an alignment.
1542        alignment = new FormatAdapter().readFile(str, "Paste", format);
1543        sequences = alignment.getSequencesArray();
1544      }
1545
1546      int alwidth=0;
1547
1548      if (newAlignment)
1549      {
1550
1551        if (Desktop.jalviewClipboard != null)
1552        {
1553          // dataset is inherited
1554          alignment.setDataset( (Alignment) Desktop.jalviewClipboard[1]);
1555        }
1556        else
1557        {
1558          // new dataset is constructed
1559          alignment.setDataset(null);
1560        }
1561        alwidth = alignment.getWidth()+1;
1562      }
1563      else
1564      {
1565        AlignmentI pastedal = alignment; // preserve pasted alignment object
1566        // Add pasted sequences and dataset into existing alignment.
1567        alignment = viewport.getAlignment();
1568        alwidth = alignment.getWidth()+1;
1569         // decide if we need to import sequences from an existing dataset
1570         boolean importDs = Desktop.jalviewClipboard != null
1571                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1572         // importDs==true instructs us to copy over new dataset sequences from
1573         // an existing alignment
1574         Vector newDs = (importDs) ? new Vector() : null; // used to create
1575                                                           // minimum dataset set
1576
1577         for (int i = 0; i < sequences.length; i++)
1578         {
1579           if (importDs)
1580           {
1581             newDs.addElement(null);
1582           }
1583           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1584                                                             // paste
1585           if (importDs && ds != null)
1586           {
1587             if (!newDs.contains(ds))
1588             {
1589               newDs.setElementAt(ds, i);
1590               ds = new Sequence(ds);
1591               // update with new dataset sequence
1592               sequences[i].setDatasetSequence(ds);
1593             }
1594             else
1595             {
1596               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1597             }
1598           }
1599           else
1600           {
1601             // copy and derive new dataset sequence
1602             sequences[i] = sequences[i].deriveSequence();
1603             alignment.getDataset().addSequence(sequences[i].getDatasetSequence());
1604             // TODO: avoid creation of duplicate dataset sequences with a
1605             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1606           }
1607           alignment.addSequence(sequences[i]); // merges dataset
1608         }
1609         if (newDs != null)
1610         {
1611           newDs.clear(); // tidy up
1612         }
1613         if (pastedal.getAlignmentAnnotation()!=null) {
1614           // Add any annotation attached to alignment.
1615           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1616           for (int i=0; i<alann.length; i++)
1617           {
1618             annotationAdded=true;
1619             if (alann[i].sequenceRef==null && !alann[i].autoCalculated) {
1620               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1621               newann.padAnnotation(alwidth);
1622               alignment.addAnnotation(newann);
1623             }
1624           }
1625         }
1626      }
1627      if (!newAlignment) {
1628        ///////
1629        // ADD HISTORY ITEM
1630        //
1631        addHistoryItem(new EditCommand(
1632                "Add sequences",
1633                EditCommand.PASTE,
1634                sequences,
1635                0,
1636                alignment.getWidth(),
1637                alignment)
1638               );
1639      }
1640      // Add any annotations attached to sequences
1641      for (int i = 0; i < sequences.length; i++)
1642      {
1643        if (sequences[i].getAnnotation() != null)
1644        {
1645          for (int a = 0; a < sequences[i].getAnnotation().length; a++)
1646          {
1647            annotationAdded=true;
1648            sequences[i].getAnnotation()[a].adjustForAlignment();
1649            sequences[i].getAnnotation()[a].padAnnotation(alwidth);
1650            alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation was duplicated earlier
1651            alignment.setAnnotationIndex(sequences[i].getAnnotation()[a], a);
1652          }
1653        }
1654      }
1655      if (!newAlignment) {
1656
1657        // propagate alignment changed.
1658        viewport.setEndSeq(alignment.getHeight());
1659        if (annotationAdded)
1660        {
1661          // Duplicate sequence annotation in all views.
1662          AlignmentI[] alview = this.getViewAlignments();
1663          for (int i = 0; i < sequences.length; i++)
1664          {
1665            AlignmentAnnotation sann[] = sequences[i].getAnnotation();
1666            if (sann == null)
1667              continue;
1668            for (int avnum=0;avnum<alview.length; avnum++)
1669            {
1670              if (alview[avnum]!=alignment)
1671              {
1672                // duplicate in a view other than the one with input focus
1673                int avwidth = alview[avnum].getWidth()+1;
1674                // this relies on sann being preserved after we 
1675                // modify the sequence's annotation array for each duplication
1676                for (int a=0; a<sann.length; a++)
1677                {
1678                  AlignmentAnnotation newann = new AlignmentAnnotation(sann[a]);
1679                  sequences[i].addAlignmentAnnotation(newann);
1680                  newann.padAnnotation(avwidth);
1681                  alview[avnum].addAnnotation(newann); // annotation was duplicated earlier
1682                  alview[avnum].setAnnotationIndex(newann, a);
1683                }
1684              }
1685            }
1686          }
1687          buildSortByAnnotationScoresMenu();
1688        }
1689        viewport.firePropertyChange("alignment", null, alignment.getSequences());
1690
1691      } else {
1692        AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH, DEFAULT_HEIGHT);
1693        String newtitle = new String("Copied sequences");
1694
1695        if(Desktop.jalviewClipboard!=null && Desktop.jalviewClipboard[2]!=null)
1696          {
1697            Vector hc = (Vector)Desktop.jalviewClipboard[2];
1698            for(int i=0; i<hc.size(); i++)
1699            {
1700              int [] region = (int[]) hc.elementAt(i);
1701              af.viewport.hideColumns(region[0], region[1]);
1702            }
1703          }
1704
1705
1706        //>>>This is a fix for the moment, until a better solution is found!!<<<
1707        af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().transferSettings(
1708            alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
1709
1710        // TODO: maintain provenance of an alignment, rather than just make the title a concatenation of operations.
1711        if (!externalPaste) {
1712          if (title.startsWith("Copied sequences"))
1713          {
1714            newtitle = title;
1715          }
1716          else
1717          {
1718            newtitle = newtitle.concat("- from " + title);
1719          }
1720        } else {
1721          newtitle = new String("Pasted sequences");
1722        }
1723
1724        Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
1725                                 DEFAULT_HEIGHT);
1726
1727      }
1728
1729
1730     }
1731     catch (Exception ex)
1732     {
1733       ex.printStackTrace();
1734         System.out.println("Exception whilst pasting: "+ex);
1735         // could be anything being pasted in here
1736     }
1737
1738
1739   }
1740
1741   /**
1742    * DOCUMENT ME!
1743    *
1744    * @param e DOCUMENT ME!
1745    */
1746   protected void cut_actionPerformed(ActionEvent e)
1747   {
1748     copy_actionPerformed(null);
1749     delete_actionPerformed(null);
1750   }
1751
1752   /**
1753    * DOCUMENT ME!
1754    *
1755    * @param e DOCUMENT ME!
1756    */
1757   protected void delete_actionPerformed(ActionEvent evt)
1758   {
1759
1760     SequenceGroup sg = viewport.getSelectionGroup();
1761     if (sg == null)
1762     {
1763       return;
1764     }
1765
1766     Vector seqs = new Vector();
1767     SequenceI seq;
1768     for (int i = 0; i < sg.getSize(); i++)
1769     {
1770       seq = sg.getSequenceAt(i);
1771       seqs.addElement(seq);
1772     }
1773
1774
1775    // If the cut affects all sequences, remove highlighted columns
1776    if (sg.getSize() == viewport.alignment.getHeight())
1777    {
1778      viewport.getColumnSelection().removeElements(sg.getStartRes(),
1779          sg.getEndRes() + 1);
1780    }
1781
1782
1783     SequenceI [] cut = new SequenceI[seqs.size()];
1784     for(int i=0; i<seqs.size(); i++)
1785     {
1786       cut[i] = (SequenceI)seqs.elementAt(i);
1787     }
1788
1789
1790     /*
1791     //ADD HISTORY ITEM
1792     */
1793     addHistoryItem(new EditCommand("Cut Sequences",
1794                                       EditCommand.CUT,
1795                                       cut,
1796                                       sg.getStartRes(),
1797                                       sg.getEndRes()-sg.getStartRes()+1,
1798                                       viewport.alignment));
1799
1800
1801     viewport.setSelectionGroup(null);
1802     viewport.alignment.deleteGroup(sg);
1803
1804     viewport.firePropertyChange("alignment", null,
1805                                   viewport.getAlignment().getSequences());
1806
1807     if (viewport.getAlignment().getHeight() < 1)
1808     {
1809       try
1810       {
1811         this.setClosed(true);
1812       }
1813       catch (Exception ex)
1814       {
1815       }
1816     }
1817   }
1818
1819   /**
1820    * DOCUMENT ME!
1821    *
1822    * @param e DOCUMENT ME!
1823    */
1824   protected void deleteGroups_actionPerformed(ActionEvent e)
1825   {
1826     viewport.alignment.deleteAllGroups();
1827     viewport.sequenceColours = null;
1828     viewport.setSelectionGroup(null);
1829     PaintRefresher.Refresh(this, viewport.getSequenceSetId());
1830     alignPanel.paintAlignment(true);
1831   }
1832
1833   /**
1834    * DOCUMENT ME!
1835    *
1836    * @param e DOCUMENT ME!
1837    */
1838   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
1839   {
1840     SequenceGroup sg = new SequenceGroup();
1841
1842     for (int i = 0; i < viewport.getAlignment().getSequences().size();
1843          i++)
1844     {
1845       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
1846     }
1847
1848     sg.setEndRes(viewport.alignment.getWidth() - 1);
1849     viewport.setSelectionGroup(sg);
1850     alignPanel.paintAlignment(true);
1851     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1852   }
1853
1854   /**
1855    * DOCUMENT ME!
1856    *
1857    * @param e DOCUMENT ME!
1858    */
1859   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
1860   {
1861     if(viewport.cursorMode)
1862     {
1863       alignPanel.seqPanel.keyboardNo1 = null;
1864       alignPanel.seqPanel.keyboardNo2 = null;
1865     }
1866     viewport.setSelectionGroup(null);
1867     viewport.getColumnSelection().clear();
1868     viewport.setSelectionGroup(null);
1869     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
1870     alignPanel.idPanel.idCanvas.searchResults = null;
1871     alignPanel.paintAlignment(true);
1872     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1873   }
1874
1875   /**
1876    * DOCUMENT ME!
1877    *
1878    * @param e DOCUMENT ME!
1879    */
1880   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
1881   {
1882     SequenceGroup sg = viewport.getSelectionGroup();
1883
1884     if (sg == null)
1885     {
1886       selectAllSequenceMenuItem_actionPerformed(null);
1887
1888       return;
1889     }
1890
1891     for (int i = 0; i < viewport.getAlignment().getSequences().size();
1892          i++)
1893     {
1894       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
1895     }
1896
1897     alignPanel.paintAlignment(true);
1898
1899     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1900   }
1901
1902   public void invertColSel_actionPerformed(ActionEvent e)
1903   {
1904     viewport.invertColumnSelection();
1905     alignPanel.paintAlignment(true);
1906   }
1907
1908
1909   /**
1910    * DOCUMENT ME!
1911    *
1912    * @param e DOCUMENT ME!
1913    */
1914   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
1915   {
1916     trimAlignment(true);
1917   }
1918
1919   /**
1920    * DOCUMENT ME!
1921    *
1922    * @param e DOCUMENT ME!
1923    */
1924   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
1925   {
1926       trimAlignment(false);
1927   }
1928
1929   void trimAlignment(boolean trimLeft)
1930   {
1931     ColumnSelection colSel = viewport.getColumnSelection();
1932     int column;
1933
1934     if (colSel.size() > 0)
1935     {
1936       if(trimLeft)
1937       {
1938         column = colSel.getMin();
1939       }
1940       else
1941       {
1942         column = colSel.getMax();
1943       }
1944
1945       SequenceI [] seqs;
1946       if(viewport.getSelectionGroup()!=null)
1947       {
1948         seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
1949             hiddenRepSequences);
1950       }
1951       else
1952       {
1953         seqs = viewport.alignment.getSequencesArray();
1954       }
1955
1956
1957       TrimRegionCommand trimRegion;
1958       if(trimLeft)
1959       {
1960         trimRegion = new TrimRegionCommand("Remove Left",
1961                                     TrimRegionCommand.TRIM_LEFT,
1962                                     seqs,
1963                                     column,
1964                                     viewport.alignment,
1965                                     viewport.colSel,
1966                                     viewport.selectionGroup);
1967         viewport.setStartRes(0);
1968       }
1969      else
1970      {
1971        trimRegion = new TrimRegionCommand("Remove Right",
1972                                    TrimRegionCommand.TRIM_RIGHT,
1973                                    seqs,
1974                                    column,
1975                                    viewport.alignment,
1976                                    viewport.colSel,
1977                                    viewport.selectionGroup);
1978      }
1979
1980      statusBar.setText("Removed "+trimRegion.getSize()+" columns.");
1981
1982
1983       addHistoryItem(trimRegion);
1984
1985       Vector groups = viewport.alignment.getGroups();
1986
1987       for (int i = 0; i < groups.size(); i++)
1988       {
1989         SequenceGroup sg = (SequenceGroup) groups.get(i);
1990
1991         if ( (trimLeft && !sg.adjustForRemoveLeft(column))
1992             || (!trimLeft && !sg.adjustForRemoveRight(column)))
1993         {
1994           viewport.alignment.deleteGroup(sg);
1995         }
1996       }
1997
1998       viewport.firePropertyChange("alignment", null,
1999                                   viewport.getAlignment().getSequences());
2000     }
2001   }
2002
2003   /**
2004    * DOCUMENT ME!
2005    *
2006    * @param e DOCUMENT ME!
2007    */
2008   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2009   {
2010     int start = 0, end = viewport.alignment.getWidth()-1;
2011
2012     SequenceI[] seqs;
2013     if (viewport.getSelectionGroup() != null)
2014     {
2015       seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
2016           hiddenRepSequences);
2017       start = viewport.getSelectionGroup().getStartRes();
2018       end = viewport.getSelectionGroup().getEndRes();
2019     }
2020     else
2021     {
2022       seqs = viewport.alignment.getSequencesArray();
2023     }
2024
2025
2026     RemoveGapColCommand removeGapCols =
2027         new RemoveGapColCommand("Remove Gapped Columns",
2028                                 seqs,
2029                                 start, end,
2030                                 viewport.alignment);
2031
2032     addHistoryItem(removeGapCols);
2033
2034     statusBar.setText("Removed "+removeGapCols.getSize()+" empty columns.");
2035
2036     //This is to maintain viewport position on first residue
2037     //of first sequence
2038     SequenceI seq = viewport.alignment.getSequenceAt(0);
2039     int startRes = seq.findPosition(viewport.startRes);
2040    // ShiftList shifts;
2041    // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2042    // edit.alColumnChanges=shifts.getInverse();
2043    // if (viewport.hasHiddenColumns)
2044    //   viewport.getColumnSelection().compensateForEdits(shifts);
2045    viewport.setStartRes(seq.findIndex(startRes)-1);
2046     viewport.firePropertyChange("alignment", null,
2047                                 viewport.getAlignment().getSequences());
2048
2049   }
2050
2051   /**
2052    * DOCUMENT ME!
2053    *
2054    * @param e DOCUMENT ME!
2055    */
2056   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2057   {
2058     int start = 0, end = viewport.alignment.getWidth()-1;
2059
2060     SequenceI[] seqs;
2061     if (viewport.getSelectionGroup() != null)
2062     {
2063       seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
2064           hiddenRepSequences);
2065       start = viewport.getSelectionGroup().getStartRes();
2066       end = viewport.getSelectionGroup().getEndRes();
2067     }
2068     else
2069     {
2070       seqs = viewport.alignment.getSequencesArray();
2071     }
2072
2073     //This is to maintain viewport position on first residue
2074     //of first sequence
2075     SequenceI seq = viewport.alignment.getSequenceAt(0);
2076     int startRes = seq.findPosition(viewport.startRes);
2077
2078     addHistoryItem(new RemoveGapsCommand("Remove Gaps",
2079                                          seqs,
2080                                          start, end,
2081                                          viewport.alignment));
2082
2083     viewport.setStartRes(seq.findIndex(startRes)-1);
2084
2085     viewport.firePropertyChange("alignment", null,
2086                                 viewport.getAlignment().getSequences());
2087
2088   }
2089
2090   /**
2091    * DOCUMENT ME!
2092    *
2093    * @param e DOCUMENT ME!
2094    */
2095   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2096   {
2097     viewport.padGaps = padGapsMenuitem.isSelected();
2098
2099     viewport.firePropertyChange("alignment",
2100                                 null,
2101                                 viewport.getAlignment().getSequences());
2102   }
2103
2104   /**
2105    * DOCUMENT ME!
2106    *
2107    * @param e DOCUMENT ME!
2108    */
2109   public void findMenuItem_actionPerformed(ActionEvent e)
2110   {
2111     new Finder();
2112   }
2113
2114   public void newView_actionPerformed(ActionEvent e)
2115   {
2116     AlignmentPanel newap =
2117         new Jalview2XML().copyAlignPanel(alignPanel, true);
2118
2119     newap.av.gatherViewsHere = false;
2120
2121     if (viewport.viewName == null)
2122     {
2123       viewport.viewName = "Original";
2124     }
2125
2126     newap.av.historyList = viewport.historyList;
2127     newap.av.redoList = viewport.redoList;
2128
2129     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2130     String newViewName = "View " +index;
2131
2132     Vector comps = (Vector) PaintRefresher.components.get(viewport.
2133         getSequenceSetId());
2134     Vector existingNames = new Vector();
2135     for(int i=0; i<comps.size(); i++)
2136     {
2137       if(comps.elementAt(i) instanceof AlignmentPanel)
2138       {
2139         AlignmentPanel ap = (AlignmentPanel)comps.elementAt(i);
2140         if(!existingNames.contains(ap.av.viewName))
2141         {
2142           existingNames.addElement(ap.av.viewName);
2143       }
2144     }
2145     }
2146
2147     while(existingNames.contains(newViewName))
2148     {
2149       newViewName = "View "+ (++index);
2150     }
2151
2152     newap.av.viewName = newViewName;
2153
2154     addAlignmentPanel(newap, false);
2155
2156     if(alignPanels.size()==2)
2157     {
2158       viewport.gatherViewsHere = true;
2159     }
2160     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2161   }
2162
2163   public void expandViews_actionPerformed(ActionEvent e)
2164   {
2165         Desktop.instance.explodeViews(this);
2166   }
2167
2168   public void gatherViews_actionPerformed(ActionEvent e)
2169   {
2170     Desktop.instance.gatherViews(this);
2171   }
2172
2173
2174
2175   /**
2176    * DOCUMENT ME!
2177    *
2178    * @param e DOCUMENT ME!
2179    */
2180   public void font_actionPerformed(ActionEvent e)
2181   {
2182     new FontChooser(alignPanel);
2183   }
2184
2185
2186   /**
2187    * DOCUMENT ME!
2188    *
2189    * @param e DOCUMENT ME!
2190    */
2191   protected void seqLimit_actionPerformed(ActionEvent e)
2192   {
2193     viewport.setShowJVSuffix(seqLimits.isSelected());
2194
2195     alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel.calculateIdWidth());
2196     alignPanel.paintAlignment(true);
2197   }
2198
2199   public void idRightAlign_actionPerformed(ActionEvent e)
2200   {
2201     viewport.rightAlignIds = idRightAlign.isSelected();
2202     alignPanel.paintAlignment(true);
2203   }
2204
2205   public void centreColumnLabels_actionPerformed(ActionEvent e)
2206   {
2207     viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
2208     alignPanel.paintAlignment(true);
2209   }
2210
2211
2212
2213   /**
2214    * DOCUMENT ME!
2215    *
2216    * @param e DOCUMENT ME!
2217    */
2218   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2219   {
2220     viewport.setColourText(colourTextMenuItem.isSelected());
2221     alignPanel.paintAlignment(true);
2222   }
2223
2224   /**
2225    * DOCUMENT ME!
2226    *
2227    * @param e DOCUMENT ME!
2228    */
2229   public void wrapMenuItem_actionPerformed(ActionEvent e)
2230   {
2231     scaleAbove.setVisible(wrapMenuItem.isSelected());
2232     scaleLeft.setVisible(wrapMenuItem.isSelected());
2233     scaleRight.setVisible(wrapMenuItem.isSelected());
2234     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2235     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
2236   }
2237
2238   public void showAllSeqs_actionPerformed(ActionEvent e)
2239   {
2240     viewport.showAllHiddenSeqs();
2241   }
2242
2243   public void showAllColumns_actionPerformed(ActionEvent e)
2244   {
2245     viewport.showAllHiddenColumns();
2246     repaint();
2247   }
2248
2249   public void hideSelSequences_actionPerformed(ActionEvent e)
2250   {
2251     viewport.hideAllSelectedSeqs();
2252     alignPanel.paintAlignment(true);
2253   }
2254
2255   public void hideSelColumns_actionPerformed(ActionEvent e)
2256   {
2257     viewport.hideSelectedColumns();
2258     alignPanel.paintAlignment(true);
2259   }
2260
2261   public void hiddenMarkers_actionPerformed(ActionEvent e)
2262   {
2263     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
2264     repaint();
2265   }
2266
2267   /**
2268    * DOCUMENT ME!
2269    *
2270    * @param e DOCUMENT ME!
2271    */
2272   protected void scaleAbove_actionPerformed(ActionEvent e)
2273   {
2274     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
2275     alignPanel.paintAlignment(true);
2276   }
2277
2278   /**
2279    * DOCUMENT ME!
2280    *
2281    * @param e DOCUMENT ME!
2282    */
2283   protected void scaleLeft_actionPerformed(ActionEvent e)
2284   {
2285     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
2286     alignPanel.paintAlignment(true);
2287   }
2288
2289   /**
2290    * DOCUMENT ME!
2291    *
2292    * @param e DOCUMENT ME!
2293    */
2294   protected void scaleRight_actionPerformed(ActionEvent e)
2295   {
2296     viewport.setScaleRightWrapped(scaleRight.isSelected());
2297     alignPanel.paintAlignment(true);
2298   }
2299
2300   /**
2301    * DOCUMENT ME!
2302    *
2303    * @param e DOCUMENT ME!
2304    */
2305   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
2306   {
2307     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
2308     alignPanel.paintAlignment(true);
2309   }
2310
2311   /**
2312    * DOCUMENT ME!
2313    *
2314    * @param e DOCUMENT ME!
2315    */
2316   public void viewTextMenuItem_actionPerformed(ActionEvent e)
2317   {
2318     viewport.setShowText(viewTextMenuItem.isSelected());
2319     alignPanel.paintAlignment(true);
2320   }
2321
2322   /**
2323    * DOCUMENT ME!
2324    *
2325    * @param e DOCUMENT ME!
2326    */
2327   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
2328   {
2329     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
2330     alignPanel.paintAlignment(true);
2331   }
2332
2333
2334   public FeatureSettings featureSettings;
2335   public void featureSettings_actionPerformed(ActionEvent e)
2336   {
2337     if(featureSettings !=null )
2338     {
2339       featureSettings.close();
2340       featureSettings = null;
2341     }
2342     featureSettings = new FeatureSettings(this);
2343   }
2344
2345   /**
2346    * DOCUMENT ME!
2347    *
2348    * @param evt DOCUMENT ME!
2349    */
2350   public void showSeqFeatures_actionPerformed(ActionEvent evt)
2351   {
2352     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
2353     alignPanel.paintAlignment(true);
2354     if (alignPanel.getOverviewPanel() != null)
2355     {
2356       alignPanel.getOverviewPanel().updateOverviewImage();
2357     }
2358   }
2359
2360   /**
2361    * DOCUMENT ME!
2362    *
2363    * @param e DOCUMENT ME!
2364    */
2365   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
2366   {
2367     viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
2368     alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
2369   }
2370
2371   public void alignmentProperties()
2372   {
2373     JEditorPane editPane = new JEditorPane("text/html","");
2374     editPane.setEditable(false);
2375     StringBuffer contents = new StringBuffer("<html>");
2376
2377     float avg  = 0;
2378     int min=Integer.MAX_VALUE, max=0;
2379     for(int i=0; i<viewport.alignment.getHeight(); i++)
2380     {
2381       int size = viewport.alignment.getSequenceAt(i).getEnd()
2382           -viewport.alignment.getSequenceAt(i).getStart();
2383       avg += size;
2384       if(size>max)
2385         max = size;
2386       if(size<min)
2387         min = size;
2388     }
2389     avg = avg/(float)viewport.alignment.getHeight();
2390
2391     contents.append("<br>Sequences: "+ viewport.alignment.getHeight());
2392     contents.append("<br>Minimum Sequence Length: "+min);
2393     contents.append("<br>Maximum Sequence Length: "+max);
2394     contents.append("<br>Average Length: "+(int)avg);
2395
2396     if (((Alignment)viewport.alignment).getProperties() != null)
2397     {
2398       Hashtable props = ((Alignment)viewport.alignment).getProperties();
2399       Enumeration en = props.keys();
2400       contents.append("<br><br><table border=\"1\">");
2401       while(en.hasMoreElements())
2402       {
2403         String key = en.nextElement().toString();
2404         StringBuffer val = new StringBuffer();
2405         String vals = props.get(key).toString();
2406         int pos=0, npos;
2407         do {
2408           npos = vals.indexOf("\n",pos);
2409           if (npos==-1)
2410           {
2411             val.append(vals.substring(pos));
2412           } else {
2413             val.append(vals.substring(pos, npos));
2414             val.append("<br>");
2415           }
2416           pos = npos+1;
2417         } while (npos!=-1);
2418         contents.append("<tr><td>"+key+"</td><td>"+val+"</td></tr>");
2419       }
2420       contents.append("</table>");
2421     }
2422     editPane.setText(contents.toString()+"</html>");
2423     JInternalFrame frame = new JInternalFrame();
2424     frame.getContentPane().add(new JScrollPane(editPane));
2425
2426     Desktop.instance.addInternalFrame(frame,"Alignment Properties: "+getTitle(),500,400);
2427   }
2428
2429
2430   /**
2431    * DOCUMENT ME!
2432    *
2433    * @param e DOCUMENT ME!
2434    */
2435   public void overviewMenuItem_actionPerformed(ActionEvent e)
2436   {
2437     if (alignPanel.overviewPanel != null)
2438     {
2439       return;
2440     }
2441
2442     JInternalFrame frame = new JInternalFrame();
2443     OverviewPanel overview = new OverviewPanel(alignPanel);
2444     frame.setContentPane(overview);
2445     Desktop.addInternalFrame(frame, "Overview " + this.getTitle(),
2446                              frame.getWidth(), frame.getHeight());
2447     frame.pack();
2448     frame.setLayer(JLayeredPane.PALETTE_LAYER);
2449     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
2450     {
2451       public void internalFrameClosed(
2452           javax.swing.event.InternalFrameEvent evt)
2453       {
2454         alignPanel.setOverviewPanel(null);
2455       }
2456       ;
2457     });
2458
2459     alignPanel.setOverviewPanel(overview);
2460   }
2461
2462   public void textColour_actionPerformed(ActionEvent e)
2463   {
2464     new TextColourChooser().chooseColour(alignPanel, null);
2465   }
2466
2467   /**
2468    * DOCUMENT ME!
2469    *
2470    * @param e DOCUMENT ME!
2471    */
2472   protected void noColourmenuItem_actionPerformed(ActionEvent e)
2473   {
2474     changeColour(null);
2475   }
2476
2477   /**
2478    * DOCUMENT ME!
2479    *
2480    * @param e DOCUMENT ME!
2481    */
2482   public void clustalColour_actionPerformed(ActionEvent e)
2483   {
2484     changeColour(new ClustalxColourScheme(
2485         viewport.alignment.getSequences(), viewport.alignment.getWidth()));
2486   }
2487
2488   /**
2489    * DOCUMENT ME!
2490    *
2491    * @param e DOCUMENT ME!
2492    */
2493   public void zappoColour_actionPerformed(ActionEvent e)
2494   {
2495     changeColour(new ZappoColourScheme());
2496   }
2497
2498   /**
2499    * DOCUMENT ME!
2500    *
2501    * @param e DOCUMENT ME!
2502    */
2503   public void taylorColour_actionPerformed(ActionEvent e)
2504   {
2505     changeColour(new TaylorColourScheme());
2506   }
2507
2508   /**
2509    * DOCUMENT ME!
2510    *
2511    * @param e DOCUMENT ME!
2512    */
2513   public void hydrophobicityColour_actionPerformed(ActionEvent e)
2514   {
2515     changeColour(new HydrophobicColourScheme());
2516   }
2517
2518   /**
2519    * DOCUMENT ME!
2520    *
2521    * @param e DOCUMENT ME!
2522    */
2523   public void helixColour_actionPerformed(ActionEvent e)
2524   {
2525     changeColour(new HelixColourScheme());
2526   }
2527
2528   /**
2529    * DOCUMENT ME!
2530    *
2531    * @param e DOCUMENT ME!
2532    */
2533   public void strandColour_actionPerformed(ActionEvent e)
2534   {
2535     changeColour(new StrandColourScheme());
2536   }
2537
2538   /**
2539    * DOCUMENT ME!
2540    *
2541    * @param e DOCUMENT ME!
2542    */
2543   public void turnColour_actionPerformed(ActionEvent e)
2544   {
2545     changeColour(new TurnColourScheme());
2546   }
2547
2548   /**
2549    * DOCUMENT ME!
2550    *
2551    * @param e DOCUMENT ME!
2552    */
2553   public void buriedColour_actionPerformed(ActionEvent e)
2554   {
2555     changeColour(new BuriedColourScheme());
2556   }
2557
2558   /**
2559    * DOCUMENT ME!
2560    *
2561    * @param e DOCUMENT ME!
2562    */
2563   public void nucleotideColour_actionPerformed(ActionEvent e)
2564   {
2565     changeColour(new NucleotideColourScheme());
2566   }
2567
2568   public void annotationColour_actionPerformed(ActionEvent e)
2569   {
2570     new AnnotationColourChooser(viewport, alignPanel);
2571   }
2572
2573
2574   /**
2575    * DOCUMENT ME!
2576    *
2577    * @param e DOCUMENT ME!
2578    */
2579   protected void applyToAllGroups_actionPerformed(ActionEvent e)
2580   {
2581     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
2582   }
2583
2584   /**
2585    * DOCUMENT ME!
2586    *
2587    * @param cs DOCUMENT ME!
2588    */
2589   public void changeColour(ColourSchemeI cs)
2590   {
2591     int threshold = 0;
2592
2593     if(cs!=null)
2594     {
2595       if (viewport.getAbovePIDThreshold())
2596       {
2597         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
2598                                                    "Background");
2599
2600         cs.setThreshold(threshold,
2601                         viewport.getIgnoreGapsConsensus());
2602
2603         viewport.setGlobalColourScheme(cs);
2604       }
2605       else
2606       {
2607         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
2608       }
2609
2610       if (viewport.getConservationSelected())
2611       {
2612
2613         Alignment al = (Alignment) viewport.alignment;
2614         Conservation c = new Conservation("All",
2615                                           ResidueProperties.propHash, 3,
2616                                           al.getSequences(), 0,
2617                                           al.getWidth() - 1);
2618
2619         c.calculate();
2620         c.verdict(false, viewport.ConsPercGaps);
2621
2622         cs.setConservation(c);
2623
2624         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel, cs,
2625             "Background"));
2626       }
2627       else
2628       {
2629         cs.setConservation(null);
2630       }
2631
2632       cs.setConsensus(viewport.hconsensus);
2633     }
2634
2635     viewport.setGlobalColourScheme(cs);
2636
2637     if (viewport.getColourAppliesToAllGroups())
2638     {
2639       Vector groups = viewport.alignment.getGroups();
2640
2641       for (int i = 0; i < groups.size(); i++)
2642       {
2643         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
2644
2645         if (cs == null)
2646         {
2647           sg.cs = null;
2648           continue;
2649         }
2650
2651         if (cs instanceof ClustalxColourScheme)
2652         {
2653           sg.cs = new ClustalxColourScheme(
2654               sg.getSequences(viewport.hiddenRepSequences), sg.getWidth());
2655         }
2656         else if (cs instanceof UserColourScheme)
2657         {
2658           sg.cs = new UserColourScheme( ( (UserColourScheme) cs).getColours());
2659         }
2660         else
2661         {
2662           try
2663           {
2664             sg.cs = (ColourSchemeI) cs.getClass().newInstance();
2665           }
2666           catch (Exception ex)
2667           {
2668           }
2669         }
2670
2671         if (viewport.getAbovePIDThreshold()
2672             || cs instanceof PIDColourScheme
2673             || cs instanceof Blosum62ColourScheme)
2674         {
2675          sg.cs.setThreshold(threshold,
2676                 viewport.getIgnoreGapsConsensus());
2677
2678          sg.cs.setConsensus(AAFrequency.calculate(
2679              sg.getSequences(viewport.hiddenRepSequences), sg.getStartRes(),
2680              sg.getEndRes()+1));
2681        }
2682         else
2683         {
2684           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
2685         }
2686
2687
2688         if (viewport.getConservationSelected())
2689         {
2690           Conservation c = new Conservation("Group",
2691                                             ResidueProperties.propHash, 3,
2692                                             sg.getSequences(viewport.
2693               hiddenRepSequences),
2694                                             sg.getStartRes(),
2695                                             sg.getEndRes()+1);
2696           c.calculate();
2697           c.verdict(false, viewport.ConsPercGaps);
2698           sg.cs.setConservation(c);
2699         }
2700         else
2701         {
2702           sg.cs.setConservation(null);
2703       }
2704     }
2705     }
2706
2707     if (alignPanel.getOverviewPanel() != null)
2708     {
2709       alignPanel.getOverviewPanel().updateOverviewImage();
2710     }
2711
2712
2713
2714
2715     alignPanel.paintAlignment(true);
2716   }
2717
2718   /**
2719    * DOCUMENT ME!
2720    *
2721    * @param e DOCUMENT ME!
2722    */
2723   protected void modifyPID_actionPerformed(ActionEvent e)
2724   {
2725     if (viewport.getAbovePIDThreshold() && viewport.globalColourScheme!=null)
2726     {
2727       SliderPanel.setPIDSliderSource(alignPanel,
2728                                      viewport.getGlobalColourScheme(),
2729                                      "Background");
2730       SliderPanel.showPIDSlider();
2731     }
2732   }
2733
2734   /**
2735    * DOCUMENT ME!
2736    *
2737    * @param e DOCUMENT ME!
2738    */
2739   protected void modifyConservation_actionPerformed(ActionEvent e)
2740   {
2741     if (viewport.getConservationSelected() && viewport.globalColourScheme!=null)
2742     {
2743       SliderPanel.setConservationSlider(alignPanel,
2744                                         viewport.globalColourScheme,
2745                                         "Background");
2746       SliderPanel.showConservationSlider();
2747     }
2748   }
2749
2750   /**
2751    * DOCUMENT ME!
2752    *
2753    * @param e DOCUMENT ME!
2754    */
2755   protected void conservationMenuItem_actionPerformed(ActionEvent e)
2756   {
2757     viewport.setConservationSelected(conservationMenuItem.isSelected());
2758
2759     viewport.setAbovePIDThreshold(false);
2760     abovePIDThreshold.setSelected(false);
2761
2762     changeColour(viewport.getGlobalColourScheme());
2763
2764     modifyConservation_actionPerformed(null);
2765   }
2766
2767   /**
2768    * DOCUMENT ME!
2769    *
2770    * @param e DOCUMENT ME!
2771    */
2772   public void abovePIDThreshold_actionPerformed(ActionEvent e)
2773   {
2774     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
2775
2776     conservationMenuItem.setSelected(false);
2777     viewport.setConservationSelected(false);
2778
2779     changeColour(viewport.getGlobalColourScheme());
2780
2781     modifyPID_actionPerformed(null);
2782   }
2783
2784   /**
2785    * DOCUMENT ME!
2786    *
2787    * @param e DOCUMENT ME!
2788    */
2789   public void userDefinedColour_actionPerformed(ActionEvent e)
2790   {
2791     if (e.getActionCommand().equals("User Defined..."))
2792     {
2793       new UserDefinedColours(alignPanel, null);
2794     }
2795     else
2796     {
2797       UserColourScheme udc = (UserColourScheme) UserDefinedColours.
2798           getUserColourSchemes().get(e.getActionCommand());
2799
2800       changeColour(udc);
2801     }
2802   }
2803
2804   public void updateUserColourMenu()
2805   {
2806
2807     Component[] menuItems = colourMenu.getMenuComponents();
2808     int i, iSize = menuItems.length;
2809     for (i = 0; i < iSize; i++)
2810     {
2811       if (menuItems[i].getName() != null &&
2812           menuItems[i].getName().equals("USER_DEFINED"))
2813       {
2814         colourMenu.remove(menuItems[i]);
2815         iSize--;
2816       }
2817     }
2818     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
2819     {
2820       java.util.Enumeration userColours = jalview.gui.UserDefinedColours.
2821           getUserColourSchemes().keys();
2822
2823       while (userColours.hasMoreElements())
2824       {
2825         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
2826             userColours.
2827             nextElement().toString());
2828         radioItem.setName("USER_DEFINED");
2829         radioItem.addMouseListener(new MouseAdapter()
2830             {
2831               public void mousePressed(MouseEvent evt)
2832               {
2833                 if(evt.isControlDown() || SwingUtilities.isRightMouseButton(evt))
2834                 {
2835                   radioItem.removeActionListener(radioItem.getActionListeners()[0]);
2836
2837               int option = JOptionPane.showInternalConfirmDialog(jalview.gui.
2838                   Desktop.desktop,
2839                       "Remove from default list?",
2840                       "Remove user defined colour",
2841                       JOptionPane.YES_NO_OPTION);
2842                   if(option == JOptionPane.YES_OPTION)
2843                   {
2844                 jalview.gui.UserDefinedColours.removeColourFromDefaults(
2845                     radioItem.getText());
2846                     colourMenu.remove(radioItem);
2847                   }
2848                   else
2849               {
2850                     radioItem.addActionListener(new ActionListener()
2851                     {
2852                       public void actionPerformed(ActionEvent evt)
2853                       {
2854                         userDefinedColour_actionPerformed(evt);
2855                       }
2856                     });
2857                 }
2858               }
2859           }
2860             });
2861         radioItem.addActionListener(new ActionListener()
2862         {
2863           public void actionPerformed(ActionEvent evt)
2864           {
2865             userDefinedColour_actionPerformed(evt);
2866           }
2867         });
2868
2869         colourMenu.insert(radioItem, 15);
2870         colours.add(radioItem);
2871       }
2872     }
2873   }
2874
2875   /**
2876    * DOCUMENT ME!
2877    *
2878    * @param e DOCUMENT ME!
2879    */
2880   public void PIDColour_actionPerformed(ActionEvent e)
2881   {
2882     changeColour(new PIDColourScheme());
2883   }
2884
2885   /**
2886    * DOCUMENT ME!
2887    *
2888    * @param e DOCUMENT ME!
2889    */
2890   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
2891   {
2892     changeColour(new Blosum62ColourScheme());
2893   }
2894
2895   /**
2896    * DOCUMENT ME!
2897    *
2898    * @param e DOCUMENT ME!
2899    */
2900   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
2901   {
2902     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
2903     AlignmentSorter.sortByPID(viewport.getAlignment(),
2904                               viewport.getAlignment().getSequenceAt(0), null);
2905     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
2906                                     viewport.alignment));
2907     alignPanel.paintAlignment(true);
2908   }
2909
2910   /**
2911    * DOCUMENT ME!
2912    *
2913    * @param e DOCUMENT ME!
2914    */
2915   public void sortIDMenuItem_actionPerformed(ActionEvent e)
2916   {
2917     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
2918     AlignmentSorter.sortByID(viewport.getAlignment());
2919     addHistoryItem(new OrderCommand("ID Sort", oldOrder, viewport.alignment));
2920     alignPanel.paintAlignment(true);
2921   }
2922
2923   /**
2924    * DOCUMENT ME!
2925    *
2926    * @param e DOCUMENT ME!
2927    */
2928   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
2929   {
2930     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
2931     AlignmentSorter.sortByGroup(viewport.getAlignment());
2932     addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.alignment));
2933
2934     alignPanel.paintAlignment(true);
2935   }
2936   /**
2937    * DOCUMENT ME!
2938    *
2939    * @param e DOCUMENT ME!
2940    */
2941   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
2942   {
2943     new RedundancyPanel(alignPanel, this);
2944   }
2945
2946
2947   /**
2948    * DOCUMENT ME!
2949    *
2950    * @param e DOCUMENT ME!
2951    */
2952   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
2953   {
2954     if ( (viewport.getSelectionGroup() == null) ||
2955         (viewport.getSelectionGroup().getSize() < 2))
2956     {
2957       JOptionPane.showInternalMessageDialog(this,
2958                                             "You must select at least 2 sequences.",
2959                                             "Invalid Selection",
2960                                             JOptionPane.WARNING_MESSAGE);
2961     }
2962     else
2963     {
2964       JInternalFrame frame = new JInternalFrame();
2965       frame.setContentPane(new PairwiseAlignPanel(viewport));
2966       Desktop.addInternalFrame(frame, "Pairwise Alignment", 600, 500);
2967     }
2968   }
2969
2970   /**
2971    * DOCUMENT ME!
2972    *
2973    * @param e DOCUMENT ME!
2974    */
2975   public void PCAMenuItem_actionPerformed(ActionEvent e)
2976   {
2977     if ( ( (viewport.getSelectionGroup() != null) &&
2978           (viewport.getSelectionGroup().getSize() < 4) &&
2979           (viewport.getSelectionGroup().getSize() > 0)) ||
2980         (viewport.getAlignment().getHeight() < 4))
2981     {
2982       JOptionPane.showInternalMessageDialog(this,
2983                                             "Principal component analysis must take\n" +
2984                                             "at least 4 input sequences.",
2985                                             "Sequence selection insufficient",
2986                                             JOptionPane.WARNING_MESSAGE);
2987
2988       return;
2989     }
2990
2991      new PCAPanel(alignPanel);
2992   }
2993
2994
2995   public void autoCalculate_actionPerformed(ActionEvent e)
2996   {
2997     viewport.autoCalculateConsensus = autoCalculate.isSelected();
2998     if(viewport.autoCalculateConsensus)
2999     {
3000       viewport.firePropertyChange("alignment",
3001                                   null,
3002                                   viewport.getAlignment().getSequences());
3003     }
3004   }
3005
3006
3007   /**
3008    * DOCUMENT ME!
3009    *
3010    * @param e DOCUMENT ME!
3011    */
3012   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3013   {
3014     NewTreePanel("AV", "PID", "Average distance tree using PID");
3015   }
3016
3017   /**
3018    * DOCUMENT ME!
3019    *
3020    * @param e DOCUMENT ME!
3021    */
3022   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3023   {
3024     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3025   }
3026
3027   /**
3028    * DOCUMENT ME!
3029    *
3030    * @param e DOCUMENT ME!
3031    */
3032   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3033   {
3034     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3035   }
3036
3037   /**
3038    * DOCUMENT ME!
3039    *
3040    * @param e DOCUMENT ME!
3041    */
3042   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3043   {
3044     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3045   }
3046
3047   /**
3048    * DOCUMENT ME!
3049    *
3050    * @param type DOCUMENT ME!
3051    * @param pwType DOCUMENT ME!
3052    * @param title DOCUMENT ME!
3053    */
3054   void NewTreePanel(String type, String pwType, String title)
3055   {
3056     TreePanel tp;
3057
3058     if (viewport.getSelectionGroup() != null)
3059     {
3060       if (viewport.getSelectionGroup().getSize() < 3)
3061       {
3062         JOptionPane.showMessageDialog(Desktop.desktop,
3063                                       "You need to have more than two sequences selected to build a tree!",
3064                                       "Not enough sequences",
3065                                       JOptionPane.WARNING_MESSAGE);
3066         return;
3067       }
3068
3069       int s = 0;
3070       SequenceGroup sg = viewport.getSelectionGroup();
3071
3072       /* Decide if the selection is a column region */
3073       while (s < sg.getSize())
3074       {
3075         if ( ( (SequenceI) sg.getSequences(null).elementAt(s++)).getLength() <
3076             sg.getEndRes())
3077         {
3078           JOptionPane.showMessageDialog(Desktop.desktop,
3079                                         "The selected region to create a tree may\nonly contain residues or gaps.\n" +
3080                                         "Try using the Pad function in the edit menu,\n" +
3081                                         "or one of the multiple sequence alignment web services.",
3082                                         "Sequences in selection are not aligned",
3083                                         JOptionPane.WARNING_MESSAGE);
3084
3085           return;
3086         }
3087       }
3088
3089       title = title + " on region";
3090       tp = new TreePanel(alignPanel, type, pwType);
3091     }
3092     else
3093     {
3094       //are the sequences aligned?
3095       if (!viewport.alignment.isAligned())
3096       {
3097         JOptionPane.showMessageDialog(Desktop.desktop,
3098                                       "The sequences must be aligned before creating a tree.\n" +
3099                                       "Try using the Pad function in the edit menu,\n" +
3100                                       "or one of the multiple sequence alignment web services.",
3101                                       "Sequences not aligned",
3102                                       JOptionPane.WARNING_MESSAGE);
3103
3104         return;
3105       }
3106
3107       if(viewport.alignment.getHeight()<2)
3108       {
3109         return;
3110       }
3111
3112       tp = new TreePanel(alignPanel, type, pwType);
3113     }
3114
3115     title += " from ";
3116
3117     if(viewport.viewName!=null)
3118     {
3119       title+= viewport.viewName+" of ";
3120     }
3121
3122     title += this.title;
3123
3124     Desktop.addInternalFrame(tp, title, 600, 500);
3125   }
3126
3127   /**
3128    * DOCUMENT ME!
3129    *
3130    * @param title DOCUMENT ME!
3131    * @param order DOCUMENT ME!
3132    */
3133   public void addSortByOrderMenuItem(String title, final AlignmentOrder order)
3134   {
3135     final JMenuItem item = new JMenuItem("by " + title);
3136     sort.add(item);
3137     item.addActionListener(new java.awt.event.ActionListener()
3138     {
3139       public void actionPerformed(ActionEvent e)
3140       {
3141         SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
3142
3143         // TODO: JBPNote - have to map order entries to curent SequenceI pointers
3144         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3145
3146         addHistoryItem(new OrderCommand(order.getName(), oldOrder,
3147                                         viewport.alignment));
3148
3149         alignPanel.paintAlignment(true);
3150       }
3151     });
3152   }
3153   /**
3154    * Add a new sort by annotation score menu item
3155    * @param sort the menu to add the option to
3156    * @param scoreLabel the label used to retrieve scores for each sequence on the alignment
3157    */
3158   public void addSortByAnnotScoreMenuItem(JMenu sort, final String scoreLabel)
3159   {
3160     final JMenuItem item = new JMenuItem(scoreLabel);
3161     sort.add(item);
3162     item.addActionListener(new java.awt.event.ActionListener()
3163     {
3164       public void actionPerformed(ActionEvent e)
3165       {
3166         SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
3167         AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport.getAlignment());//,viewport.getSelectionGroup());
3168         addHistoryItem(new OrderCommand("Sort by "+scoreLabel, oldOrder, viewport.alignment));
3169         alignPanel.paintAlignment(true);
3170       }
3171     });
3172   }
3173   /**
3174    * last hash for alignment's annotation array - used to minimise cost of rebuild.
3175    */
3176   protected int _annotationScoreVectorHash;
3177   /**
3178    * search the alignment and rebuild the sort by annotation score submenu
3179    * the last alignment annotation vector hash is stored to minimize
3180    * cost of rebuilding in subsequence calls.
3181    *
3182    */
3183   public void buildSortByAnnotationScoresMenu()
3184   {
3185     if(viewport.alignment.getAlignmentAnnotation()==null)
3186     {
3187       return;
3188     }
3189
3190     if (viewport.alignment.getAlignmentAnnotation().hashCode()!=_annotationScoreVectorHash)
3191     {
3192       sortByAnnotScore.removeAll();
3193       // almost certainly a quicker way to do this - but we keep it simple
3194       Hashtable scoreSorts=new Hashtable();
3195       AlignmentAnnotation aann[];
3196       Enumeration sq = viewport.alignment.getSequences().elements();
3197       while (sq.hasMoreElements())
3198       {
3199         aann = ((SequenceI) sq.nextElement()).getAnnotation();
3200         for (int i=0;aann!=null && i<aann.length; i++)
3201         {
3202           if (aann[i].hasScore() && aann[i].sequenceRef!=null)
3203           {
3204             scoreSorts.put(aann[i].label, aann[i].label);
3205           }
3206         }
3207       }
3208       Enumeration labels = scoreSorts.keys();
3209       while (labels.hasMoreElements())
3210       {
3211         addSortByAnnotScoreMenuItem(sortByAnnotScore, (String) labels.nextElement());
3212       }
3213       sortByAnnotScore.setVisible(scoreSorts.size()>0);
3214       scoreSorts.clear();
3215
3216       _annotationScoreVectorHash =
3217           viewport.alignment.getAlignmentAnnotation().hashCode();
3218     }
3219   }
3220
3221   /**
3222    * Maintain the Order by->Displayed Tree menu.
3223    * Creates a new menu item for a TreePanel with an appropriate
3224    * <code>jalview.analysis.AlignmentSorter</code> call. Listeners are added
3225    * to remove the menu item when the treePanel is closed, and adjust
3226    * the tree leaf to sequence mapping when the alignment is modified.
3227    * @param treePanel Displayed tree window.
3228    * @param title SortBy menu item title.
3229    */
3230   public void buildTreeMenu()
3231   {
3232     sortByTreeMenu.removeAll();
3233
3234     Vector comps = (Vector) PaintRefresher.components.get(viewport.
3235         getSequenceSetId());
3236     Vector treePanels = new Vector();
3237     int i, iSize = comps.size();
3238     for(i=0; i<iSize; i++)
3239     {
3240       if(comps.elementAt(i) instanceof TreePanel)
3241       {
3242         treePanels.add(comps.elementAt(i));
3243       }
3244     }
3245
3246     iSize = treePanels.size();
3247
3248     if(iSize<1)
3249     {
3250       sortByTreeMenu.setVisible(false);
3251       return;
3252     }
3253
3254     sortByTreeMenu.setVisible(true);
3255
3256     for(i=0; i<treePanels.size(); i++)
3257     {
3258       TreePanel tp = (TreePanel)treePanels.elementAt(i);
3259       final JMenuItem item = new JMenuItem(tp.getTitle());
3260       final NJTree tree = ((TreePanel)treePanels.elementAt(i)).getTree();
3261       item.addActionListener(new java.awt.event.ActionListener()
3262       {
3263         public void actionPerformed(ActionEvent e)
3264         {
3265           SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3266           AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
3267
3268           addHistoryItem(new OrderCommand("Tree Sort",
3269                                           oldOrder,
3270                                           viewport.alignment));
3271
3272           alignPanel.paintAlignment(true);
3273         }
3274       });
3275
3276       sortByTreeMenu.add(item);
3277     }
3278   }
3279
3280   /**
3281    * Work out whether the whole set of sequences
3282    * or just the selected set will be submitted for multiple alignment.
3283    *
3284    */
3285   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
3286   {
3287     // Now, check we have enough sequences
3288     AlignmentView msa = null;
3289
3290     if ( (viewport.getSelectionGroup() != null) &&
3291         (viewport.getSelectionGroup().getSize() > 1))
3292     {
3293       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to some common interface!
3294       /*SequenceGroup seqs = viewport.getSelectionGroup();
3295       int sz;
3296       msa = new SequenceI[sz = seqs.getSize(false)];
3297
3298       for (int i = 0; i < sz; i++)
3299       {
3300         msa[i] = (SequenceI) seqs.getSequenceAt(i);
3301       } */
3302       msa = viewport.getAlignmentView(true);
3303     }
3304     else
3305     {
3306       /*Vector seqs = viewport.getAlignment().getSequences();
3307
3308       if (seqs.size() > 1)
3309       {
3310         msa = new SequenceI[seqs.size()];
3311
3312         for (int i = 0; i < seqs.size(); i++)
3313         {
3314           msa[i] = (SequenceI) seqs.elementAt(i);
3315         }
3316       }*/
3317       msa = viewport.getAlignmentView(false);
3318     }
3319     return msa;
3320   }
3321
3322   /**
3323    * Decides what is submitted to a secondary structure prediction service:
3324    * the first sequence in the alignment, or in the current selection,
3325    * or, if the alignment is 'aligned' (ie padded with gaps), then the
3326    * currently selected region or the whole alignment.
3327    * (where the first sequence in the set is the one that the prediction
3328    * will be for).
3329    */
3330   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
3331   {
3332    AlignmentView seqs = null;
3333
3334     if ( (viewport.getSelectionGroup() != null) &&
3335         (viewport.getSelectionGroup().getSize() > 0))
3336     {
3337       seqs = viewport.getAlignmentView(true);
3338     }
3339     else
3340     {
3341       seqs = viewport.getAlignmentView(false);
3342     }
3343     // limit sequences - JBPNote in future - could spawn multiple prediction jobs
3344     // TODO: viewport.alignment.isAligned is a global state - the local selection may well be aligned - we preserve 2.0.8 behaviour for moment.
3345     if (!viewport.alignment.isAligned())
3346     {
3347       seqs.setSequences(new SeqCigar[]
3348                         {seqs.getSequences()[0]});
3349     }
3350     return seqs;
3351   }
3352   /**
3353    * DOCUMENT ME!
3354    *
3355    * @param e DOCUMENT ME!
3356    */
3357   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
3358   {
3359     // Pick the tree file
3360     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
3361         getProperty(
3362             "LAST_DIRECTORY"));
3363     chooser.setFileView(new JalviewFileView());
3364     chooser.setDialogTitle("Select a newick-like tree file");
3365     chooser.setToolTipText("Load a tree file");
3366
3367     int value = chooser.showOpenDialog(null);
3368
3369     if (value == JalviewFileChooser.APPROVE_OPTION)
3370     {
3371       String choice = chooser.getSelectedFile().getPath();
3372       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
3373       jalview.io.NewickFile fin = null;
3374       try
3375       {
3376         fin = new jalview.io.NewickFile(choice,
3377             "File");
3378         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
3379       }
3380       catch (Exception ex)
3381       {
3382         JOptionPane.showMessageDialog(Desktop.desktop,
3383                                       ex.getMessage(),
3384                                       "Problem reading tree file",
3385                                       JOptionPane.WARNING_MESSAGE);
3386         ex.printStackTrace();
3387       }
3388       if (fin!=null && fin.hasWarningMessage())
3389       {
3390         JOptionPane.showMessageDialog(Desktop.desktop,
3391                 fin.getWarningMessage(),
3392                 "Possible problem with tree file",
3393                 JOptionPane.WARNING_MESSAGE);
3394       }
3395     }
3396   }
3397
3398
3399   public TreePanel ShowNewickTree(NewickFile nf, String title)
3400   {
3401     return ShowNewickTree(nf,title,600,500,4,5);
3402   }
3403
3404   public TreePanel ShowNewickTree(NewickFile nf, String title,
3405                                   AlignmentView input)
3406   {
3407     return ShowNewickTree(nf,title, input, 600,500,4,5);
3408   }
3409
3410   public TreePanel ShowNewickTree(NewickFile nf, String title, int w, int h,
3411                                   int x, int y)
3412   {
3413     return ShowNewickTree(nf, title, null, w, h, x, y);
3414   }
3415   /**
3416    * Add a treeviewer for the tree extracted from a newick file object to the current alignment view
3417    *
3418    * @param nf the tree
3419    * @param title tree viewer title
3420    * @param input Associated alignment input data (or null)
3421    * @param w width
3422    * @param h height
3423    * @param x position
3424    * @param y position
3425    * @return TreePanel handle
3426    */
3427   public TreePanel ShowNewickTree(NewickFile nf, String title,
3428                                   AlignmentView input, int w, int h, int x,
3429                                   int y)
3430   {
3431     TreePanel tp = null;
3432
3433     try
3434     {
3435       nf.parse();
3436
3437       if (nf.getTree() != null)
3438       {
3439         tp = new TreePanel(alignPanel,
3440                            "FromFile",
3441                            title,
3442                            nf, input);
3443
3444         tp.setSize(w,h);
3445
3446         if(x>0 && y>0)
3447         {
3448           tp.setLocation(x,y);
3449         }
3450
3451
3452         Desktop.addInternalFrame(tp, title, w, h);
3453       }
3454     }
3455     catch (Exception ex)
3456     {
3457       ex.printStackTrace();
3458     }
3459     
3460     return tp;
3461   }
3462
3463
3464   /**
3465    * Generates menu items and listener event actions for web service clients
3466    *
3467    */
3468   public void BuildWebServiceMenu()
3469   {
3470     // TODO: add support for context dependent disabling of services based on alignment and current selection
3471     // TODO: add additional serviceHandle parameter to specify abstract handler class independently of AbstractName
3472     // TODO: add in rediscovery GUI function to restart discoverer
3473     // TODO: group services by location as well as function and/or introduce object broker mechanism.
3474     if ( (Discoverer.services != null)
3475         && (Discoverer.services.size() > 0))
3476     {
3477       // TODO: refactor to allow list of AbstractName/Handler bindings to be stored or retrieved from elsewhere
3478       Vector msaws = (Vector) Discoverer.services.get("MsaWS");
3479       Vector secstrpr = (Vector) Discoverer.services.get("SecStrPred");
3480       Vector seqsrch = (Vector) Discoverer.services.get("SeqSearch");
3481       // 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)
3482       Vector wsmenu = new Vector();
3483       final IProgressIndicator af = this;
3484       if (msaws != null)
3485       {
3486         // Add any Multiple Sequence Alignment Services
3487         final JMenu msawsmenu = new JMenu("Alignment");
3488         for (int i = 0, j = msaws.size(); i < j; i++)
3489         {
3490           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws.
3491               get(i);
3492           jalview.ws.WSClient impl = jalview.ws.Discoverer.getServiceClient(sh);
3493           impl.attachWSMenuEntry(msawsmenu, this);
3494         
3495         }
3496         wsmenu.add(msawsmenu);
3497       }
3498       if (secstrpr != null)
3499       {
3500         // Add any secondary structure prediction services
3501         final JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
3502         for (int i = 0, j = secstrpr.size(); i < j; i++)
3503         {
3504           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle)
3505               secstrpr.get(i);
3506           jalview.ws.WSClient impl = jalview.ws.Discoverer.getServiceClient(sh);
3507           impl.attachWSMenuEntry(secstrmenu, this);
3508         }
3509         wsmenu.add(secstrmenu);
3510       }
3511       if (seqsrch!=null)
3512       {
3513         // Add any sequence search services
3514         final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
3515         for (int i = 0, j = seqsrch.size(); i < j; i++)
3516         {
3517           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle)
3518               seqsrch.elementAt(i);
3519           jalview.ws.WSClient impl = jalview.ws.Discoverer.getServiceClient(sh);
3520           impl.attachWSMenuEntry(seqsrchmenu, this);
3521         }
3522         // finally, add the whole shebang onto the webservices menu
3523         wsmenu.add(seqsrchmenu); 
3524       }
3525       resetWebServiceMenu();
3526       for (int i = 0, j = wsmenu.size(); i < j; i++)
3527       {
3528         webService.add( (JMenu) wsmenu.get(i));
3529       }
3530     }
3531     else
3532     {
3533       resetWebServiceMenu();
3534       this.webService.add(this.webServiceNoServices);
3535     }
3536   }
3537
3538
3539   /**
3540    * empty the web service menu and add any ad-hoc functions
3541    * not dynamically discovered.
3542    *
3543    */
3544   private void resetWebServiceMenu()
3545   {
3546     webService.removeAll();
3547     // Temporary hack - DBRef Fetcher always top level ws entry.
3548     JMenuItem rfetch = new JMenuItem("Fetch DB References");
3549     rfetch.setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
3550     webService.add(rfetch);
3551     rfetch.addActionListener(new ActionListener() {
3552
3553       public void actionPerformed(ActionEvent e)
3554       {
3555         new Thread(new Runnable() {
3556
3557           public void run()
3558           {
3559             new jalview.ws.DBRefFetcher(
3560                     alignPanel.av.getSequenceSelection(),
3561                     alignPanel.alignFrame).fetchDBRefs(false);
3562           }
3563         }).start();
3564         
3565       }
3566
3567     });
3568   }
3569
3570  /* public void vamsasStore_actionPerformed(ActionEvent e)
3571   {
3572     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
3573         getProperty("LAST_DIRECTORY"));
3574
3575     chooser.setFileView(new JalviewFileView());
3576     chooser.setDialogTitle("Export to Vamsas file");
3577     chooser.setToolTipText("Export");
3578
3579     int value = chooser.showSaveDialog(this);
3580
3581     if (value == JalviewFileChooser.APPROVE_OPTION)
3582     {
3583       jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
3584       //vs.store(chooser.getSelectedFile().getAbsolutePath()   );
3585       vs.storeJalview( chooser.getSelectedFile().getAbsolutePath(), this);
3586     }
3587   }*/
3588   /**
3589    * prototype of an automatically enabled/disabled analysis function
3590    *
3591    */
3592   protected void setShowProductsEnabled()
3593   {
3594     SequenceI [] selection = viewport.getSequenceSelection();
3595     if (canShowProducts(selection, viewport.getSelectionGroup()!=null, viewport.getAlignment().getDataset()))
3596     {
3597       showProducts.setEnabled(true);
3598       
3599     } else {
3600       showProducts.setEnabled(false);
3601     }
3602   }
3603   /**
3604    * search selection for sequence xRef products and build the
3605    * show products menu.
3606    * @param selection
3607    * @param dataset
3608    * @return true if showProducts menu should be enabled.
3609    */
3610   public boolean canShowProducts(SequenceI[] selection, boolean isRegionSelection, Alignment dataset)
3611   {
3612     boolean showp=false;
3613     try {
3614       showProducts.removeAll();
3615       final boolean dna = viewport.getAlignment().isNucleotide();
3616       final Alignment ds = dataset;
3617       String[] ptypes = CrossRef.findSequenceXrefTypes(dna, selection, dataset);
3618       //Object[] prods = CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(), selection, dataset, true);
3619       final SequenceI[] sel = selection;
3620       for (int t=0; ptypes!=null && t<ptypes.length; t++)
3621       {
3622         showp=true;
3623         final boolean isRegSel = isRegionSelection;
3624         final AlignFrame af = this;
3625         final String source = ptypes[t];
3626         JMenuItem xtype = new JMenuItem(ptypes[t]);
3627         xtype.addActionListener(new ActionListener() {
3628
3629           public void actionPerformed(ActionEvent e)
3630           {
3631             // TODO: new thread for this call with vis-delay
3632             af.showProductsFor(af.viewport.getSequenceSelection(), ds, isRegSel, dna, source);
3633           }
3634           
3635         });
3636         showProducts.add(xtype);
3637       }
3638       showProducts.setVisible(showp);
3639       showProducts.setEnabled(showp);
3640     } catch (Exception e)
3641     {
3642       jalview.bin.Cache.log.warn("canTranslate threw an exception - please report to help@jalview.org",e);
3643      return false;
3644     }
3645     return showp;
3646   }
3647 protected void showProductsFor(SequenceI[] sel, Alignment ds, boolean isRegSel, boolean dna, String source)
3648   {
3649   final boolean fisRegSel = isRegSel;
3650   final boolean fdna = dna;
3651   final String fsrc = source;
3652   final AlignFrame ths = this;
3653   final SequenceI[] fsel = sel;
3654   Runnable foo = new Runnable() {
3655
3656     public void run()
3657     {
3658       final long sttime = System.currentTimeMillis();
3659       ths.setProgressBar("Searching for sequences from "+fsrc, sttime);
3660       try {
3661         Alignment ds = ths.getViewport().alignment.getDataset(); // update our local dataset reference
3662       Alignment prods = CrossRef.findXrefSequences(fsel, fdna, fsrc, ds);
3663       if (prods!=null)
3664       {
3665         SequenceI[] sprods = new SequenceI[prods.getHeight()];
3666         for (int s=0; s<sprods.length;s++)
3667         {
3668           sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
3669           if (ds.getSequences()==null || !ds.getSequences().contains(sprods[s].getDatasetSequence()))
3670             ds.addSequence(sprods[s].getDatasetSequence());
3671           sprods[s].updatePDBIds();
3672         }
3673         Alignment al = new Alignment(sprods);
3674         AlignedCodonFrame[] cf = prods.getCodonFrames();
3675         for (int s=0; cf!=null && s<cf.length; s++)
3676         {
3677           al.addCodonFrame(cf[s]);
3678           cf[s] = null;
3679         }
3680         al.setDataset(ds);
3681         AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
3682         String newtitle =""+((fdna) ? "Proteins " : "Nucleotides ") + " for "+((fisRegSel) ? "selected region of " : "")
3683                 + getTitle();
3684         Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
3685                 DEFAULT_HEIGHT);
3686       } else {
3687         System.err.println("No Sequences generated for xRef type "+fsrc);
3688       }
3689       }
3690       catch (Exception e)
3691       {
3692         jalview.bin.Cache.log.error("Exception when finding crossreferences",e);
3693       }
3694       catch (Error e)
3695       {
3696         jalview.bin.Cache.log.error("Error when finding crossreferences",e);
3697       }
3698       ths.setProgressBar("Finished searching for sequences from "+fsrc, sttime);
3699     }
3700     
3701   };
3702   Thread frunner = new Thread(foo);
3703   frunner.start();
3704   }
3705
3706
3707 public boolean canShowTranslationProducts(SequenceI[] selection, AlignmentI alignment)
3708 {
3709   // old way
3710   try {
3711       return (jalview.analysis.Dna.canTranslate(selection, viewport.getViewAsVisibleContigs(true)));
3712   } catch (Exception e)
3713   {
3714     jalview.bin.Cache.log.warn("canTranslate threw an exception - please report to help@jalview.org",e);
3715    return false;
3716   }
3717 }
3718
3719 public void showProducts_actionPerformed(ActionEvent e)
3720 {
3721   ///////////////////////////////
3722   // Collect Data to be translated/transferred
3723   
3724   SequenceI [] selection = viewport.getSequenceSelection();
3725   AlignmentI al  = null;
3726   try {
3727       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport.getViewAsVisibleContigs(true),
3728           viewport.getGapCharacter(), viewport.getAlignment().getDataset());
3729     } catch (Exception ex) {
3730       al = null;
3731       jalview.bin.Cache.log.debug("Exception during translation.",ex);
3732     }
3733     if (al==null)
3734     {
3735       JOptionPane.showMessageDialog(Desktop.desktop,
3736           "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
3737           "Translation Failed",
3738           JOptionPane.WARNING_MESSAGE);
3739     } else {
3740       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
3741       Desktop.addInternalFrame(af, "Translation of "+this.getTitle(),
3742                                DEFAULT_WIDTH,
3743                                DEFAULT_HEIGHT);
3744     }
3745   }
3746
3747 public void showTranslation_actionPerformed(ActionEvent e)
3748 {
3749   ///////////////////////////////
3750   // Collect Data to be translated/transferred
3751
3752   SequenceI [] selection = viewport.getSequenceSelection();
3753   String [] seqstring = viewport.getViewAsString(true);
3754   AlignmentI al  = null;
3755   try {
3756     al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring, viewport.getViewAsVisibleContigs(true),
3757         viewport.getGapCharacter(), viewport.alignment.getAlignmentAnnotation(),
3758         viewport.alignment.getWidth(), viewport.getAlignment().getDataset());
3759   } catch (Exception ex) {
3760     al = null;
3761     jalview.bin.Cache.log.debug("Exception during translation.",ex);
3762   }
3763   if (al==null)
3764   {
3765     JOptionPane.showMessageDialog(Desktop.desktop,
3766         "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
3767         "Translation Failed",
3768         JOptionPane.WARNING_MESSAGE);
3769   } else {
3770     AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
3771     Desktop.addInternalFrame(af, "Translation of "+this.getTitle(),
3772                              DEFAULT_WIDTH,
3773                              DEFAULT_HEIGHT);
3774   }
3775 }
3776
3777 /**
3778  * Try to load a features file onto the alignment.
3779  * @param file contents or path to retrieve file 
3780  * @param type access mode of file (see jalview.io.AlignFile)
3781  * @return true if features file was parsed corectly.
3782  */
3783 public boolean parseFeaturesFile(String file, String type)
3784 {
3785     boolean featuresFile = false;
3786     try
3787     {
3788       featuresFile = new FeaturesFile(file,
3789           type).parse(viewport.alignment.getDataset(),
3790                                          alignPanel.seqPanel.seqCanvas.
3791                                          getFeatureRenderer().featureColours,
3792                                          false);
3793     }
3794     catch(Exception ex)
3795     {
3796       ex.printStackTrace();
3797     }
3798
3799     if(featuresFile)
3800     {
3801       viewport.showSequenceFeatures = true;
3802       showSeqFeatures.setSelected(true);
3803       alignPanel.paintAlignment(true);
3804     }
3805
3806     return featuresFile;
3807 }
3808
3809 public void dragEnter(DropTargetDragEvent evt)
3810 {}
3811
3812 public void dragExit(DropTargetEvent evt)
3813 {}
3814
3815 public void dragOver(DropTargetDragEvent evt)
3816 {}
3817
3818 public void dropActionChanged(DropTargetDragEvent evt)
3819 {}
3820
3821 public void drop(DropTargetDropEvent evt)
3822 {
3823     Transferable t = evt.getTransferable();
3824     java.util.List files = null;
3825
3826     try
3827     {
3828       DataFlavor uriListFlavor = new DataFlavor(
3829           "text/uri-list;class=java.lang.String");
3830       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3831       {
3832         //Works on Windows and MacOSX
3833         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3834         files = (java.util.List) t.getTransferData(DataFlavor.
3835             javaFileListFlavor);
3836       }
3837       else if (t.isDataFlavorSupported(uriListFlavor))
3838       {
3839         // This is used by Unix drag system
3840         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3841         String data = (String) t.getTransferData(uriListFlavor);
3842         files = new java.util.ArrayList(1);
3843         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3844             data,
3845             "\r\n");
3846              st.hasMoreTokens(); )
3847         {
3848           String s = st.nextToken();
3849           if (s.startsWith("#"))
3850           {
3851             // the line is a comment (as per the RFC 2483)
3852             continue;
3853           }
3854
3855           java.net.URI uri = new java.net.URI(s);
3856           java.io.File file = new java.io.File(uri);
3857           files.add(file);
3858         }
3859       }
3860     }
3861     catch (Exception e)
3862     {
3863       e.printStackTrace();
3864     }
3865     if (files != null)
3866     {
3867       try
3868       {
3869
3870         for (int i = 0; i < files.size(); i++)
3871         {
3872           loadJalviewDataFile(files.get(i).toString());
3873         }
3874       }
3875       catch (Exception ex)
3876       {
3877         ex.printStackTrace();
3878       }
3879     }
3880 }
3881
3882   /**     
3883    * Attempt to load a "dropped" file: First by testing
3884    * whether it's and Annotation file, then a JNet file, and finally a features file. If all are
3885    * false then the user may have dropped an alignment file onto this
3886    * AlignFrame.
3887    * @param file either a filename or a URL string.
3888    */
3889    public void loadJalviewDataFile(String file)
3890    {
3891     try
3892     {
3893       String protocol = "File";
3894
3895       if (file.indexOf("http:") > -1 || file.indexOf("file:") > -1)
3896       {
3897         protocol = "URL";
3898       }
3899
3900       boolean isAnnotation = new AnnotationFile().readAnnotationFile(viewport.
3901           alignment, file, protocol);
3902
3903       if (!isAnnotation)
3904       {
3905         // try to see if its a JNet 'concise' style annotation file *before* we try to parse it as a features file
3906         String format = new IdentifyFile().Identify(file, protocol);
3907         if(format.equalsIgnoreCase("JnetFile"))
3908         {
3909           jalview.io.JPredFile predictions = new jalview.io.JPredFile(
3910                   file, protocol);
3911           new JnetAnnotationMaker().add_annotation(predictions,
3912                 viewport.getAlignment(),
3913                 0, false);
3914           isAnnotation=true;
3915         }
3916         else
3917         {
3918           // try to parse it as a features file
3919           boolean isGroupsFile = parseFeaturesFile(file,protocol);
3920           // if it wasn't a features file then we just treat it as a general alignment file to load into the current view.
3921           if (!isGroupsFile)
3922           {
3923             new FileLoader().LoadFile(viewport, file, protocol, format);
3924           } else {
3925             alignPanel.paintAlignment(true);
3926           }
3927         }
3928       }
3929       if (isAnnotation)
3930       {
3931         
3932         alignPanel.adjustAnnotationHeight();
3933         viewport.updateSequenceIdColours();
3934         buildSortByAnnotationScoresMenu();
3935         alignPanel.paintAlignment(true);
3936       }
3937     }
3938     catch (Exception ex)
3939     {
3940       ex.printStackTrace();
3941     }
3942   }
3943
3944   public void tabSelectionChanged(int index)
3945   {
3946     if (index > -1)
3947     {
3948       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
3949       viewport = alignPanel.av;
3950       setMenusFromViewport(viewport);
3951     }
3952   }
3953
3954   public void tabbedPane_mousePressed(MouseEvent e)
3955   {
3956     if(SwingUtilities.isRightMouseButton(e))
3957     {
3958       String reply = JOptionPane.showInternalInputDialog(this,
3959           "Enter View Name",
3960           "Edit View Name",
3961           JOptionPane.QUESTION_MESSAGE);
3962
3963       if (reply != null)
3964       {
3965         viewport.viewName = reply;
3966         tabbedPane.setTitleAt( tabbedPane.getSelectedIndex() ,reply);
3967       }
3968     }
3969   }
3970
3971
3972   public AlignViewport getCurrentView()
3973   {
3974     return viewport;
3975   }
3976
3977
3978   /**
3979    * Open the dialog for regex description parsing.
3980    */
3981   protected void extractScores_actionPerformed(ActionEvent e)
3982   {
3983     ParseProperties pp = new jalview.analysis.ParseProperties(viewport.alignment);
3984     // TODO: verify regex and introduce GUI dialog for version 2.5
3985     //if (pp.getScoresFromDescription("col", "score column ", "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)", true)>0)
3986     if (pp.getScoresFromDescription("description column", "score in description column ", "\\W*([-+eE0-9.]+)", true)>0)    {
3987       buildSortByAnnotationScoresMenu();
3988     }
3989   }
3990 }
3991
3992 class PrintThread
3993     extends Thread
3994 {
3995   AlignmentPanel ap;
3996   public PrintThread(AlignmentPanel ap)
3997   {
3998    this.ap = ap;
3999   }
4000   static PageFormat pf;
4001   public void run()
4002   {
4003     PrinterJob printJob = PrinterJob.getPrinterJob();
4004
4005     if (pf != null)
4006     {
4007       printJob.setPrintable(ap, pf);
4008     }
4009     else
4010     {
4011       printJob.setPrintable(ap);
4012     }
4013
4014     if (printJob.printDialog())
4015     {
4016       try
4017       {
4018         printJob.print();
4019       }
4020       catch (Exception PrintException)
4021       {
4022         PrintException.printStackTrace();
4023       }
4024     }
4025   }
4026 }