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