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