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