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