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