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