profile visualizations are now logos
[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     showSequenceLogo.setSelected(av.showSequenceLogo);
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         wsmenu.add(seqsrchmenu);
3782       }
3783       // finally, add the whole shebang onto the webservices menu
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     build_urlServiceMenu(webService);
3807   }
3808
3809   /**
3810    * construct any groupURL type service menu entries.
3811    * @param webService
3812    */
3813   private void build_urlServiceMenu(JMenu webService)
3814   {
3815     jalview.ws.EnfinEnvision2OneWay.getInstance().attachWSMenuEntry(webService, this);
3816   }
3817
3818   /*
3819    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
3820    * chooser = new JalviewFileChooser(jalview.bin.Cache.
3821    * getProperty("LAST_DIRECTORY"));
3822    * 
3823    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
3824    * to Vamsas file"); chooser.setToolTipText("Export");
3825    * 
3826    * int value = chooser.showSaveDialog(this);
3827    * 
3828    * if (value == JalviewFileChooser.APPROVE_OPTION) {
3829    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
3830    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
3831    * chooser.getSelectedFile().getAbsolutePath(), this); } }
3832    */
3833   /**
3834    * prototype of an automatically enabled/disabled analysis function
3835    * 
3836    */
3837   protected void setShowProductsEnabled()
3838   {
3839     SequenceI[] selection = viewport.getSequenceSelection();
3840     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
3841             viewport.getAlignment().getDataset()))
3842     {
3843       showProducts.setEnabled(true);
3844
3845     }
3846     else
3847     {
3848       showProducts.setEnabled(false);
3849     }
3850   }
3851
3852   /**
3853    * search selection for sequence xRef products and build the show products
3854    * menu.
3855    * 
3856    * @param selection
3857    * @param dataset
3858    * @return true if showProducts menu should be enabled.
3859    */
3860   public boolean canShowProducts(SequenceI[] selection,
3861           boolean isRegionSelection, Alignment dataset)
3862   {
3863     boolean showp = false;
3864     try
3865     {
3866       showProducts.removeAll();
3867       final boolean dna = viewport.getAlignment().isNucleotide();
3868       final Alignment ds = dataset;
3869       String[] ptypes = (selection == null || selection.length == 0) ? null
3870               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
3871       // Object[] prods =
3872       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
3873       // selection, dataset, true);
3874       final SequenceI[] sel = selection;
3875       for (int t = 0; ptypes != null && t < ptypes.length; t++)
3876       {
3877         showp = true;
3878         final boolean isRegSel = isRegionSelection;
3879         final AlignFrame af = this;
3880         final String source = ptypes[t];
3881         JMenuItem xtype = new JMenuItem(ptypes[t]);
3882         xtype.addActionListener(new ActionListener()
3883         {
3884
3885           public void actionPerformed(ActionEvent e)
3886           {
3887             // TODO: new thread for this call with vis-delay
3888             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
3889                     isRegSel, dna, source);
3890           }
3891
3892         });
3893         showProducts.add(xtype);
3894       }
3895       showProducts.setVisible(showp);
3896       showProducts.setEnabled(showp);
3897     } catch (Exception e)
3898     {
3899       jalview.bin.Cache.log
3900               .warn(
3901                       "canTranslate threw an exception - please report to help@jalview.org",
3902                       e);
3903       return false;
3904     }
3905     return showp;
3906   }
3907
3908   protected void showProductsFor(SequenceI[] sel, Alignment ds,
3909           boolean isRegSel, boolean dna, String source)
3910   {
3911     final boolean fisRegSel = isRegSel;
3912     final boolean fdna = dna;
3913     final String fsrc = source;
3914     final AlignFrame ths = this;
3915     final SequenceI[] fsel = sel;
3916     Runnable foo = new Runnable()
3917     {
3918
3919       public void run()
3920       {
3921         final long sttime = System.currentTimeMillis();
3922         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
3923         try
3924         {
3925           Alignment ds = ths.getViewport().alignment.getDataset(); // update
3926           // our local
3927           // dataset
3928           // reference
3929           Alignment prods = CrossRef
3930                   .findXrefSequences(fsel, fdna, fsrc, ds);
3931           if (prods != null)
3932           {
3933             SequenceI[] sprods = new SequenceI[prods.getHeight()];
3934             for (int s = 0; s < sprods.length; s++)
3935             {
3936               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
3937               if (ds.getSequences() == null
3938                       || !ds.getSequences().contains(
3939                               sprods[s].getDatasetSequence()))
3940                 ds.addSequence(sprods[s].getDatasetSequence());
3941               sprods[s].updatePDBIds();
3942             }
3943             Alignment al = new Alignment(sprods);
3944             AlignedCodonFrame[] cf = prods.getCodonFrames();
3945             al.setDataset(ds);
3946             for (int s = 0; cf != null && s < cf.length; s++)
3947             {
3948               al.addCodonFrame(cf[s]);
3949               cf[s] = null;
3950             }
3951             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
3952                     DEFAULT_HEIGHT);
3953             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
3954                     + " for " + ((fisRegSel) ? "selected region of " : "")
3955                     + getTitle();
3956             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
3957                     DEFAULT_HEIGHT);
3958           }
3959           else
3960           {
3961             System.err.println("No Sequences generated for xRef type "
3962                     + fsrc);
3963           }
3964         } catch (Exception e)
3965         {
3966           jalview.bin.Cache.log.error(
3967                   "Exception when finding crossreferences", e);
3968         } catch (OutOfMemoryError e)
3969         {
3970           new OOMWarning("whilst fetching crossreferences", e);
3971         } catch (Error e)
3972         {
3973           jalview.bin.Cache.log.error("Error when finding crossreferences",
3974                   e);
3975         }
3976         ths.setProgressBar("Finished searching for sequences from " + fsrc,
3977                 sttime);
3978       }
3979
3980     };
3981     Thread frunner = new Thread(foo);
3982     frunner.start();
3983   }
3984
3985   public boolean canShowTranslationProducts(SequenceI[] selection,
3986           AlignmentI alignment)
3987   {
3988     // old way
3989     try
3990     {
3991       return (jalview.analysis.Dna.canTranslate(selection, viewport
3992               .getViewAsVisibleContigs(true)));
3993     } catch (Exception e)
3994     {
3995       jalview.bin.Cache.log
3996               .warn(
3997                       "canTranslate threw an exception - please report to help@jalview.org",
3998                       e);
3999       return false;
4000     }
4001   }
4002
4003   public void showProducts_actionPerformed(ActionEvent e)
4004   {
4005     // /////////////////////////////
4006     // Collect Data to be translated/transferred
4007
4008     SequenceI[] selection = viewport.getSequenceSelection();
4009     AlignmentI al = null;
4010     try
4011     {
4012       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4013               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4014               viewport.getAlignment().getDataset());
4015     } catch (Exception ex)
4016     {
4017       al = null;
4018       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4019     }
4020     if (al == null)
4021     {
4022       JOptionPane
4023               .showMessageDialog(
4024                       Desktop.desktop,
4025                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4026                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4027     }
4028     else
4029     {
4030       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4031       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4032               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4033     }
4034   }
4035
4036   public void showTranslation_actionPerformed(ActionEvent e)
4037   {
4038     // /////////////////////////////
4039     // Collect Data to be translated/transferred
4040
4041     SequenceI[] selection = viewport.getSequenceSelection();
4042     String[] seqstring = viewport.getViewAsString(true);
4043     AlignmentI al = null;
4044     try
4045     {
4046       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4047               viewport.getViewAsVisibleContigs(true), viewport
4048                       .getGapCharacter(), viewport.alignment
4049                       .getAlignmentAnnotation(), viewport.alignment
4050                       .getWidth(), viewport.getAlignment().getDataset());
4051     } catch (Exception ex)
4052     {
4053       al = null;
4054       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4055     }
4056     if (al == null)
4057     {
4058       JOptionPane
4059               .showMessageDialog(
4060                       Desktop.desktop,
4061                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4062                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4063     }
4064     else
4065     {
4066       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4067       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4068               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4069     }
4070   }
4071
4072   /**
4073    * Try to load a features file onto the alignment.
4074    * 
4075    * @param file
4076    *          contents or path to retrieve file
4077    * @param type
4078    *          access mode of file (see jalview.io.AlignFile)
4079    * @return true if features file was parsed corectly.
4080    */
4081   public boolean parseFeaturesFile(String file, String type)
4082   {
4083     boolean featuresFile = false;
4084     try
4085     {
4086       featuresFile = new FeaturesFile(file, type).parse(viewport.alignment
4087               .getDataset(), alignPanel.seqPanel.seqCanvas
4088               .getFeatureRenderer().featureColours, false);
4089     } catch (Exception ex)
4090     {
4091       ex.printStackTrace();
4092     }
4093
4094     if (featuresFile)
4095     {
4096       viewport.showSequenceFeatures = true;
4097       showSeqFeatures.setSelected(true);
4098       if (alignPanel.seqPanel.seqCanvas.fr != null)
4099       {
4100         // update the min/max ranges where necessary
4101         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4102       }
4103       alignPanel.paintAlignment(true);
4104     }
4105
4106     return featuresFile;
4107   }
4108
4109   public void dragEnter(DropTargetDragEvent evt)
4110   {
4111   }
4112
4113   public void dragExit(DropTargetEvent evt)
4114   {
4115   }
4116
4117   public void dragOver(DropTargetDragEvent evt)
4118   {
4119   }
4120
4121   public void dropActionChanged(DropTargetDragEvent evt)
4122   {
4123   }
4124
4125   public void drop(DropTargetDropEvent evt)
4126   {
4127     Transferable t = evt.getTransferable();
4128     java.util.List files = null;
4129
4130     try
4131     {
4132       DataFlavor uriListFlavor = new DataFlavor(
4133               "text/uri-list;class=java.lang.String");
4134       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4135       {
4136         // Works on Windows and MacOSX
4137         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4138         files = (java.util.List) t
4139                 .getTransferData(DataFlavor.javaFileListFlavor);
4140       }
4141       else if (t.isDataFlavorSupported(uriListFlavor))
4142       {
4143         // This is used by Unix drag system
4144         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4145         String data = (String) t.getTransferData(uriListFlavor);
4146         files = new java.util.ArrayList(1);
4147         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4148                 data, "\r\n"); st.hasMoreTokens();)
4149         {
4150           String s = st.nextToken();
4151           if (s.startsWith("#"))
4152           {
4153             // the line is a comment (as per the RFC 2483)
4154             continue;
4155           }
4156
4157           java.net.URI uri = new java.net.URI(s);
4158           // check to see if we can handle this kind of URI
4159           if (uri.getScheme().toLowerCase().startsWith("http"))
4160           {
4161             files.add(uri.toString());
4162           }
4163           else
4164           {
4165             // otherwise preserve old behaviour: catch all for file objects
4166             java.io.File file = new java.io.File(uri);
4167             files.add(file.toString());
4168           }
4169         }
4170       }
4171     } catch (Exception e)
4172     {
4173       e.printStackTrace();
4174     }
4175     if (files != null)
4176     {
4177       try
4178       {
4179
4180         for (int i = 0; i < files.size(); i++)
4181         {
4182           loadJalviewDataFile(files.get(i).toString());
4183         }
4184       } catch (Exception ex)
4185       {
4186         ex.printStackTrace();
4187       }
4188     }
4189   }
4190
4191   /**
4192    * Attempt to load a "dropped" file or URL string: First by testing whether
4193    * it's and Annotation file, then a JNet file, and finally a features file. If
4194    * all are false then the user may have dropped an alignment file onto this
4195    * AlignFrame.
4196    * 
4197    * @param file
4198    *          either a filename or a URL string.
4199    */
4200   public void loadJalviewDataFile(String file)
4201   {
4202     try
4203     {
4204       String protocol = jalview.io.FormatAdapter.FILE;
4205       String f = file.toLowerCase();
4206       if (f.indexOf("http:") == 0 || f.indexOf("https:") == 0
4207               || f.indexOf("file:") == 0)
4208       {
4209         protocol = jalview.io.FormatAdapter.URL;
4210       }
4211
4212       boolean isAnnotation = new AnnotationFile().readAnnotationFile(
4213               viewport.alignment, file, protocol);
4214
4215       if (!isAnnotation)
4216       {
4217         // try to see if its a JNet 'concise' style annotation file *before* we
4218         // try to parse it as a features file
4219         String format = new IdentifyFile().Identify(file, protocol);
4220         if (format.equalsIgnoreCase("JnetFile"))
4221         {
4222           jalview.io.JPredFile predictions = new jalview.io.JPredFile(file,
4223                   protocol);
4224           new JnetAnnotationMaker().add_annotation(predictions, viewport
4225                   .getAlignment(), 0, false);
4226           isAnnotation = true;
4227         }
4228         else
4229         {
4230           // try to parse it as a features file
4231           boolean isGroupsFile = parseFeaturesFile(file, protocol);
4232           // if it wasn't a features file then we just treat it as a general
4233           // alignment file to load into the current view.
4234           if (!isGroupsFile)
4235           {
4236             new FileLoader().LoadFile(viewport, file, protocol, format);
4237           }
4238           else
4239           {
4240             alignPanel.paintAlignment(true);
4241           }
4242         }
4243       }
4244       if (isAnnotation)
4245       {
4246
4247         alignPanel.adjustAnnotationHeight();
4248         viewport.updateSequenceIdColours();
4249         buildSortByAnnotationScoresMenu();
4250         alignPanel.paintAlignment(true);
4251       }
4252     } catch (Exception ex)
4253     {
4254       ex.printStackTrace();
4255     }
4256   }
4257
4258   public void tabSelectionChanged(int index)
4259   {
4260     if (index > -1)
4261     {
4262       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
4263       viewport = alignPanel.av;
4264       setMenusFromViewport(viewport);
4265     }
4266   }
4267
4268   public void tabbedPane_mousePressed(MouseEvent e)
4269   {
4270     if (SwingUtilities.isRightMouseButton(e))
4271     {
4272       String reply = JOptionPane.showInternalInputDialog(this,
4273               "Enter View Name", "Edit View Name",
4274               JOptionPane.QUESTION_MESSAGE);
4275
4276       if (reply != null)
4277       {
4278         viewport.viewName = reply;
4279         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
4280       }
4281     }
4282   }
4283
4284   public AlignViewport getCurrentView()
4285   {
4286     return viewport;
4287   }
4288
4289   /**
4290    * Open the dialog for regex description parsing.
4291    */
4292   protected void extractScores_actionPerformed(ActionEvent e)
4293   {
4294     ParseProperties pp = new jalview.analysis.ParseProperties(
4295             viewport.alignment);
4296     // TODO: verify regex and introduce GUI dialog for version 2.5
4297     // if (pp.getScoresFromDescription("col", "score column ",
4298     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
4299     // true)>0)
4300     if (pp.getScoresFromDescription("description column",
4301             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
4302     {
4303       buildSortByAnnotationScoresMenu();
4304     }
4305   }
4306
4307   /*
4308    * (non-Javadoc)
4309    * 
4310    * @see
4311    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
4312    * )
4313    */
4314   protected void showDbRefs_actionPerformed(ActionEvent e)
4315   {
4316     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
4317   }
4318
4319   /*
4320    * (non-Javadoc)
4321    * 
4322    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
4323    * ActionEvent)
4324    */
4325   protected void showNpFeats_actionPerformed(ActionEvent e)
4326   {
4327     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
4328   }
4329
4330   /**
4331    * find the viewport amongst the tabs in this alignment frame and close that
4332    * tab
4333    * 
4334    * @param av
4335    */
4336   public boolean closeView(AlignViewport av)
4337   {
4338     if (viewport == av)
4339     {
4340       this.closeMenuItem_actionPerformed(false);
4341       return true;
4342     }
4343     Component[] comp = tabbedPane.getComponents();
4344     for (int i = 0; comp != null && i < comp.length; i++)
4345     {
4346       if (comp[i] instanceof AlignmentPanel)
4347       {
4348         if (((AlignmentPanel) comp[i]).av == av)
4349         {
4350           // close the view.
4351           closeView((AlignmentPanel) comp[i]);
4352           return true;
4353         }
4354       }
4355     }
4356     return false;
4357   }
4358
4359   protected void build_fetchdbmenu(JMenu webService)
4360   {
4361     // Temporary hack - DBRef Fetcher always top level ws entry.
4362     // TODO We probably want to store a sequence database checklist in
4363     // preferences and have checkboxes.. rather than individual sources selected
4364     // here
4365     JMenu rfetch = new JMenu("Fetch DB References");
4366     rfetch
4367             .setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
4368     webService.add(rfetch);
4369
4370     JMenuItem fetchr = new JMenuItem("Standard Databases");
4371     fetchr
4372             .setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
4373     fetchr.addActionListener(new ActionListener()
4374     {
4375
4376       public void actionPerformed(ActionEvent e)
4377       {
4378         new Thread(new Runnable()
4379         {
4380
4381           public void run()
4382           {
4383             new jalview.ws.DBRefFetcher(alignPanel.av
4384                     .getSequenceSelection(), alignPanel.alignFrame)
4385                     .fetchDBRefs(false);
4386           }
4387         }).start();
4388
4389       }
4390
4391     });
4392     rfetch.add(fetchr);
4393     JMenu dfetch = new JMenu();
4394     rfetch.add(dfetch);
4395     jalview.ws.SequenceFetcher sf = SequenceFetcher
4396             .getSequenceFetcherSingleton(this);
4397     String[] otherdb = sf.getOrderedSupportedSources();
4398     // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
4399     // jalview.util.QuickSort.sort(otherdb, otherdb);
4400     int comp = 0, mcomp = 15;
4401     String mname = null;
4402     if (otherdb != null && otherdb.length > 0)
4403     {
4404       for (int i = 0; i < otherdb.length; i++)
4405       {
4406         String dbname = sf.getSourceProxy(otherdb[i]).getDbName();
4407         if (mname == null)
4408         {
4409           mname = "from '" + dbname + "'";
4410         }
4411         fetchr = new JMenuItem(otherdb[i]);
4412         final String[] dassource = new String[]
4413         { otherdb[i] };
4414         fetchr.addActionListener(new ActionListener()
4415         {
4416
4417           public void actionPerformed(ActionEvent e)
4418           {
4419             new Thread(new Runnable()
4420             {
4421
4422               public void run()
4423               {
4424                 new jalview.ws.DBRefFetcher(alignPanel.av
4425                         .getSequenceSelection(), alignPanel.alignFrame,
4426                         dassource).fetchDBRefs(false);
4427               }
4428             }).start();
4429           }
4430
4431         });
4432         fetchr.setToolTipText("Retrieve from " + dbname);
4433         dfetch.add(fetchr);
4434         if (comp++ == mcomp || i == (otherdb.length - 1))
4435         {
4436           dfetch.setText(mname + " to '" + dbname + "'");
4437           rfetch.add(dfetch);
4438           dfetch = new JMenu();
4439           mname = null;
4440           comp = 0;
4441         }
4442       }
4443     }
4444   }
4445
4446   /**
4447    * Left justify the whole alignment.
4448    */
4449   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
4450   {
4451     AlignmentI al = viewport.getAlignment();
4452     al.justify(false);
4453     viewport.firePropertyChange("alignment", null, al);
4454   }
4455
4456   /**
4457    * Right justify the whole alignment.
4458    */
4459   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
4460   {
4461     AlignmentI al = viewport.getAlignment();
4462     al.justify(true);
4463     viewport.firePropertyChange("alignment", null, al);
4464   }
4465
4466   public void setShowSeqFeatures(boolean b)
4467   {
4468     showSeqFeatures.setSelected(true);
4469     viewport.setShowSequenceFeatures(true);
4470   }
4471
4472   /*
4473    * (non-Javadoc)
4474    * 
4475    * @see
4476    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
4477    * awt.event.ActionEvent)
4478    */
4479   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
4480   {
4481     viewport.setShowUnconserved(showUnconservedMenuItem.getState());
4482     alignPanel.paintAlignment(true);
4483   }
4484
4485   /*
4486    * (non-Javadoc)
4487    * 
4488    * @see
4489    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
4490    * .ActionEvent)
4491    */
4492   protected void showGroupConsensus_actionPerformed(ActionEvent e)
4493   {
4494     viewport.setShowGroupConsensus(showGroupConsensus.getState());
4495     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4496
4497   }
4498
4499   /*
4500    * (non-Javadoc)
4501    * 
4502    * @see
4503    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
4504    * .event.ActionEvent)
4505    */
4506   protected void showGroupConservation_actionPerformed(ActionEvent e)
4507   {
4508     viewport.setShowGroupConservation(showGroupConservation.getState());
4509     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4510   }
4511
4512   /* (non-Javadoc)
4513    * @see jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt.event.ActionEvent)
4514    */
4515   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
4516   {
4517     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
4518     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4519   }
4520
4521   /* (non-Javadoc)
4522    * @see jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt.event.ActionEvent)
4523    */
4524   protected void showSequenceLogo_actionPerformed(ActionEvent e)
4525   {
4526     viewport.setShowSequenceLogo(showSequenceLogo.getState());
4527     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4528   }
4529   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
4530   {
4531     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4532   }
4533
4534   /*
4535    * (non-Javadoc)
4536    * 
4537    * @see
4538    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
4539    * .event.ActionEvent)
4540    */
4541   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
4542   {
4543     if (viewport.getSelectionGroup() != null)
4544     {
4545       SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
4546               viewport.getSequenceSelection(), viewport.getAlignmentView(
4547                       true).getSequenceStrings(viewport.getGapCharacter()),
4548               viewport.alignment.getGroups());
4549       viewport.alignment.deleteAllGroups();
4550       viewport.sequenceColours = null;
4551       viewport.setSelectionGroup(null);
4552       // set view properties for each group
4553       for (int g = 0; g < gps.length; g++)
4554       {
4555         gps[g].setShowunconserved(viewport.getShowUnconserved());
4556         gps[g].setIncludeAllConsSymbols(viewport.isIncludeAllConsensusSymbols());
4557         viewport.alignment.addGroup(gps[g]);
4558         Color col = new Color((int) (Math.random() * 255), (int) (Math
4559                 .random() * 255), (int) (Math.random() * 255));
4560         col = col.brighter();
4561         for (Enumeration sq = gps[g].getSequences(null).elements(); sq
4562                 .hasMoreElements(); viewport.setSequenceColour(
4563                 (SequenceI) sq.nextElement(), col))
4564           ;
4565       }
4566       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
4567       alignPanel.updateAnnotation();
4568       alignPanel.paintAlignment(true);
4569     }
4570   }
4571 }
4572
4573 class PrintThread extends Thread
4574 {
4575   AlignmentPanel ap;
4576
4577   public PrintThread(AlignmentPanel ap)
4578   {
4579     this.ap = ap;
4580   }
4581
4582   static PageFormat pf;
4583
4584   public void run()
4585   {
4586     PrinterJob printJob = PrinterJob.getPrinterJob();
4587
4588     if (pf != null)
4589     {
4590       printJob.setPrintable(ap, pf);
4591     }
4592     else
4593     {
4594       printJob.setPrintable(ap);
4595     }
4596
4597     if (printJob.printDialog())
4598     {
4599       try
4600       {
4601         printJob.print();
4602       } catch (Exception PrintException)
4603       {
4604         PrintException.printStackTrace();
4605       }
4606     }
4607   }
4608 }