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