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