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