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