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