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