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