JAL-468 JAL-936 sort alignment by tree and autosort for new tree
[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           
3973           if (Cache.getDefault("SHOW_JWS1_SERVICES", true)
3974                   && Discoverer.services != null
3975                   && (Discoverer.services.size() > 0))
3976           {
3977             // TODO: refactor to allow list of AbstractName/Handler bindings to
3978             // be
3979             // stored or retrieved from elsewhere
3980             Vector msaws = (Vector) Discoverer.services.get("MsaWS");
3981             Vector secstrpr = (Vector) Discoverer.services
3982                     .get("SecStrPred");
3983             Vector seqsrch = (Vector) Discoverer.services.get("SeqSearch");
3984             // TODO: move GUI generation code onto service implementation - so a
3985             // client instance attaches itself to the GUI with method call like
3986             // jalview.ws.MsaWSClient.bind(servicehandle, Desktop.instance,
3987             // alignframe)
3988             if (msaws != null)
3989             {
3990               // Add any Multiple Sequence Alignment Services
3991               for (int i = 0, j = msaws.size(); i < j; i++)
3992               {
3993                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws
3994                         .get(i);
3995                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
3996                         .getServiceClient(sh);
3997                 impl.attachWSMenuEntry(msawsmenu, me);
3998
3999               }
4000             }
4001             if (secstrpr != null)
4002             {
4003               // Add any secondary structure prediction services
4004               for (int i = 0, j = secstrpr.size(); i < j; i++)
4005               {
4006                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4007                         .get(i);
4008                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4009                         .getServiceClient(sh);
4010                 impl.attachWSMenuEntry(secstrmenu, me);
4011               }
4012             }
4013             if (seqsrch != null)
4014             {
4015               // Add any sequence search services
4016               for (int i = 0, j = seqsrch.size(); i < j; i++)
4017               {
4018                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch
4019                         .elementAt(i);
4020                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4021                         .getServiceClient(sh);
4022                 impl.attachWSMenuEntry(seqsrchmenu, me);
4023               }
4024             }
4025           }
4026
4027           // TODO: move into separate menu builder class.
4028           if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4029           {
4030             Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4031             if (jws2servs != null)
4032             {
4033               if (jws2servs.hasServices())
4034               {
4035                 jws2servs.attachWSMenuEntry(msawsmenu, me);
4036               }
4037             }
4038           }
4039           // Add all submenus in the order they should appear on the web services menu
4040           wsmenu.add(msawsmenu);
4041           wsmenu.add(secstrmenu);
4042           wsmenu.add(analymenu);
4043           // No search services yet
4044           // wsmenu.add(seqsrchmenu);
4045
4046           javax.swing.SwingUtilities.invokeLater(new Runnable()
4047           {
4048             public void run()
4049             {
4050               try
4051               {
4052                 webService.removeAll();
4053                 // first, add discovered services onto the webservices menu
4054                 if (wsmenu.size() > 0)
4055                 {
4056                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4057                   {
4058                     webService.add((JMenu) wsmenu.get(i));
4059                   }
4060                 }
4061                 else
4062                 {
4063                   webService.add(me.webServiceNoServices);
4064                 }
4065                 build_urlServiceMenu(me.webService);
4066                 build_fetchdbmenu(webService);
4067               } catch (Exception e)
4068               {
4069               }
4070               ;
4071             }
4072           });
4073         } catch (Exception e)
4074         {
4075         }
4076         ;
4077
4078         buildingMenu = false;
4079       }
4080     }).start();
4081
4082   }
4083
4084
4085   /**
4086    * construct any groupURL type service menu entries.
4087    * 
4088    * @param webService
4089    */
4090   private void build_urlServiceMenu(JMenu webService)
4091   {
4092     // TODO: remove this code when 2.7 is released
4093     // DEBUG - alignmentView
4094     /*
4095      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4096      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4097      * 
4098      * @Override public void actionPerformed(ActionEvent e) {
4099      * jalview.datamodel.AlignmentView.testSelectionViews(af.viewport.alignment,
4100      * af.viewport.colSel, af.viewport.selectionGroup); }
4101      * 
4102      * }); webService.add(testAlView);
4103      */
4104     // TODO: refactor to RestClient discoverer and merge menu entries for
4105     // rest-style services with other types of analysis/calculation service
4106     // SHmmr test client - still being implemented.
4107     // DEBUG - alignmentView
4108     
4109     for (jalview.ws.rest.RestClient client: jalview.ws.rest.RestClient.getRestClients()) {
4110       client.attachWSMenuEntry(JvSwingUtils.findOrCreateMenu(webService, client.getAction()), this);
4111     }
4112
4113     if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
4114     {
4115       jalview.ws.EnfinEnvision2OneWay.getInstance().attachWSMenuEntry(
4116               webService, this);
4117     }
4118   }
4119
4120   /*
4121    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4122    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4123    * getProperty("LAST_DIRECTORY"));
4124    * 
4125    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4126    * to Vamsas file"); chooser.setToolTipText("Export");
4127    * 
4128    * int value = chooser.showSaveDialog(this);
4129    * 
4130    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4131    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4132    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4133    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4134    */
4135   /**
4136    * prototype of an automatically enabled/disabled analysis function
4137    * 
4138    */
4139   protected void setShowProductsEnabled()
4140   {
4141     SequenceI[] selection = viewport.getSequenceSelection();
4142     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4143             viewport.getAlignment().getDataset()))
4144     {
4145       showProducts.setEnabled(true);
4146
4147     }
4148     else
4149     {
4150       showProducts.setEnabled(false);
4151     }
4152   }
4153
4154   /**
4155    * search selection for sequence xRef products and build the show products
4156    * menu.
4157    * 
4158    * @param selection
4159    * @param dataset
4160    * @return true if showProducts menu should be enabled.
4161    */
4162   public boolean canShowProducts(SequenceI[] selection,
4163           boolean isRegionSelection, Alignment dataset)
4164   {
4165     boolean showp = false;
4166     try
4167     {
4168       showProducts.removeAll();
4169       final boolean dna = viewport.getAlignment().isNucleotide();
4170       final Alignment ds = dataset;
4171       String[] ptypes = (selection == null || selection.length == 0) ? null
4172               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4173       // Object[] prods =
4174       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4175       // selection, dataset, true);
4176       final SequenceI[] sel = selection;
4177       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4178       {
4179         showp = true;
4180         final boolean isRegSel = isRegionSelection;
4181         final AlignFrame af = this;
4182         final String source = ptypes[t];
4183         JMenuItem xtype = new JMenuItem(ptypes[t]);
4184         xtype.addActionListener(new ActionListener()
4185         {
4186
4187           public void actionPerformed(ActionEvent e)
4188           {
4189             // TODO: new thread for this call with vis-delay
4190             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4191                     isRegSel, dna, source);
4192           }
4193
4194         });
4195         showProducts.add(xtype);
4196       }
4197       showProducts.setVisible(showp);
4198       showProducts.setEnabled(showp);
4199     } catch (Exception e)
4200     {
4201       jalview.bin.Cache.log
4202               .warn("canTranslate threw an exception - please report to help@jalview.org",
4203                       e);
4204       return false;
4205     }
4206     return showp;
4207   }
4208
4209   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4210           boolean isRegSel, boolean dna, String source)
4211   {
4212     final boolean fisRegSel = isRegSel;
4213     final boolean fdna = dna;
4214     final String fsrc = source;
4215     final AlignFrame ths = this;
4216     final SequenceI[] fsel = sel;
4217     Runnable foo = new Runnable()
4218     {
4219
4220       public void run()
4221       {
4222         final long sttime = System.currentTimeMillis();
4223         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
4224         try
4225         {
4226           Alignment ds = ths.getViewport().alignment.getDataset(); // update
4227           // our local
4228           // dataset
4229           // reference
4230           Alignment prods = CrossRef
4231                   .findXrefSequences(fsel, fdna, fsrc, ds);
4232           if (prods != null)
4233           {
4234             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4235             for (int s = 0; s < sprods.length; s++)
4236             {
4237               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4238               if (ds.getSequences() == null
4239                       || !ds.getSequences().contains(
4240                               sprods[s].getDatasetSequence()))
4241                 ds.addSequence(sprods[s].getDatasetSequence());
4242               sprods[s].updatePDBIds();
4243             }
4244             Alignment al = new Alignment(sprods);
4245             AlignedCodonFrame[] cf = prods.getCodonFrames();
4246             al.setDataset(ds);
4247             for (int s = 0; cf != null && s < cf.length; s++)
4248             {
4249               al.addCodonFrame(cf[s]);
4250               cf[s] = null;
4251             }
4252             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4253                     DEFAULT_HEIGHT);
4254             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4255                     + " for " + ((fisRegSel) ? "selected region of " : "")
4256                     + getTitle();
4257             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4258                     DEFAULT_HEIGHT);
4259           }
4260           else
4261           {
4262             System.err.println("No Sequences generated for xRef type "
4263                     + fsrc);
4264           }
4265         } catch (Exception e)
4266         {
4267           jalview.bin.Cache.log.error(
4268                   "Exception when finding crossreferences", e);
4269         } catch (OutOfMemoryError e)
4270         {
4271           new OOMWarning("whilst fetching crossreferences", e);
4272         } catch (Error e)
4273         {
4274           jalview.bin.Cache.log.error("Error when finding crossreferences",
4275                   e);
4276         }
4277         ths.setProgressBar("Finished searching for sequences from " + fsrc,
4278                 sttime);
4279       }
4280
4281     };
4282     Thread frunner = new Thread(foo);
4283     frunner.start();
4284   }
4285
4286   public boolean canShowTranslationProducts(SequenceI[] selection,
4287           AlignmentI alignment)
4288   {
4289     // old way
4290     try
4291     {
4292       return (jalview.analysis.Dna.canTranslate(selection,
4293               viewport.getViewAsVisibleContigs(true)));
4294     } catch (Exception e)
4295     {
4296       jalview.bin.Cache.log
4297               .warn("canTranslate threw an exception - please report to help@jalview.org",
4298                       e);
4299       return false;
4300     }
4301   }
4302
4303   public void showProducts_actionPerformed(ActionEvent e)
4304   {
4305     // /////////////////////////////
4306     // Collect Data to be translated/transferred
4307
4308     SequenceI[] selection = viewport.getSequenceSelection();
4309     AlignmentI al = null;
4310     try
4311     {
4312       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4313               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4314               viewport.getAlignment().getDataset());
4315     } catch (Exception ex)
4316     {
4317       al = null;
4318       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4319     }
4320     if (al == null)
4321     {
4322       JOptionPane
4323               .showMessageDialog(
4324                       Desktop.desktop,
4325                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4326                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4327     }
4328     else
4329     {
4330       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4331       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4332               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4333     }
4334   }
4335
4336   public void showTranslation_actionPerformed(ActionEvent e)
4337   {
4338     // /////////////////////////////
4339     // Collect Data to be translated/transferred
4340
4341     SequenceI[] selection = viewport.getSequenceSelection();
4342     String[] seqstring = viewport.getViewAsString(true);
4343     AlignmentI al = null;
4344     try
4345     {
4346       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4347               viewport.getViewAsVisibleContigs(true), viewport
4348                       .getGapCharacter(), viewport.alignment
4349                       .getAlignmentAnnotation(), viewport.alignment
4350                       .getWidth(), viewport.getAlignment().getDataset());
4351     } catch (Exception ex)
4352     {
4353       al = null;
4354       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4355     }
4356     if (al == null)
4357     {
4358       JOptionPane
4359               .showMessageDialog(
4360                       Desktop.desktop,
4361                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4362                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4363     }
4364     else
4365     {
4366       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4367       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4368               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4369     }
4370   }
4371
4372   /**
4373    * Try to load a features file onto the alignment.
4374    * 
4375    * @param file
4376    *          contents or path to retrieve file
4377    * @param type
4378    *          access mode of file (see jalview.io.AlignFile)
4379    * @return true if features file was parsed corectly.
4380    */
4381   public boolean parseFeaturesFile(String file, String type)
4382   {
4383     boolean featuresFile = false;
4384     try
4385     {
4386       featuresFile = new FeaturesFile(file, type)
4387               .parse(viewport.alignment.getDataset(),
4388                       alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureColours,
4389                       false, jalview.bin.Cache.getDefault(
4390                               "RELAXEDSEQIDMATCHING", false));
4391     } catch (Exception ex)
4392     {
4393       ex.printStackTrace();
4394     }
4395
4396     if (featuresFile)
4397     {
4398       viewport.showSequenceFeatures = true;
4399       showSeqFeatures.setSelected(true);
4400       if (alignPanel.seqPanel.seqCanvas.fr != null)
4401       {
4402         // update the min/max ranges where necessary
4403         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4404       }
4405       if (featureSettings != null)
4406       {
4407         featureSettings.setTableData();
4408       }
4409       alignPanel.paintAlignment(true);
4410     }
4411
4412     return featuresFile;
4413   }
4414
4415   public void dragEnter(DropTargetDragEvent evt)
4416   {
4417   }
4418
4419   public void dragExit(DropTargetEvent evt)
4420   {
4421   }
4422
4423   public void dragOver(DropTargetDragEvent evt)
4424   {
4425   }
4426
4427   public void dropActionChanged(DropTargetDragEvent evt)
4428   {
4429   }
4430
4431   public void drop(DropTargetDropEvent evt)
4432   {
4433     Transferable t = evt.getTransferable();
4434     java.util.List files = null;
4435
4436     try
4437     {
4438       DataFlavor uriListFlavor = new DataFlavor(
4439               "text/uri-list;class=java.lang.String");
4440       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4441       {
4442         // Works on Windows and MacOSX
4443         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4444         files = (java.util.List) t
4445                 .getTransferData(DataFlavor.javaFileListFlavor);
4446       }
4447       else if (t.isDataFlavorSupported(uriListFlavor))
4448       {
4449         // This is used by Unix drag system
4450         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4451         String data = (String) t.getTransferData(uriListFlavor);
4452         files = new java.util.ArrayList(1);
4453         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4454                 data, "\r\n"); st.hasMoreTokens();)
4455         {
4456           String s = st.nextToken();
4457           if (s.startsWith("#"))
4458           {
4459             // the line is a comment (as per the RFC 2483)
4460             continue;
4461           }
4462
4463           java.net.URI uri = new java.net.URI(s);
4464           // check to see if we can handle this kind of URI
4465           if (uri.getScheme().toLowerCase().startsWith("http"))
4466           {
4467             files.add(uri.toString());
4468           }
4469           else
4470           {
4471             // otherwise preserve old behaviour: catch all for file objects
4472             java.io.File file = new java.io.File(uri);
4473             files.add(file.toString());
4474           }
4475         }
4476       }
4477     } catch (Exception e)
4478     {
4479       e.printStackTrace();
4480     }
4481     if (files != null)
4482     {
4483       try
4484       {
4485         // check to see if any of these files have names matching sequences in
4486         // the alignment
4487         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4488                 .getAlignment().getSequencesArray());
4489         /**
4490          * Object[] { String,SequenceI}
4491          */
4492         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4493         ArrayList<String> filesnotmatched = new ArrayList<String>();
4494         for (int i = 0; i < files.size(); i++)
4495         {
4496           String file = files.get(i).toString();
4497           String pdbfn = "";
4498           String protocol = FormatAdapter.checkProtocol(file);
4499           if (protocol == jalview.io.FormatAdapter.FILE)
4500           {
4501             File fl = new File(file);
4502             pdbfn = fl.getName();
4503           }
4504           else if (protocol == jalview.io.FormatAdapter.URL)
4505           {
4506             URL url = new URL(file);
4507             pdbfn = url.getFile();
4508           }
4509           if (pdbfn.length() > 0)
4510           {
4511             // attempt to find a match in the alignment
4512             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
4513             int l = 0, c = pdbfn.indexOf(".");
4514             while (mtch == null && c != -1)
4515             {
4516               do 
4517               {
4518                 l = c;
4519               } while ((c = pdbfn.indexOf(".", l)) > l);
4520               if (l > -1)
4521               {
4522                 pdbfn = pdbfn.substring(0, l);
4523               }
4524               mtch = idm.findAllIdMatches(pdbfn);
4525             }
4526             if (mtch != null)
4527             {
4528               String type = null;
4529               try
4530               {
4531                 type = new IdentifyFile().Identify(file, protocol);
4532               } catch (Exception ex)
4533               {
4534                 type = null;
4535               }
4536               if (type != null)
4537               {
4538                 if (type.equalsIgnoreCase("PDB"))
4539                 {
4540                   filesmatched.add(new Object[]
4541                   { file, protocol, mtch });
4542                   continue;
4543                 }
4544               }
4545             }
4546             // File wasn't named like one of the sequences or wasn't a PDB file.
4547             filesnotmatched.add(file);
4548           }
4549         }
4550         int assocfiles = 0;
4551         if (filesmatched.size() > 0)
4552         {
4553           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
4554                   || JOptionPane
4555                           .showConfirmDialog(
4556                                   this,
4557                                   "Do you want to automatically associate the "
4558                                           + filesmatched.size()
4559                                           + " PDB files with sequences in the alignment that have the same name ?",
4560                                   "Automatically Associate PDB files by name",
4561                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
4562
4563           {
4564             for (Object[] fm : filesmatched)
4565             {
4566               // try and associate
4567               // TODO: may want to set a standard ID naming formalism for
4568               // associating PDB files which have no IDs.
4569               for (SequenceI toassoc: (SequenceI[])fm[2]) {
4570               PDBEntry pe = new AssociatePdbFileWithSeq()
4571                       .associatePdbWithSeq((String) fm[0], (String) fm[1],
4572                               toassoc, false);
4573               if (pe != null)
4574               {
4575                 System.err
4576                         .println("Associated file : " + ((String) fm[0])
4577                                 + " with "
4578                                 + toassoc.getDisplayId(true));
4579                 assocfiles++;
4580               }
4581               }
4582               alignPanel.paintAlignment(true);
4583             }
4584           }
4585         }
4586         if (filesnotmatched.size() > 0)
4587         {
4588           if (assocfiles > 0
4589                   && (Cache.getDefault(
4590                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
4591                           .showConfirmDialog(
4592                                   this,
4593                                   "<html>Do you want to <em>ignore</em> the "
4594                                           + filesnotmatched.size()
4595                                           + " files whose names did not match any sequence IDs ?</html>",
4596                                   "Ignore unmatched dropped files ?",
4597                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
4598           {
4599             return;
4600           }
4601           for (String fn : filesnotmatched)
4602           {
4603             loadJalviewDataFile(fn, null, null, null);
4604           }
4605
4606         }
4607       } catch (Exception ex)
4608       {
4609         ex.printStackTrace();
4610       }
4611     }
4612   }
4613
4614   /**
4615    * Attempt to load a "dropped" file or URL string: First by testing whether
4616    * it's and Annotation file, then a JNet file, and finally a features file. If
4617    * all are false then the user may have dropped an alignment file onto this
4618    * AlignFrame.
4619    * 
4620    * @param file
4621    *          either a filename or a URL string.
4622    */
4623   public void loadJalviewDataFile(String file, String protocol,
4624           String format, SequenceI assocSeq)
4625   {
4626     try
4627     {
4628       if (protocol == null)
4629       {
4630         protocol = jalview.io.FormatAdapter.checkProtocol(file);
4631       }
4632       // if the file isn't identified, or not positively identified as some
4633       // other filetype (PFAM is default unidentified alignment file type) then
4634       // try to parse as annotation.
4635       boolean isAnnotation = (format == null || format
4636               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
4637               .readAnnotationFile(viewport.alignment, file, protocol)
4638               : false;
4639
4640       if (!isAnnotation)
4641       {
4642         // try to see if its a JNet 'concise' style annotation file *before* we
4643         // try to parse it as a features file
4644         if (format == null)
4645         {
4646           format = new IdentifyFile().Identify(file, protocol);
4647         }
4648         if (format.equalsIgnoreCase("JnetFile"))
4649         {
4650           jalview.io.JPredFile predictions = new jalview.io.JPredFile(file,
4651                   protocol);
4652           new JnetAnnotationMaker().add_annotation(predictions,
4653                   viewport.getAlignment(), 0, false);
4654           isAnnotation = true;
4655         }
4656         else
4657         {
4658           /*
4659            * if (format.equalsIgnoreCase("PDB")) {
4660            * 
4661            * String pdbfn = ""; // try to match up filename with sequence id try
4662            * { if (protocol == jalview.io.FormatAdapter.FILE) { File fl = new
4663            * File(file); pdbfn = fl.getName(); } else if (protocol ==
4664            * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
4665            * url.getFile(); } } catch (Exception e) { } ; if (assocSeq == null)
4666            * { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4667            * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) { //
4668            * attempt to find a match in the alignment SequenceI mtch =
4669            * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
4670            * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) > l)
4671            * { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch =
4672            * idm.findIdMatch(pdbfn); } if (mtch != null) { // try and associate
4673            * // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
4674            * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null) {
4675            * System.err.println("Associated file : " + file + " with " +
4676            * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
4677            * TODO: maybe need to load as normal otherwise return; } }
4678            */
4679           // try to parse it as a features file
4680           boolean isGroupsFile = parseFeaturesFile(file, protocol);
4681           // if it wasn't a features file then we just treat it as a general
4682           // alignment file to load into the current view.
4683           if (!isGroupsFile)
4684           {
4685             new FileLoader().LoadFile(viewport, file, protocol, format);
4686           }
4687           else
4688           {
4689             alignPanel.paintAlignment(true);
4690           }
4691         }
4692       }
4693       if (isAnnotation)
4694       {
4695
4696         alignPanel.adjustAnnotationHeight();
4697         viewport.updateSequenceIdColours();
4698         buildSortByAnnotationScoresMenu();
4699         alignPanel.paintAlignment(true);
4700       }
4701     } catch (Exception ex)
4702     {
4703       ex.printStackTrace();
4704     }
4705   }
4706
4707   public void tabSelectionChanged(int index)
4708   {
4709     if (index > -1)
4710     {
4711       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
4712       viewport = alignPanel.av;
4713       setMenusFromViewport(viewport);
4714     }
4715   }
4716
4717   public void tabbedPane_mousePressed(MouseEvent e)
4718   {
4719     if (SwingUtilities.isRightMouseButton(e))
4720     {
4721       String reply = JOptionPane.showInternalInputDialog(this,
4722               "Enter View Name", "Edit View Name",
4723               JOptionPane.QUESTION_MESSAGE);
4724
4725       if (reply != null)
4726       {
4727         viewport.viewName = reply;
4728         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
4729       }
4730     }
4731   }
4732
4733   public AlignViewport getCurrentView()
4734   {
4735     return viewport;
4736   }
4737
4738   /**
4739    * Open the dialog for regex description parsing.
4740    */
4741   protected void extractScores_actionPerformed(ActionEvent e)
4742   {
4743     ParseProperties pp = new jalview.analysis.ParseProperties(
4744             viewport.alignment);
4745     // TODO: verify regex and introduce GUI dialog for version 2.5
4746     // if (pp.getScoresFromDescription("col", "score column ",
4747     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
4748     // true)>0)
4749     if (pp.getScoresFromDescription("description column",
4750             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
4751     {
4752       buildSortByAnnotationScoresMenu();
4753     }
4754   }
4755
4756   /*
4757    * (non-Javadoc)
4758    * 
4759    * @see
4760    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
4761    * )
4762    */
4763   protected void showDbRefs_actionPerformed(ActionEvent e)
4764   {
4765     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
4766   }
4767
4768   /*
4769    * (non-Javadoc)
4770    * 
4771    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
4772    * ActionEvent)
4773    */
4774   protected void showNpFeats_actionPerformed(ActionEvent e)
4775   {
4776     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
4777   }
4778
4779   /**
4780    * find the viewport amongst the tabs in this alignment frame and close that
4781    * tab
4782    * 
4783    * @param av
4784    */
4785   public boolean closeView(AlignViewport av)
4786   {
4787     if (viewport == av)
4788     {
4789       this.closeMenuItem_actionPerformed(false);
4790       return true;
4791     }
4792     Component[] comp = tabbedPane.getComponents();
4793     for (int i = 0; comp != null && i < comp.length; i++)
4794     {
4795       if (comp[i] instanceof AlignmentPanel)
4796       {
4797         if (((AlignmentPanel) comp[i]).av == av)
4798         {
4799           // close the view.
4800           closeView((AlignmentPanel) comp[i]);
4801           return true;
4802         }
4803       }
4804     }
4805     return false;
4806   }
4807
4808   protected void build_fetchdbmenu(JMenu webService)
4809   {
4810     // Temporary hack - DBRef Fetcher always top level ws entry.
4811     // TODO We probably want to store a sequence database checklist in
4812     // preferences and have checkboxes.. rather than individual sources selected
4813     // here
4814     final JMenu rfetch = new JMenu("Fetch DB References");
4815     rfetch.setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
4816     webService.add(rfetch);
4817
4818     JMenuItem fetchr = new JMenuItem("Standard Databases");
4819     fetchr.setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
4820     fetchr.addActionListener(new ActionListener()
4821     {
4822
4823       public void actionPerformed(ActionEvent e)
4824       {
4825         new Thread(new Runnable()
4826         {
4827
4828           public void run()
4829           {
4830             new jalview.ws.DBRefFetcher(alignPanel.av
4831                     .getSequenceSelection(), alignPanel.alignFrame)
4832                     .fetchDBRefs(false);
4833           }
4834         }).start();
4835
4836       }
4837
4838     });
4839     rfetch.add(fetchr);
4840     final AlignFrame me = this;
4841     new Thread(new Runnable()
4842     {
4843       public void run()
4844       {
4845         final jalview.ws.SequenceFetcher sf = SequenceFetcher
4846                 .getSequenceFetcherSingleton(me);
4847         final String[] otherdb = sf.getOrderedSupportedSources();
4848         // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
4849         // jalview.util.QuickSort.sort(otherdb, otherdb);
4850         javax.swing.SwingUtilities.invokeLater(new Runnable()
4851         {
4852           public void run()
4853           {
4854
4855             JMenu dfetch = new JMenu();
4856             JMenuItem fetchr;
4857             rfetch.add(dfetch);
4858             int comp = 0, mcomp = 15;
4859             String mname = null;
4860             if (otherdb != null && otherdb.length > 0)
4861             {
4862               for (int i = 0; i < otherdb.length; i++)
4863               {
4864                 String dbname = sf.getSourceProxy(otherdb[i]).getDbName();
4865                 if (mname == null)
4866                 {
4867                   mname = "from '" + dbname + "'";
4868                 }
4869                 fetchr = new JMenuItem(otherdb[i]);
4870                 final String[] dassource = new String[]
4871                 { otherdb[i] };
4872                 fetchr.addActionListener(new ActionListener()
4873                 {
4874
4875                   public void actionPerformed(ActionEvent e)
4876                   {
4877                     new Thread(new Runnable()
4878                     {
4879
4880                       public void run()
4881                       {
4882                         new jalview.ws.DBRefFetcher(alignPanel.av
4883                                 .getSequenceSelection(),
4884                                 alignPanel.alignFrame, dassource)
4885                                 .fetchDBRefs(false);
4886                       }
4887                     }).start();
4888                   }
4889
4890                 });
4891                 fetchr.setToolTipText("Retrieve from " + dbname);
4892                 dfetch.add(fetchr);
4893                 if (comp++ == mcomp || i == (otherdb.length - 1))
4894                 {
4895                   dfetch.setText(mname + " to '" + dbname + "'");
4896                   rfetch.add(dfetch);
4897                   dfetch = new JMenu();
4898                   mname = null;
4899                   comp = 0;
4900                 }
4901               }
4902             }
4903           }
4904         });
4905       }
4906     }).start();
4907
4908   }
4909
4910   /**
4911    * Left justify the whole alignment.
4912    */
4913   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
4914   {
4915     AlignmentI al = viewport.getAlignment();
4916     al.justify(false);
4917     viewport.firePropertyChange("alignment", null, al);
4918   }
4919
4920   /**
4921    * Right justify the whole alignment.
4922    */
4923   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
4924   {
4925     AlignmentI al = viewport.getAlignment();
4926     al.justify(true);
4927     viewport.firePropertyChange("alignment", null, al);
4928   }
4929
4930   public void setShowSeqFeatures(boolean b)
4931   {
4932     showSeqFeatures.setSelected(true);
4933     viewport.setShowSequenceFeatures(true);
4934   }
4935
4936   /*
4937    * (non-Javadoc)
4938    * 
4939    * @see
4940    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
4941    * awt.event.ActionEvent)
4942    */
4943   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
4944   {
4945     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
4946     alignPanel.paintAlignment(true);
4947   }
4948
4949   /*
4950    * (non-Javadoc)
4951    * 
4952    * @see
4953    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
4954    * .ActionEvent)
4955    */
4956   protected void showGroupConsensus_actionPerformed(ActionEvent e)
4957   {
4958     viewport.setShowGroupConsensus(showGroupConsensus.getState());
4959     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4960
4961   }
4962
4963   /*
4964    * (non-Javadoc)
4965    * 
4966    * @see
4967    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
4968    * .event.ActionEvent)
4969    */
4970   protected void showGroupConservation_actionPerformed(ActionEvent e)
4971   {
4972     viewport.setShowGroupConservation(showGroupConservation.getState());
4973     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4974   }
4975
4976   /*
4977    * (non-Javadoc)
4978    * 
4979    * @see
4980    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
4981    * .event.ActionEvent)
4982    */
4983   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
4984   {
4985     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
4986     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4987   }
4988
4989   /*
4990    * (non-Javadoc)
4991    * 
4992    * @see
4993    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
4994    * .event.ActionEvent)
4995    */
4996   protected void showSequenceLogo_actionPerformed(ActionEvent e)
4997   {
4998     viewport.setShowSequenceLogo(showSequenceLogo.getState());
4999     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5000   }
5001
5002   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5003   {
5004     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5005   }
5006
5007   /*
5008    * (non-Javadoc)
5009    * 
5010    * @see
5011    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5012    * .event.ActionEvent)
5013    */
5014   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5015   {
5016     if (viewport.getSelectionGroup() != null)
5017     {
5018       SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
5019               viewport.getSequenceSelection(),
5020               viewport.getAlignmentView(true).getSequenceStrings(
5021                       viewport.getGapCharacter()),
5022               viewport.alignment.getGroups());
5023       viewport.alignment.deleteAllGroups();
5024       viewport.sequenceColours = null;
5025       viewport.setSelectionGroup(null);
5026       // set view properties for each group
5027       for (int g = 0; g < gps.length; g++)
5028       {
5029         gps[g].setShowNonconserved(viewport.getShowUnconserved());
5030         gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
5031         viewport.alignment.addGroup(gps[g]);
5032         Color col = new Color((int) (Math.random() * 255),
5033                 (int) (Math.random() * 255), (int) (Math.random() * 255));
5034         col = col.brighter();
5035         for (Enumeration sq = gps[g].getSequences(null).elements(); sq
5036                 .hasMoreElements(); viewport.setSequenceColour(
5037                 (SequenceI) sq.nextElement(), col))
5038           ;
5039       }
5040       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5041       alignPanel.updateAnnotation();
5042       alignPanel.paintAlignment(true);
5043     }
5044   }
5045
5046   /**
5047    * make the given alignmentPanel the currently selected tab
5048    * 
5049    * @param alignmentPanel
5050    */
5051   public void setDisplayedView(AlignmentPanel alignmentPanel)
5052   {
5053     if (!viewport.getSequenceSetId().equals(
5054             alignmentPanel.av.getSequenceSetId()))
5055     {
5056       throw new Error(
5057               "Implementation error: cannot show a view from another alignment in an AlignFrame.");
5058     }
5059     if (tabbedPane != null
5060             & alignPanels.indexOf(alignmentPanel) != tabbedPane.getSelectedIndex())
5061     {
5062       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5063     }
5064   }
5065 }
5066
5067 class PrintThread extends Thread
5068 {
5069   AlignmentPanel ap;
5070
5071   public PrintThread(AlignmentPanel ap)
5072   {
5073     this.ap = ap;
5074   }
5075
5076   static PageFormat pf;
5077
5078   public void run()
5079   {
5080     PrinterJob printJob = PrinterJob.getPrinterJob();
5081
5082     if (pf != null)
5083     {
5084       printJob.setPrintable(ap, pf);
5085     }
5086     else
5087     {
5088       printJob.setPrintable(ap);
5089     }
5090
5091     if (printJob.printDialog())
5092     {
5093       try
5094       {
5095         printJob.print();
5096       } catch (Exception PrintException)
5097       {
5098         PrintException.printStackTrace();
5099       }
5100     }
5101   }
5102 }