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