refactored from PopupMenu (for JAL-860)
[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     viewport.sendSelection();
2083   }
2084
2085   /**
2086    * DOCUMENT ME!
2087    * 
2088    * @param e
2089    *          DOCUMENT ME!
2090    */
2091   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
2092   {
2093     SequenceGroup sg = viewport.getSelectionGroup();
2094
2095     if (sg == null)
2096     {
2097       selectAllSequenceMenuItem_actionPerformed(null);
2098
2099       return;
2100     }
2101
2102     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2103     {
2104       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
2105     }
2106
2107     alignPanel.paintAlignment(true);
2108     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2109     viewport.sendSelection();
2110   }
2111
2112   public void invertColSel_actionPerformed(ActionEvent e)
2113   {
2114     viewport.invertColumnSelection();
2115     alignPanel.paintAlignment(true);
2116     viewport.sendSelection();
2117   }
2118
2119   /**
2120    * DOCUMENT ME!
2121    * 
2122    * @param e
2123    *          DOCUMENT ME!
2124    */
2125   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
2126   {
2127     trimAlignment(true);
2128   }
2129
2130   /**
2131    * DOCUMENT ME!
2132    * 
2133    * @param e
2134    *          DOCUMENT ME!
2135    */
2136   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
2137   {
2138     trimAlignment(false);
2139   }
2140
2141   void trimAlignment(boolean trimLeft)
2142   {
2143     ColumnSelection colSel = viewport.getColumnSelection();
2144     int column;
2145
2146     if (colSel.size() > 0)
2147     {
2148       if (trimLeft)
2149       {
2150         column = colSel.getMin();
2151       }
2152       else
2153       {
2154         column = colSel.getMax();
2155       }
2156
2157       SequenceI[] seqs;
2158       if (viewport.getSelectionGroup() != null)
2159       {
2160         seqs = viewport.getSelectionGroup().getSequencesAsArray(
2161                 viewport.hiddenRepSequences);
2162       }
2163       else
2164       {
2165         seqs = viewport.alignment.getSequencesArray();
2166       }
2167
2168       TrimRegionCommand trimRegion;
2169       if (trimLeft)
2170       {
2171         trimRegion = new TrimRegionCommand("Remove Left",
2172                 TrimRegionCommand.TRIM_LEFT, seqs, column,
2173                 viewport.alignment, viewport.colSel,
2174                 viewport.selectionGroup);
2175         viewport.setStartRes(0);
2176       }
2177       else
2178       {
2179         trimRegion = new TrimRegionCommand("Remove Right",
2180                 TrimRegionCommand.TRIM_RIGHT, seqs, column,
2181                 viewport.alignment, viewport.colSel,
2182                 viewport.selectionGroup);
2183       }
2184
2185       statusBar.setText("Removed " + trimRegion.getSize() + " columns.");
2186
2187       addHistoryItem(trimRegion);
2188
2189       Vector groups = viewport.alignment.getGroups();
2190
2191       for (int i = 0; i < groups.size(); i++)
2192       {
2193         SequenceGroup sg = (SequenceGroup) groups.get(i);
2194
2195         if ((trimLeft && !sg.adjustForRemoveLeft(column))
2196                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
2197         {
2198           viewport.alignment.deleteGroup(sg);
2199         }
2200       }
2201
2202       viewport.firePropertyChange("alignment", null, viewport
2203               .getAlignment().getSequences());
2204     }
2205   }
2206
2207   /**
2208    * DOCUMENT ME!
2209    * 
2210    * @param e
2211    *          DOCUMENT ME!
2212    */
2213   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2214   {
2215     int start = 0, end = viewport.alignment.getWidth() - 1;
2216
2217     SequenceI[] seqs;
2218     if (viewport.getSelectionGroup() != null)
2219     {
2220       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2221               viewport.hiddenRepSequences);
2222       start = viewport.getSelectionGroup().getStartRes();
2223       end = viewport.getSelectionGroup().getEndRes();
2224     }
2225     else
2226     {
2227       seqs = viewport.alignment.getSequencesArray();
2228     }
2229
2230     RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2231             "Remove Gapped Columns", seqs, start, end, viewport.alignment);
2232
2233     addHistoryItem(removeGapCols);
2234
2235     statusBar.setText("Removed " + removeGapCols.getSize()
2236             + " empty columns.");
2237
2238     // This is to maintain viewport position on first residue
2239     // of first sequence
2240     SequenceI seq = viewport.alignment.getSequenceAt(0);
2241     int startRes = seq.findPosition(viewport.startRes);
2242     // ShiftList shifts;
2243     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2244     // edit.alColumnChanges=shifts.getInverse();
2245     // if (viewport.hasHiddenColumns)
2246     // viewport.getColumnSelection().compensateForEdits(shifts);
2247     viewport.setStartRes(seq.findIndex(startRes) - 1);
2248     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2249             .getSequences());
2250
2251   }
2252
2253   /**
2254    * DOCUMENT ME!
2255    * 
2256    * @param e
2257    *          DOCUMENT ME!
2258    */
2259   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2260   {
2261     int start = 0, end = viewport.alignment.getWidth() - 1;
2262
2263     SequenceI[] seqs;
2264     if (viewport.getSelectionGroup() != null)
2265     {
2266       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2267               viewport.hiddenRepSequences);
2268       start = viewport.getSelectionGroup().getStartRes();
2269       end = viewport.getSelectionGroup().getEndRes();
2270     }
2271     else
2272     {
2273       seqs = viewport.alignment.getSequencesArray();
2274     }
2275
2276     // This is to maintain viewport position on first residue
2277     // of first sequence
2278     SequenceI seq = viewport.alignment.getSequenceAt(0);
2279     int startRes = seq.findPosition(viewport.startRes);
2280
2281     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
2282             viewport.alignment));
2283
2284     viewport.setStartRes(seq.findIndex(startRes) - 1);
2285
2286     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2287             .getSequences());
2288
2289   }
2290
2291   /**
2292    * DOCUMENT ME!
2293    * 
2294    * @param e
2295    *          DOCUMENT ME!
2296    */
2297   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2298   {
2299     viewport.padGaps = padGapsMenuitem.isSelected();
2300     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2301             .getSequences());
2302   }
2303
2304   // else
2305   {
2306     // if (justifySeqs>0)
2307     {
2308       // alignment.justify(justifySeqs!=RIGHT_JUSTIFY);
2309     }
2310   }
2311
2312   // }
2313
2314   /**
2315    * DOCUMENT ME!
2316    * 
2317    * @param e
2318    *          DOCUMENT ME!
2319    */
2320   public void findMenuItem_actionPerformed(ActionEvent e)
2321   {
2322     new Finder();
2323   }
2324
2325   public void newView_actionPerformed(ActionEvent e)
2326   {
2327     newView(true);
2328   }
2329
2330   /**
2331    * 
2332    * @param copyAnnotation
2333    *          if true then duplicate all annnotation, groups and settings
2334    * @return new alignment panel, already displayed.
2335    */
2336   public AlignmentPanel newView(boolean copyAnnotation)
2337   {
2338     return newView(null, copyAnnotation);
2339   }
2340
2341   /**
2342    * 
2343    * @param viewTitle
2344    *          title of newly created view
2345    * @return new alignment panel, already displayed.
2346    */
2347   public AlignmentPanel newView(String viewTitle)
2348   {
2349     return newView(viewTitle, true);
2350   }
2351
2352   /**
2353    * 
2354    * @param viewTitle
2355    *          title of newly created view
2356    * @param copyAnnotation
2357    *          if true then duplicate all annnotation, groups and settings
2358    * @return new alignment panel, already displayed.
2359    */
2360   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
2361   {
2362     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
2363             true);
2364     if (!copyAnnotation)
2365     {
2366       // just remove all the current annotation except for the automatic stuff
2367       newap.av.alignment.deleteAllGroups();
2368       for (AlignmentAnnotation alan : newap.av.alignment
2369               .getAlignmentAnnotation())
2370       {
2371         if (!alan.autoCalculated)
2372         {
2373           newap.av.alignment.deleteAnnotation(alan);
2374         }
2375         ;
2376       }
2377     }
2378
2379     newap.av.gatherViewsHere = false;
2380
2381     if (viewport.viewName == null)
2382     {
2383       viewport.viewName = "Original";
2384     }
2385
2386     newap.av.historyList = viewport.historyList;
2387     newap.av.redoList = viewport.redoList;
2388
2389     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2390     // make sure the new view has a unique name - this is essential for Jalview
2391     // 2 archives
2392     boolean addFirstIndex = false;
2393     if (viewTitle == null || viewTitle.trim().length() == 0)
2394     {
2395       viewTitle = "View";
2396       addFirstIndex = true;
2397     }
2398     else
2399     {
2400       index = 1;// we count from 1 if given a specific name
2401     }
2402     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
2403     Vector comps = (Vector) PaintRefresher.components.get(viewport
2404             .getSequenceSetId());
2405     Vector existingNames = new Vector();
2406     for (int i = 0; i < comps.size(); i++)
2407     {
2408       if (comps.elementAt(i) instanceof AlignmentPanel)
2409       {
2410         AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
2411         if (!existingNames.contains(ap.av.viewName))
2412         {
2413           existingNames.addElement(ap.av.viewName);
2414         }
2415       }
2416     }
2417
2418     while (existingNames.contains(newViewName))
2419     {
2420       newViewName = viewTitle + " " + (++index);
2421     }
2422
2423     newap.av.viewName = newViewName;
2424
2425     addAlignmentPanel(newap, true);
2426
2427     if (alignPanels.size() == 2)
2428     {
2429       viewport.gatherViewsHere = true;
2430     }
2431     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2432     return newap;
2433   }
2434
2435   public void expandViews_actionPerformed(ActionEvent e)
2436   {
2437     Desktop.instance.explodeViews(this);
2438   }
2439
2440   public void gatherViews_actionPerformed(ActionEvent e)
2441   {
2442     Desktop.instance.gatherViews(this);
2443   }
2444
2445   /**
2446    * DOCUMENT ME!
2447    * 
2448    * @param e
2449    *          DOCUMENT ME!
2450    */
2451   public void font_actionPerformed(ActionEvent e)
2452   {
2453     new FontChooser(alignPanel);
2454   }
2455
2456   /**
2457    * DOCUMENT ME!
2458    * 
2459    * @param e
2460    *          DOCUMENT ME!
2461    */
2462   protected void seqLimit_actionPerformed(ActionEvent e)
2463   {
2464     viewport.setShowJVSuffix(seqLimits.isSelected());
2465
2466     alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel
2467             .calculateIdWidth());
2468     alignPanel.paintAlignment(true);
2469   }
2470
2471   public void idRightAlign_actionPerformed(ActionEvent e)
2472   {
2473     viewport.rightAlignIds = idRightAlign.isSelected();
2474     alignPanel.paintAlignment(true);
2475   }
2476
2477   public void centreColumnLabels_actionPerformed(ActionEvent e)
2478   {
2479     viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
2480     alignPanel.paintAlignment(true);
2481   }
2482
2483   /*
2484    * (non-Javadoc)
2485    * 
2486    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
2487    */
2488   protected void followHighlight_actionPerformed()
2489   {
2490     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
2491     {
2492       alignPanel.scrollToPosition(
2493               alignPanel.seqPanel.seqCanvas.searchResults, false);
2494     }
2495   }
2496
2497   /**
2498    * DOCUMENT ME!
2499    * 
2500    * @param e
2501    *          DOCUMENT ME!
2502    */
2503   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2504   {
2505     viewport.setColourText(colourTextMenuItem.isSelected());
2506     alignPanel.paintAlignment(true);
2507   }
2508
2509   /**
2510    * DOCUMENT ME!
2511    * 
2512    * @param e
2513    *          DOCUMENT ME!
2514    */
2515   public void wrapMenuItem_actionPerformed(ActionEvent e)
2516   {
2517     scaleAbove.setVisible(wrapMenuItem.isSelected());
2518     scaleLeft.setVisible(wrapMenuItem.isSelected());
2519     scaleRight.setVisible(wrapMenuItem.isSelected());
2520     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2521     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
2522   }
2523
2524   public void showAllSeqs_actionPerformed(ActionEvent e)
2525   {
2526     viewport.showAllHiddenSeqs();
2527   }
2528
2529   public void showAllColumns_actionPerformed(ActionEvent e)
2530   {
2531     viewport.showAllHiddenColumns();
2532     repaint();
2533   }
2534
2535   public void hideSelSequences_actionPerformed(ActionEvent e)
2536   {
2537     viewport.hideAllSelectedSeqs();
2538     alignPanel.paintAlignment(true);
2539   }
2540
2541   /**
2542    * called by key handler and the hide all/show all menu items
2543    * 
2544    * @param toggleSeqs
2545    * @param toggleCols
2546    */
2547   private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
2548   {
2549
2550     boolean hide = false;
2551     SequenceGroup sg = viewport.getSelectionGroup();
2552     if (!toggleSeqs && !toggleCols)
2553     {
2554       // Hide everything by the current selection - this is a hack - we do the
2555       // invert and then hide
2556       // first check that there will be visible columns after the invert.
2557       if ((viewport.colSel != null && viewport.colSel.getSelected() != null && viewport.colSel
2558               .getSelected().size() > 0)
2559               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
2560                       .getEndRes()))
2561       {
2562         // now invert the sequence set, if required - empty selection implies
2563         // that no hiding is required.
2564         if (sg != null)
2565         {
2566           invertSequenceMenuItem_actionPerformed(null);
2567           sg = viewport.getSelectionGroup();
2568           toggleSeqs = true;
2569
2570         }
2571         viewport.expandColSelection(sg, true);
2572         // finally invert the column selection and get the new sequence
2573         // selection.
2574         invertColSel_actionPerformed(null);
2575         toggleCols = true;
2576       }
2577     }
2578
2579     if (toggleSeqs)
2580     {
2581       if (sg != null && sg.getSize() != viewport.alignment.getHeight())
2582       {
2583         hideSelSequences_actionPerformed(null);
2584         hide = true;
2585       }
2586       else if (!(toggleCols && viewport.colSel.getSelected().size() > 0))
2587       {
2588         showAllSeqs_actionPerformed(null);
2589       }
2590     }
2591
2592     if (toggleCols)
2593     {
2594       if (viewport.colSel.getSelected().size() > 0)
2595       {
2596         hideSelColumns_actionPerformed(null);
2597         if (!toggleSeqs)
2598         {
2599           viewport.selectionGroup = sg;
2600         }
2601       }
2602       else if (!hide)
2603       {
2604         showAllColumns_actionPerformed(null);
2605       }
2606     }
2607   }
2608
2609   /*
2610    * (non-Javadoc)
2611    * 
2612    * @see
2613    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
2614    * event.ActionEvent)
2615    */
2616   public void hideAllButSelection_actionPerformed(ActionEvent e)
2617   {
2618     toggleHiddenRegions(false, false);
2619   }
2620
2621   /*
2622    * (non-Javadoc)
2623    * 
2624    * @see
2625    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
2626    * .ActionEvent)
2627    */
2628   public void hideAllSelection_actionPerformed(ActionEvent e)
2629   {
2630     SequenceGroup sg = viewport.getSelectionGroup();
2631     viewport.expandColSelection(sg, false);
2632     viewport.hideAllSelectedSeqs();
2633     viewport.hideSelectedColumns();
2634     alignPanel.paintAlignment(true);
2635   }
2636
2637   /*
2638    * (non-Javadoc)
2639    * 
2640    * @see
2641    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
2642    * ActionEvent)
2643    */
2644   public void showAllhidden_actionPerformed(ActionEvent e)
2645   {
2646     viewport.showAllHiddenColumns();
2647     viewport.showAllHiddenSeqs();
2648     alignPanel.paintAlignment(true);
2649   }
2650
2651   public void hideSelColumns_actionPerformed(ActionEvent e)
2652   {
2653     viewport.hideSelectedColumns();
2654     alignPanel.paintAlignment(true);
2655   }
2656
2657   public void hiddenMarkers_actionPerformed(ActionEvent e)
2658   {
2659     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
2660     repaint();
2661   }
2662
2663   /**
2664    * DOCUMENT ME!
2665    * 
2666    * @param e
2667    *          DOCUMENT ME!
2668    */
2669   protected void scaleAbove_actionPerformed(ActionEvent e)
2670   {
2671     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
2672     alignPanel.paintAlignment(true);
2673   }
2674
2675   /**
2676    * DOCUMENT ME!
2677    * 
2678    * @param e
2679    *          DOCUMENT ME!
2680    */
2681   protected void scaleLeft_actionPerformed(ActionEvent e)
2682   {
2683     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
2684     alignPanel.paintAlignment(true);
2685   }
2686
2687   /**
2688    * DOCUMENT ME!
2689    * 
2690    * @param e
2691    *          DOCUMENT ME!
2692    */
2693   protected void scaleRight_actionPerformed(ActionEvent e)
2694   {
2695     viewport.setScaleRightWrapped(scaleRight.isSelected());
2696     alignPanel.paintAlignment(true);
2697   }
2698
2699   /**
2700    * DOCUMENT ME!
2701    * 
2702    * @param e
2703    *          DOCUMENT ME!
2704    */
2705   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
2706   {
2707     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
2708     alignPanel.paintAlignment(true);
2709   }
2710
2711   /**
2712    * DOCUMENT ME!
2713    * 
2714    * @param e
2715    *          DOCUMENT ME!
2716    */
2717   public void viewTextMenuItem_actionPerformed(ActionEvent e)
2718   {
2719     viewport.setShowText(viewTextMenuItem.isSelected());
2720     alignPanel.paintAlignment(true);
2721   }
2722
2723   /**
2724    * DOCUMENT ME!
2725    * 
2726    * @param e
2727    *          DOCUMENT ME!
2728    */
2729   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
2730   {
2731     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
2732     alignPanel.paintAlignment(true);
2733   }
2734
2735   public FeatureSettings featureSettings;
2736
2737   public void featureSettings_actionPerformed(ActionEvent e)
2738   {
2739     if (featureSettings != null)
2740     {
2741       featureSettings.close();
2742       featureSettings = null;
2743     }
2744     if (!showSeqFeatures.isSelected())
2745     {
2746       // make sure features are actually displayed
2747       showSeqFeatures.setSelected(true);
2748       showSeqFeatures_actionPerformed(null);
2749     }
2750     featureSettings = new FeatureSettings(this);
2751   }
2752
2753   /**
2754    * Set or clear 'Show Sequence Features'
2755    * 
2756    * @param evt
2757    *          DOCUMENT ME!
2758    */
2759   public void showSeqFeatures_actionPerformed(ActionEvent evt)
2760   {
2761     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
2762     alignPanel.paintAlignment(true);
2763     if (alignPanel.getOverviewPanel() != null)
2764     {
2765       alignPanel.getOverviewPanel().updateOverviewImage();
2766     }
2767   }
2768
2769   /**
2770    * Set or clear 'Show Sequence Features'
2771    * 
2772    * @param evt
2773    *          DOCUMENT ME!
2774    */
2775   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
2776   {
2777     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
2778             .isSelected());
2779     if (viewport.getShowSequenceFeaturesHeight())
2780     {
2781       // ensure we're actually displaying features
2782       viewport.setShowSequenceFeatures(true);
2783       showSeqFeatures.setSelected(true);
2784     }
2785     alignPanel.paintAlignment(true);
2786     if (alignPanel.getOverviewPanel() != null)
2787     {
2788       alignPanel.getOverviewPanel().updateOverviewImage();
2789     }
2790   }
2791
2792   /**
2793    * DOCUMENT ME!
2794    * 
2795    * @param e
2796    *          DOCUMENT ME!
2797    */
2798   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
2799   {
2800     viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
2801     alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
2802   }
2803
2804   public void alignmentProperties()
2805   {
2806     JEditorPane editPane = new JEditorPane("text/html", "");
2807     editPane.setEditable(false);
2808     StringBuffer contents = new AlignmentProperties(viewport.alignment)
2809             .formatAsHtml();
2810     editPane.setText("<html>" + contents.toString() + "</html>");
2811     JInternalFrame frame = new JInternalFrame();
2812     frame.getContentPane().add(new JScrollPane(editPane));
2813
2814     Desktop.instance.addInternalFrame(frame, "Alignment Properties: "
2815             + getTitle(), 500, 400);
2816   }
2817
2818   /**
2819    * DOCUMENT ME!
2820    * 
2821    * @param e
2822    *          DOCUMENT ME!
2823    */
2824   public void overviewMenuItem_actionPerformed(ActionEvent e)
2825   {
2826     if (alignPanel.overviewPanel != null)
2827     {
2828       return;
2829     }
2830
2831     JInternalFrame frame = new JInternalFrame();
2832     OverviewPanel overview = new OverviewPanel(alignPanel);
2833     frame.setContentPane(overview);
2834     Desktop.addInternalFrame(frame, "Overview " + this.getTitle(),
2835             frame.getWidth(), frame.getHeight());
2836     frame.pack();
2837     frame.setLayer(JLayeredPane.PALETTE_LAYER);
2838     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
2839     {
2840       public void internalFrameClosed(
2841               javax.swing.event.InternalFrameEvent evt)
2842       {
2843         alignPanel.setOverviewPanel(null);
2844       };
2845     });
2846
2847     alignPanel.setOverviewPanel(overview);
2848   }
2849
2850   public void textColour_actionPerformed(ActionEvent e)
2851   {
2852     new TextColourChooser().chooseColour(alignPanel, null);
2853   }
2854
2855   /**
2856    * DOCUMENT ME!
2857    * 
2858    * @param e
2859    *          DOCUMENT ME!
2860    */
2861   protected void noColourmenuItem_actionPerformed(ActionEvent e)
2862   {
2863     changeColour(null);
2864   }
2865
2866   /**
2867    * DOCUMENT ME!
2868    * 
2869    * @param e
2870    *          DOCUMENT ME!
2871    */
2872   public void clustalColour_actionPerformed(ActionEvent e)
2873   {
2874     changeColour(new ClustalxColourScheme(
2875             viewport.alignment.getSequences(),
2876             viewport.alignment.getWidth()));
2877   }
2878
2879   /**
2880    * DOCUMENT ME!
2881    * 
2882    * @param e
2883    *          DOCUMENT ME!
2884    */
2885   public void zappoColour_actionPerformed(ActionEvent e)
2886   {
2887     changeColour(new ZappoColourScheme());
2888   }
2889
2890   /**
2891    * DOCUMENT ME!
2892    * 
2893    * @param e
2894    *          DOCUMENT ME!
2895    */
2896   public void taylorColour_actionPerformed(ActionEvent e)
2897   {
2898     changeColour(new TaylorColourScheme());
2899   }
2900
2901   /**
2902    * DOCUMENT ME!
2903    * 
2904    * @param e
2905    *          DOCUMENT ME!
2906    */
2907   public void hydrophobicityColour_actionPerformed(ActionEvent e)
2908   {
2909     changeColour(new HydrophobicColourScheme());
2910   }
2911
2912   /**
2913    * DOCUMENT ME!
2914    * 
2915    * @param e
2916    *          DOCUMENT ME!
2917    */
2918   public void helixColour_actionPerformed(ActionEvent e)
2919   {
2920     changeColour(new HelixColourScheme());
2921   }
2922
2923   /**
2924    * DOCUMENT ME!
2925    * 
2926    * @param e
2927    *          DOCUMENT ME!
2928    */
2929   public void strandColour_actionPerformed(ActionEvent e)
2930   {
2931     changeColour(new StrandColourScheme());
2932   }
2933
2934   /**
2935    * DOCUMENT ME!
2936    * 
2937    * @param e
2938    *          DOCUMENT ME!
2939    */
2940   public void turnColour_actionPerformed(ActionEvent e)
2941   {
2942     changeColour(new TurnColourScheme());
2943   }
2944
2945   /**
2946    * DOCUMENT ME!
2947    * 
2948    * @param e
2949    *          DOCUMENT ME!
2950    */
2951   public void buriedColour_actionPerformed(ActionEvent e)
2952   {
2953     changeColour(new BuriedColourScheme());
2954   }
2955
2956   /**
2957    * DOCUMENT ME!
2958    * 
2959    * @param e
2960    *          DOCUMENT ME!
2961    */
2962   public void nucleotideColour_actionPerformed(ActionEvent e)
2963   {
2964     changeColour(new NucleotideColourScheme());
2965   }
2966
2967   public void annotationColour_actionPerformed(ActionEvent e)
2968   {
2969     new AnnotationColourChooser(viewport, alignPanel);
2970   }
2971
2972   /**
2973    * DOCUMENT ME!
2974    * 
2975    * @param e
2976    *          DOCUMENT ME!
2977    */
2978   protected void applyToAllGroups_actionPerformed(ActionEvent e)
2979   {
2980     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
2981   }
2982
2983   /**
2984    * DOCUMENT ME!
2985    * 
2986    * @param cs
2987    *          DOCUMENT ME!
2988    */
2989   public void changeColour(ColourSchemeI cs)
2990   {
2991     int threshold = 0;
2992
2993     if (cs != null)
2994     {
2995       if (viewport.getAbovePIDThreshold())
2996       {
2997         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
2998                 "Background");
2999
3000         cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3001
3002         viewport.setGlobalColourScheme(cs);
3003       }
3004       else
3005       {
3006         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3007       }
3008
3009       if (viewport.getConservationSelected())
3010       {
3011
3012         Alignment al = (Alignment) viewport.alignment;
3013         Conservation c = new Conservation("All",
3014                 ResidueProperties.propHash, 3, al.getSequences(), 0,
3015                 al.getWidth() - 1);
3016
3017         c.calculate();
3018         c.verdict(false, viewport.ConsPercGaps);
3019
3020         cs.setConservation(c);
3021
3022         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
3023                 cs, "Background"));
3024       }
3025       else
3026       {
3027         cs.setConservation(null);
3028       }
3029
3030       cs.setConsensus(viewport.hconsensus);
3031     }
3032
3033     viewport.setGlobalColourScheme(cs);
3034
3035     if (viewport.getColourAppliesToAllGroups())
3036     {
3037       Vector groups = viewport.alignment.getGroups();
3038
3039       for (int i = 0; i < groups.size(); i++)
3040       {
3041         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
3042
3043         if (cs == null)
3044         {
3045           sg.cs = null;
3046           continue;
3047         }
3048
3049         if (cs instanceof ClustalxColourScheme)
3050         {
3051           sg.cs = new ClustalxColourScheme(
3052                   sg.getSequences(viewport.hiddenRepSequences),
3053                   sg.getWidth());
3054         }
3055         else if (cs instanceof UserColourScheme)
3056         {
3057           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
3058         }
3059         else
3060         {
3061           try
3062           {
3063             sg.cs = (ColourSchemeI) cs.getClass().newInstance();
3064           } catch (Exception ex)
3065           {
3066           }
3067         }
3068
3069         if (viewport.getAbovePIDThreshold()
3070                 || cs instanceof PIDColourScheme
3071                 || cs instanceof Blosum62ColourScheme)
3072         {
3073           sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
3074
3075           sg.cs.setConsensus(AAFrequency.calculate(
3076                   sg.getSequences(viewport.hiddenRepSequences),
3077                   sg.getStartRes(), sg.getEndRes() + 1));
3078         }
3079         else
3080         {
3081           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
3082         }
3083
3084         if (viewport.getConservationSelected())
3085         {
3086           Conservation c = new Conservation("Group",
3087                   ResidueProperties.propHash, 3,
3088                   sg.getSequences(viewport.hiddenRepSequences),
3089                   sg.getStartRes(), sg.getEndRes() + 1);
3090           c.calculate();
3091           c.verdict(false, viewport.ConsPercGaps);
3092           sg.cs.setConservation(c);
3093         }
3094         else
3095         {
3096           sg.cs.setConservation(null);
3097         }
3098       }
3099     }
3100
3101     if (alignPanel.getOverviewPanel() != null)
3102     {
3103       alignPanel.getOverviewPanel().updateOverviewImage();
3104     }
3105
3106     alignPanel.paintAlignment(true);
3107   }
3108
3109   /**
3110    * DOCUMENT ME!
3111    * 
3112    * @param e
3113    *          DOCUMENT ME!
3114    */
3115   protected void modifyPID_actionPerformed(ActionEvent e)
3116   {
3117     if (viewport.getAbovePIDThreshold()
3118             && viewport.globalColourScheme != null)
3119     {
3120       SliderPanel.setPIDSliderSource(alignPanel,
3121               viewport.getGlobalColourScheme(), "Background");
3122       SliderPanel.showPIDSlider();
3123     }
3124   }
3125
3126   /**
3127    * DOCUMENT ME!
3128    * 
3129    * @param e
3130    *          DOCUMENT ME!
3131    */
3132   protected void modifyConservation_actionPerformed(ActionEvent e)
3133   {
3134     if (viewport.getConservationSelected()
3135             && viewport.globalColourScheme != null)
3136     {
3137       SliderPanel.setConservationSlider(alignPanel,
3138               viewport.globalColourScheme, "Background");
3139       SliderPanel.showConservationSlider();
3140     }
3141   }
3142
3143   /**
3144    * DOCUMENT ME!
3145    * 
3146    * @param e
3147    *          DOCUMENT ME!
3148    */
3149   protected void conservationMenuItem_actionPerformed(ActionEvent e)
3150   {
3151     viewport.setConservationSelected(conservationMenuItem.isSelected());
3152
3153     viewport.setAbovePIDThreshold(false);
3154     abovePIDThreshold.setSelected(false);
3155
3156     changeColour(viewport.getGlobalColourScheme());
3157
3158     modifyConservation_actionPerformed(null);
3159   }
3160
3161   /**
3162    * DOCUMENT ME!
3163    * 
3164    * @param e
3165    *          DOCUMENT ME!
3166    */
3167   public void abovePIDThreshold_actionPerformed(ActionEvent e)
3168   {
3169     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
3170
3171     conservationMenuItem.setSelected(false);
3172     viewport.setConservationSelected(false);
3173
3174     changeColour(viewport.getGlobalColourScheme());
3175
3176     modifyPID_actionPerformed(null);
3177   }
3178
3179   /**
3180    * DOCUMENT ME!
3181    * 
3182    * @param e
3183    *          DOCUMENT ME!
3184    */
3185   public void userDefinedColour_actionPerformed(ActionEvent e)
3186   {
3187     if (e.getActionCommand().equals("User Defined..."))
3188     {
3189       new UserDefinedColours(alignPanel, null);
3190     }
3191     else
3192     {
3193       UserColourScheme udc = (UserColourScheme) UserDefinedColours
3194               .getUserColourSchemes().get(e.getActionCommand());
3195
3196       changeColour(udc);
3197     }
3198   }
3199
3200   public void updateUserColourMenu()
3201   {
3202
3203     Component[] menuItems = colourMenu.getMenuComponents();
3204     int i, iSize = menuItems.length;
3205     for (i = 0; i < iSize; i++)
3206     {
3207       if (menuItems[i].getName() != null
3208               && menuItems[i].getName().equals("USER_DEFINED"))
3209       {
3210         colourMenu.remove(menuItems[i]);
3211         iSize--;
3212       }
3213     }
3214     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
3215     {
3216       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
3217               .getUserColourSchemes().keys();
3218
3219       while (userColours.hasMoreElements())
3220       {
3221         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
3222                 userColours.nextElement().toString());
3223         radioItem.setName("USER_DEFINED");
3224         radioItem.addMouseListener(new MouseAdapter()
3225         {
3226           public void mousePressed(MouseEvent evt)
3227           {
3228             if (evt.isControlDown()
3229                     || SwingUtilities.isRightMouseButton(evt))
3230             {
3231               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
3232
3233               int option = JOptionPane.showInternalConfirmDialog(
3234                       jalview.gui.Desktop.desktop,
3235                       "Remove from default list?",
3236                       "Remove user defined colour",
3237                       JOptionPane.YES_NO_OPTION);
3238               if (option == JOptionPane.YES_OPTION)
3239               {
3240                 jalview.gui.UserDefinedColours
3241                         .removeColourFromDefaults(radioItem.getText());
3242                 colourMenu.remove(radioItem);
3243               }
3244               else
3245               {
3246                 radioItem.addActionListener(new ActionListener()
3247                 {
3248                   public void actionPerformed(ActionEvent evt)
3249                   {
3250                     userDefinedColour_actionPerformed(evt);
3251                   }
3252                 });
3253               }
3254             }
3255           }
3256         });
3257         radioItem.addActionListener(new ActionListener()
3258         {
3259           public void actionPerformed(ActionEvent evt)
3260           {
3261             userDefinedColour_actionPerformed(evt);
3262           }
3263         });
3264
3265         colourMenu.insert(radioItem, 15);
3266         colours.add(radioItem);
3267       }
3268     }
3269   }
3270
3271   /**
3272    * DOCUMENT ME!
3273    * 
3274    * @param e
3275    *          DOCUMENT ME!
3276    */
3277   public void PIDColour_actionPerformed(ActionEvent e)
3278   {
3279     changeColour(new PIDColourScheme());
3280   }
3281
3282   /**
3283    * DOCUMENT ME!
3284    * 
3285    * @param e
3286    *          DOCUMENT ME!
3287    */
3288   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
3289   {
3290     changeColour(new Blosum62ColourScheme());
3291   }
3292
3293   /**
3294    * DOCUMENT ME!
3295    * 
3296    * @param e
3297    *          DOCUMENT ME!
3298    */
3299   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3300   {
3301     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3302     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
3303             .getAlignment().getSequenceAt(0), null);
3304     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3305             viewport.alignment));
3306     alignPanel.paintAlignment(true);
3307   }
3308
3309   /**
3310    * DOCUMENT ME!
3311    * 
3312    * @param e
3313    *          DOCUMENT ME!
3314    */
3315   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3316   {
3317     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3318     AlignmentSorter.sortByID(viewport.getAlignment());
3319     addHistoryItem(new OrderCommand("ID Sort", oldOrder, viewport.alignment));
3320     alignPanel.paintAlignment(true);
3321   }
3322
3323   /**
3324    * DOCUMENT ME!
3325    * 
3326    * @param e
3327    *          DOCUMENT ME!
3328    */
3329   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3330   {
3331     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3332     AlignmentSorter.sortByLength(viewport.getAlignment());
3333     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3334             viewport.alignment));
3335     alignPanel.paintAlignment(true);
3336   }
3337
3338   /**
3339    * DOCUMENT ME!
3340    * 
3341    * @param e
3342    *          DOCUMENT ME!
3343    */
3344   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3345   {
3346     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3347     AlignmentSorter.sortByGroup(viewport.getAlignment());
3348     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3349             viewport.alignment));
3350
3351     alignPanel.paintAlignment(true);
3352   }
3353
3354   /**
3355    * DOCUMENT ME!
3356    * 
3357    * @param e
3358    *          DOCUMENT ME!
3359    */
3360   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3361   {
3362     new RedundancyPanel(alignPanel, this);
3363   }
3364
3365   /**
3366    * DOCUMENT ME!
3367    * 
3368    * @param e
3369    *          DOCUMENT ME!
3370    */
3371   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3372   {
3373     if ((viewport.getSelectionGroup() == null)
3374             || (viewport.getSelectionGroup().getSize() < 2))
3375     {
3376       JOptionPane.showInternalMessageDialog(this,
3377               "You must select at least 2 sequences.", "Invalid Selection",
3378               JOptionPane.WARNING_MESSAGE);
3379     }
3380     else
3381     {
3382       JInternalFrame frame = new JInternalFrame();
3383       frame.setContentPane(new PairwiseAlignPanel(viewport));
3384       Desktop.addInternalFrame(frame, "Pairwise Alignment", 600, 500);
3385     }
3386   }
3387
3388   /**
3389    * DOCUMENT ME!
3390    * 
3391    * @param e
3392    *          DOCUMENT ME!
3393    */
3394   public void PCAMenuItem_actionPerformed(ActionEvent e)
3395   {
3396     if (((viewport.getSelectionGroup() != null)
3397             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
3398             .getSelectionGroup().getSize() > 0))
3399             || (viewport.getAlignment().getHeight() < 4))
3400     {
3401       JOptionPane.showInternalMessageDialog(this,
3402               "Principal component analysis must take\n"
3403                       + "at least 4 input sequences.",
3404               "Sequence selection insufficient",
3405               JOptionPane.WARNING_MESSAGE);
3406
3407       return;
3408     }
3409
3410     new PCAPanel(alignPanel);
3411   }
3412
3413   public void autoCalculate_actionPerformed(ActionEvent e)
3414   {
3415     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3416     if (viewport.autoCalculateConsensus)
3417     {
3418       viewport.firePropertyChange("alignment", null, viewport
3419               .getAlignment().getSequences());
3420     }
3421   }
3422
3423   /**
3424    * DOCUMENT ME!
3425    * 
3426    * @param e
3427    *          DOCUMENT ME!
3428    */
3429   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3430   {
3431     NewTreePanel("AV", "PID", "Average distance tree using PID");
3432   }
3433
3434   /**
3435    * DOCUMENT ME!
3436    * 
3437    * @param e
3438    *          DOCUMENT ME!
3439    */
3440   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3441   {
3442     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3443   }
3444
3445   /**
3446    * DOCUMENT ME!
3447    * 
3448    * @param e
3449    *          DOCUMENT ME!
3450    */
3451   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3452   {
3453     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3454   }
3455
3456   /**
3457    * DOCUMENT ME!
3458    * 
3459    * @param e
3460    *          DOCUMENT ME!
3461    */
3462   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3463   {
3464     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3465   }
3466
3467   /**
3468    * DOCUMENT ME!
3469    * 
3470    * @param type
3471    *          DOCUMENT ME!
3472    * @param pwType
3473    *          DOCUMENT ME!
3474    * @param title
3475    *          DOCUMENT ME!
3476    */
3477   void NewTreePanel(String type, String pwType, String title)
3478   {
3479     TreePanel tp;
3480
3481     if (viewport.getSelectionGroup() != null)
3482     {
3483       if (viewport.getSelectionGroup().getSize() < 3)
3484       {
3485         JOptionPane
3486                 .showMessageDialog(
3487                         Desktop.desktop,
3488                         "You need to have more than two sequences selected to build a tree!",
3489                         "Not enough sequences", JOptionPane.WARNING_MESSAGE);
3490         return;
3491       }
3492
3493       int s = 0;
3494       SequenceGroup sg = viewport.getSelectionGroup();
3495
3496       /* Decide if the selection is a column region */
3497       while (s < sg.getSize())
3498       {
3499         if (((SequenceI) sg.getSequences(null).elementAt(s++)).getLength() < sg
3500                 .getEndRes())
3501         {
3502           JOptionPane
3503                   .showMessageDialog(
3504                           Desktop.desktop,
3505                           "The selected region to create a tree may\nonly contain residues or gaps.\n"
3506                                   + "Try using the Pad function in the edit menu,\n"
3507                                   + "or one of the multiple sequence alignment web services.",
3508                           "Sequences in selection are not aligned",
3509                           JOptionPane.WARNING_MESSAGE);
3510
3511           return;
3512         }
3513       }
3514
3515       title = title + " on region";
3516       tp = new TreePanel(alignPanel, type, pwType);
3517     }
3518     else
3519     {
3520       // are the visible sequences aligned?
3521       if (!viewport.alignment.isAligned(false))
3522       {
3523         JOptionPane
3524                 .showMessageDialog(
3525                         Desktop.desktop,
3526                         "The sequences must be aligned before creating a tree.\n"
3527                                 + "Try using the Pad function in the edit menu,\n"
3528                                 + "or one of the multiple sequence alignment web services.",
3529                         "Sequences not aligned",
3530                         JOptionPane.WARNING_MESSAGE);
3531
3532         return;
3533       }
3534
3535       if (viewport.alignment.getHeight() < 2)
3536       {
3537         return;
3538       }
3539
3540       tp = new TreePanel(alignPanel, type, pwType);
3541     }
3542
3543     title += " from ";
3544
3545     if (viewport.viewName != null)
3546     {
3547       title += viewport.viewName + " of ";
3548     }
3549
3550     title += this.title;
3551
3552     Desktop.addInternalFrame(tp, title, 600, 500);
3553   }
3554
3555   /**
3556    * DOCUMENT ME!
3557    * 
3558    * @param title
3559    *          DOCUMENT ME!
3560    * @param order
3561    *          DOCUMENT ME!
3562    */
3563   public void addSortByOrderMenuItem(String title,
3564           final AlignmentOrder order)
3565   {
3566     final JMenuItem item = new JMenuItem("by " + title);
3567     sort.add(item);
3568     item.addActionListener(new java.awt.event.ActionListener()
3569     {
3570       public void actionPerformed(ActionEvent e)
3571       {
3572         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3573
3574         // TODO: JBPNote - have to map order entries to curent SequenceI
3575         // pointers
3576         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3577
3578         addHistoryItem(new OrderCommand(order.getName(), oldOrder,
3579                 viewport.alignment));
3580
3581         alignPanel.paintAlignment(true);
3582       }
3583     });
3584   }
3585
3586   /**
3587    * Add a new sort by annotation score menu item
3588    * 
3589    * @param sort
3590    *          the menu to add the option to
3591    * @param scoreLabel
3592    *          the label used to retrieve scores for each sequence on the
3593    *          alignment
3594    */
3595   public void addSortByAnnotScoreMenuItem(JMenu sort,
3596           final String scoreLabel)
3597   {
3598     final JMenuItem item = new JMenuItem(scoreLabel);
3599     sort.add(item);
3600     item.addActionListener(new java.awt.event.ActionListener()
3601     {
3602       public void actionPerformed(ActionEvent e)
3603       {
3604         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3605         AlignmentSorter.sortByAnnotationScore(scoreLabel,
3606                 viewport.getAlignment());// ,viewport.getSelectionGroup());
3607         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
3608                 viewport.alignment));
3609         alignPanel.paintAlignment(true);
3610       }
3611     });
3612   }
3613
3614   /**
3615    * last hash for alignment's annotation array - used to minimise cost of
3616    * rebuild.
3617    */
3618   protected int _annotationScoreVectorHash;
3619
3620   /**
3621    * search the alignment and rebuild the sort by annotation score submenu the
3622    * last alignment annotation vector hash is stored to minimize cost of
3623    * rebuilding in subsequence calls.
3624    * 
3625    */
3626   public void buildSortByAnnotationScoresMenu()
3627   {
3628     if (viewport.alignment.getAlignmentAnnotation() == null)
3629     {
3630       return;
3631     }
3632
3633     if (viewport.alignment.getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
3634     {
3635       sortByAnnotScore.removeAll();
3636       // almost certainly a quicker way to do this - but we keep it simple
3637       Hashtable scoreSorts = new Hashtable();
3638       AlignmentAnnotation aann[];
3639       Enumeration sq = viewport.alignment.getSequences().elements();
3640       while (sq.hasMoreElements())
3641       {
3642         aann = ((SequenceI) sq.nextElement()).getAnnotation();
3643         for (int i = 0; aann != null && i < aann.length; i++)
3644         {
3645           if (aann[i].hasScore() && aann[i].sequenceRef != null)
3646           {
3647             scoreSorts.put(aann[i].label, aann[i].label);
3648           }
3649         }
3650       }
3651       Enumeration labels = scoreSorts.keys();
3652       while (labels.hasMoreElements())
3653       {
3654         addSortByAnnotScoreMenuItem(sortByAnnotScore,
3655                 (String) labels.nextElement());
3656       }
3657       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
3658       scoreSorts.clear();
3659
3660       _annotationScoreVectorHash = viewport.alignment
3661               .getAlignmentAnnotation().hashCode();
3662     }
3663   }
3664
3665   /**
3666    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
3667    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
3668    * call. Listeners are added to remove the menu item when the treePanel is
3669    * closed, and adjust the tree leaf to sequence mapping when the alignment is
3670    * modified.
3671    * 
3672    * @param treePanel
3673    *          Displayed tree window.
3674    * @param title
3675    *          SortBy menu item title.
3676    */
3677   public void buildTreeMenu()
3678   {
3679     sortByTreeMenu.removeAll();
3680
3681     Vector comps = (Vector) PaintRefresher.components.get(viewport
3682             .getSequenceSetId());
3683     Vector treePanels = new Vector();
3684     int i, iSize = comps.size();
3685     for (i = 0; i < iSize; i++)
3686     {
3687       if (comps.elementAt(i) instanceof TreePanel)
3688       {
3689         treePanels.add(comps.elementAt(i));
3690       }
3691     }
3692
3693     iSize = treePanels.size();
3694
3695     if (iSize < 1)
3696     {
3697       sortByTreeMenu.setVisible(false);
3698       return;
3699     }
3700
3701     sortByTreeMenu.setVisible(true);
3702
3703     for (i = 0; i < treePanels.size(); i++)
3704     {
3705       TreePanel tp = (TreePanel) treePanels.elementAt(i);
3706       final JMenuItem item = new JMenuItem(tp.getTitle());
3707       final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
3708       item.addActionListener(new java.awt.event.ActionListener()
3709       {
3710         public void actionPerformed(ActionEvent e)
3711         {
3712           SequenceI[] oldOrder = viewport.getAlignment()
3713                   .getSequencesArray();
3714           AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
3715
3716           addHistoryItem(new OrderCommand("Tree Sort", oldOrder,
3717                   viewport.alignment));
3718
3719           alignPanel.paintAlignment(true);
3720         }
3721       });
3722
3723       sortByTreeMenu.add(item);
3724     }
3725   }
3726
3727   public boolean sortBy(AlignmentOrder alorder, String undoname)
3728   {
3729     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3730     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
3731     if (undoname != null)
3732     {
3733       addHistoryItem(new OrderCommand(undoname, oldOrder,
3734               viewport.alignment));
3735     }
3736     alignPanel.paintAlignment(true);
3737     return true;
3738   }
3739
3740   /**
3741    * Work out whether the whole set of sequences or just the selected set will
3742    * be submitted for multiple alignment.
3743    * 
3744    */
3745   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
3746   {
3747     // Now, check we have enough sequences
3748     AlignmentView msa = null;
3749
3750     if ((viewport.getSelectionGroup() != null)
3751             && (viewport.getSelectionGroup().getSize() > 1))
3752     {
3753       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
3754       // some common interface!
3755       /*
3756        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
3757        * SequenceI[sz = seqs.getSize(false)];
3758        * 
3759        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
3760        * seqs.getSequenceAt(i); }
3761        */
3762       msa = viewport.getAlignmentView(true);
3763     }
3764     else
3765     {
3766       /*
3767        * Vector seqs = viewport.getAlignment().getSequences();
3768        * 
3769        * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
3770        * 
3771        * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
3772        * seqs.elementAt(i); } }
3773        */
3774       msa = viewport.getAlignmentView(false);
3775     }
3776     return msa;
3777   }
3778
3779   /**
3780    * Decides what is submitted to a secondary structure prediction service: the
3781    * first sequence in the alignment, or in the current selection, or, if the
3782    * alignment is 'aligned' (ie padded with gaps), then the currently selected
3783    * region or the whole alignment. (where the first sequence in the set is the
3784    * one that the prediction will be for).
3785    */
3786   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
3787   {
3788     AlignmentView seqs = null;
3789
3790     if ((viewport.getSelectionGroup() != null)
3791             && (viewport.getSelectionGroup().getSize() > 0))
3792     {
3793       seqs = viewport.getAlignmentView(true);
3794     }
3795     else
3796     {
3797       seqs = viewport.getAlignmentView(false);
3798     }
3799     // limit sequences - JBPNote in future - could spawn multiple prediction
3800     // jobs
3801     // TODO: viewport.alignment.isAligned is a global state - the local
3802     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
3803     if (!viewport.alignment.isAligned(false))
3804     {
3805       seqs.setSequences(new SeqCigar[]
3806       { seqs.getSequences()[0] });
3807       // TODO: if seqs.getSequences().length>1 then should really have warned
3808       // user!
3809
3810     }
3811     return seqs;
3812   }
3813
3814   /**
3815    * DOCUMENT ME!
3816    * 
3817    * @param e
3818    *          DOCUMENT ME!
3819    */
3820   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
3821   {
3822     // Pick the tree file
3823     JalviewFileChooser chooser = new JalviewFileChooser(
3824             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
3825     chooser.setFileView(new JalviewFileView());
3826     chooser.setDialogTitle("Select a newick-like tree file");
3827     chooser.setToolTipText("Load a tree file");
3828
3829     int value = chooser.showOpenDialog(null);
3830
3831     if (value == JalviewFileChooser.APPROVE_OPTION)
3832     {
3833       String choice = chooser.getSelectedFile().getPath();
3834       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
3835       jalview.io.NewickFile fin = null;
3836       try
3837       {
3838         fin = new jalview.io.NewickFile(choice, "File");
3839         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
3840       } catch (Exception ex)
3841       {
3842         JOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
3843                 "Problem reading tree file", JOptionPane.WARNING_MESSAGE);
3844         ex.printStackTrace();
3845       }
3846       if (fin != null && fin.hasWarningMessage())
3847       {
3848         JOptionPane.showMessageDialog(Desktop.desktop,
3849                 fin.getWarningMessage(), "Possible problem with tree file",
3850                 JOptionPane.WARNING_MESSAGE);
3851       }
3852     }
3853   }
3854
3855   public TreePanel ShowNewickTree(NewickFile nf, String title)
3856   {
3857     return ShowNewickTree(nf, title, 600, 500, 4, 5);
3858   }
3859
3860   public TreePanel ShowNewickTree(NewickFile nf, String title,
3861           AlignmentView input)
3862   {
3863     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
3864   }
3865
3866   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
3867           int h, int x, int y)
3868   {
3869     return ShowNewickTree(nf, title, null, w, h, x, y);
3870   }
3871
3872   /**
3873    * Add a treeviewer for the tree extracted from a newick file object to the
3874    * current alignment view
3875    * 
3876    * @param nf
3877    *          the tree
3878    * @param title
3879    *          tree viewer title
3880    * @param input
3881    *          Associated alignment input data (or null)
3882    * @param w
3883    *          width
3884    * @param h
3885    *          height
3886    * @param x
3887    *          position
3888    * @param y
3889    *          position
3890    * @return TreePanel handle
3891    */
3892   public TreePanel ShowNewickTree(NewickFile nf, String title,
3893           AlignmentView input, int w, int h, int x, int y)
3894   {
3895     TreePanel tp = null;
3896
3897     try
3898     {
3899       nf.parse();
3900
3901       if (nf.getTree() != null)
3902       {
3903         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
3904
3905         tp.setSize(w, h);
3906
3907         if (x > 0 && y > 0)
3908         {
3909           tp.setLocation(x, y);
3910         }
3911
3912         Desktop.addInternalFrame(tp, title, w, h);
3913       }
3914     } catch (Exception ex)
3915     {
3916       ex.printStackTrace();
3917     }
3918
3919     return tp;
3920   }
3921
3922   private boolean buildingMenu = false;
3923
3924   /**
3925    * Generates menu items and listener event actions for web service clients
3926    * 
3927    */
3928   public void BuildWebServiceMenu()
3929   {
3930     while (buildingMenu)
3931     {
3932       try
3933       {
3934         System.err.println("Waiting for building menu to finish.");
3935         Thread.sleep(10);
3936       } catch (Exception e)
3937       {
3938       }
3939       ;
3940     }
3941     final AlignFrame me = this;
3942     buildingMenu = true;
3943     new Thread(new Runnable()
3944     {
3945       public void run()
3946       {
3947         try
3948         {
3949           System.err.println("Building ws menu again "
3950                   + Thread.currentThread());
3951           // TODO: add support for context dependent disabling of services based
3952           // on
3953           // alignment and current selection
3954           // TODO: add additional serviceHandle parameter to specify abstract
3955           // handler
3956           // class independently of AbstractName
3957           // TODO: add in rediscovery GUI function to restart discoverer
3958           // TODO: group services by location as well as function and/or
3959           // introduce
3960           // object broker mechanism.
3961           final Vector wsmenu = new Vector();
3962           final IProgressIndicator af = me;
3963           if (Cache.getDefault("SHOW_JWS1_SERVICES", true)
3964                   && Discoverer.services != null
3965                   && (Discoverer.services.size() > 0))
3966           {
3967             // TODO: refactor to allow list of AbstractName/Handler bindings to
3968             // be
3969             // stored or retrieved from elsewhere
3970             Vector msaws = (Vector) Discoverer.services.get("MsaWS");
3971             Vector secstrpr = (Vector) Discoverer.services
3972                     .get("SecStrPred");
3973             Vector seqsrch = (Vector) Discoverer.services.get("SeqSearch");
3974             // TODO: move GUI generation code onto service implementation - so a
3975             // client instance attaches itself to the GUI with method call like
3976             // jalview.ws.MsaWSClient.bind(servicehandle, Desktop.instance,
3977             // alignframe)
3978             if (msaws != null)
3979             {
3980               // Add any Multiple Sequence Alignment Services
3981               final JMenu msawsmenu = new JMenu("Alignment");
3982               for (int i = 0, j = msaws.size(); i < j; i++)
3983               {
3984                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws
3985                         .get(i);
3986                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
3987                         .getServiceClient(sh);
3988                 impl.attachWSMenuEntry(msawsmenu, me);
3989
3990               }
3991               wsmenu.add(msawsmenu);
3992             }
3993             if (secstrpr != null)
3994             {
3995               // Add any secondary structure prediction services
3996               final JMenu secstrmenu = new JMenu(
3997                       "Secondary Structure Prediction");
3998               for (int i = 0, j = secstrpr.size(); i < j; i++)
3999               {
4000                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4001                         .get(i);
4002                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4003                         .getServiceClient(sh);
4004                 impl.attachWSMenuEntry(secstrmenu, me);
4005               }
4006               wsmenu.add(secstrmenu);
4007             }
4008             if (seqsrch != null)
4009             {
4010               // Add any sequence search services
4011               final JMenu seqsrchmenu = new JMenu(
4012                       "Sequence Database Search");
4013               for (int i = 0, j = seqsrch.size(); i < j; i++)
4014               {
4015                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch
4016                         .elementAt(i);
4017                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4018                         .getServiceClient(sh);
4019                 impl.attachWSMenuEntry(seqsrchmenu, me);
4020               }
4021               wsmenu.add(seqsrchmenu);
4022             }
4023           }
4024
4025           // TODO: move into separate menu builder class.
4026           if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4027           {
4028             Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4029             if (jws2servs != null)
4030             {
4031               if (jws2servs.hasServices())
4032               {
4033                 JMenu jws2men = new JMenu("Jaba Web Services");
4034                 jws2servs.attachWSMenuEntry(jws2men, me);
4035                 for (int i = 0, iSize = jws2men.getMenuComponentCount(); i < iSize; i++)
4036                 {
4037                   wsmenu.add(jws2men.getMenuComponent(i));
4038                 }
4039               }
4040             }
4041           }
4042
4043           javax.swing.SwingUtilities.invokeLater(new Runnable()
4044           {
4045             public void run()
4046             {
4047               try
4048               {
4049
4050                 resetWebServiceMenu();
4051                 // finally, add the whole shebang onto the webservices menu
4052                 if (wsmenu.size() > 0)
4053                 {
4054                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4055                   {
4056                     webService.add((JMenu) wsmenu.get(i));
4057                   }
4058                 }
4059                 else
4060                 {
4061                   me.webService.add(me.webServiceNoServices);
4062                 }
4063               } catch (Exception e)
4064               {
4065               }
4066               ;
4067             }
4068           });
4069         } catch (Exception e)
4070         {
4071         }
4072         ;
4073
4074         buildingMenu = false;
4075       }
4076     }).start();
4077
4078   }
4079
4080   /**
4081    * empty the web service menu and add any ad-hoc functions not dynamically
4082    * discovered.
4083    * 
4084    */
4085   private void resetWebServiceMenu()
4086   {
4087     webService.removeAll();
4088     build_fetchdbmenu(webService);
4089     build_urlServiceMenu(webService);
4090   }
4091
4092   /**
4093    * construct any groupURL type service menu entries.
4094    * 
4095    * @param webService
4096    */
4097   private void build_urlServiceMenu(JMenu webService)
4098   {
4099     if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
4100     {
4101       jalview.ws.EnfinEnvision2OneWay.getInstance().attachWSMenuEntry(
4102               webService, this);
4103     }
4104     // TODO: remove this code when 2.7 is released
4105     // DEBUG - alignmentView
4106     /*
4107      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4108      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4109      * 
4110      * @Override public void actionPerformed(ActionEvent e) {
4111      * jalview.datamodel.AlignmentView.testSelectionViews(af.viewport.alignment,
4112      * af.viewport.colSel, af.viewport.selectionGroup); }
4113      * 
4114      * }); webService.add(testAlView);
4115      */
4116     // TODO: refactor to RestClient discoverer and merge menu entries for
4117     // rest-style services with other types of analysis/calculation service
4118     // SHmmr test client - still being implemented.
4119     jalview.ws.rest.RestClient.makeShmmrRestClient().attachWSMenuEntry(
4120             webService, this);
4121     // DEBUG - alignmentView
4122   }
4123
4124   /*
4125    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4126    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4127    * getProperty("LAST_DIRECTORY"));
4128    * 
4129    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4130    * to Vamsas file"); chooser.setToolTipText("Export");
4131    * 
4132    * int value = chooser.showSaveDialog(this);
4133    * 
4134    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4135    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4136    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4137    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4138    */
4139   /**
4140    * prototype of an automatically enabled/disabled analysis function
4141    * 
4142    */
4143   protected void setShowProductsEnabled()
4144   {
4145     SequenceI[] selection = viewport.getSequenceSelection();
4146     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4147             viewport.getAlignment().getDataset()))
4148     {
4149       showProducts.setEnabled(true);
4150
4151     }
4152     else
4153     {
4154       showProducts.setEnabled(false);
4155     }
4156   }
4157
4158   /**
4159    * search selection for sequence xRef products and build the show products
4160    * menu.
4161    * 
4162    * @param selection
4163    * @param dataset
4164    * @return true if showProducts menu should be enabled.
4165    */
4166   public boolean canShowProducts(SequenceI[] selection,
4167           boolean isRegionSelection, Alignment dataset)
4168   {
4169     boolean showp = false;
4170     try
4171     {
4172       showProducts.removeAll();
4173       final boolean dna = viewport.getAlignment().isNucleotide();
4174       final Alignment ds = dataset;
4175       String[] ptypes = (selection == null || selection.length == 0) ? null
4176               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4177       // Object[] prods =
4178       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4179       // selection, dataset, true);
4180       final SequenceI[] sel = selection;
4181       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4182       {
4183         showp = true;
4184         final boolean isRegSel = isRegionSelection;
4185         final AlignFrame af = this;
4186         final String source = ptypes[t];
4187         JMenuItem xtype = new JMenuItem(ptypes[t]);
4188         xtype.addActionListener(new ActionListener()
4189         {
4190
4191           public void actionPerformed(ActionEvent e)
4192           {
4193             // TODO: new thread for this call with vis-delay
4194             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
4195                     isRegSel, dna, source);
4196           }
4197
4198         });
4199         showProducts.add(xtype);
4200       }
4201       showProducts.setVisible(showp);
4202       showProducts.setEnabled(showp);
4203     } catch (Exception e)
4204     {
4205       jalview.bin.Cache.log
4206               .warn("canTranslate threw an exception - please report to help@jalview.org",
4207                       e);
4208       return false;
4209     }
4210     return showp;
4211   }
4212
4213   protected void showProductsFor(SequenceI[] sel, Alignment ds,
4214           boolean isRegSel, boolean dna, String source)
4215   {
4216     final boolean fisRegSel = isRegSel;
4217     final boolean fdna = dna;
4218     final String fsrc = source;
4219     final AlignFrame ths = this;
4220     final SequenceI[] fsel = sel;
4221     Runnable foo = new Runnable()
4222     {
4223
4224       public void run()
4225       {
4226         final long sttime = System.currentTimeMillis();
4227         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
4228         try
4229         {
4230           Alignment ds = ths.getViewport().alignment.getDataset(); // update
4231           // our local
4232           // dataset
4233           // reference
4234           Alignment prods = CrossRef
4235                   .findXrefSequences(fsel, fdna, fsrc, ds);
4236           if (prods != null)
4237           {
4238             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4239             for (int s = 0; s < sprods.length; s++)
4240             {
4241               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4242               if (ds.getSequences() == null
4243                       || !ds.getSequences().contains(
4244                               sprods[s].getDatasetSequence()))
4245                 ds.addSequence(sprods[s].getDatasetSequence());
4246               sprods[s].updatePDBIds();
4247             }
4248             Alignment al = new Alignment(sprods);
4249             AlignedCodonFrame[] cf = prods.getCodonFrames();
4250             al.setDataset(ds);
4251             for (int s = 0; cf != null && s < cf.length; s++)
4252             {
4253               al.addCodonFrame(cf[s]);
4254               cf[s] = null;
4255             }
4256             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4257                     DEFAULT_HEIGHT);
4258             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
4259                     + " for " + ((fisRegSel) ? "selected region of " : "")
4260                     + getTitle();
4261             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4262                     DEFAULT_HEIGHT);
4263           }
4264           else
4265           {
4266             System.err.println("No Sequences generated for xRef type "
4267                     + fsrc);
4268           }
4269         } catch (Exception e)
4270         {
4271           jalview.bin.Cache.log.error(
4272                   "Exception when finding crossreferences", e);
4273         } catch (OutOfMemoryError e)
4274         {
4275           new OOMWarning("whilst fetching crossreferences", e);
4276         } catch (Error e)
4277         {
4278           jalview.bin.Cache.log.error("Error when finding crossreferences",
4279                   e);
4280         }
4281         ths.setProgressBar("Finished searching for sequences from " + fsrc,
4282                 sttime);
4283       }
4284
4285     };
4286     Thread frunner = new Thread(foo);
4287     frunner.start();
4288   }
4289
4290   public boolean canShowTranslationProducts(SequenceI[] selection,
4291           AlignmentI alignment)
4292   {
4293     // old way
4294     try
4295     {
4296       return (jalview.analysis.Dna.canTranslate(selection,
4297               viewport.getViewAsVisibleContigs(true)));
4298     } catch (Exception e)
4299     {
4300       jalview.bin.Cache.log
4301               .warn("canTranslate threw an exception - please report to help@jalview.org",
4302                       e);
4303       return false;
4304     }
4305   }
4306
4307   public void showProducts_actionPerformed(ActionEvent e)
4308   {
4309     // /////////////////////////////
4310     // Collect Data to be translated/transferred
4311
4312     SequenceI[] selection = viewport.getSequenceSelection();
4313     AlignmentI al = null;
4314     try
4315     {
4316       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
4317               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
4318               viewport.getAlignment().getDataset());
4319     } catch (Exception ex)
4320     {
4321       al = null;
4322       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4323     }
4324     if (al == null)
4325     {
4326       JOptionPane
4327               .showMessageDialog(
4328                       Desktop.desktop,
4329                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4330                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4331     }
4332     else
4333     {
4334       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4335       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4336               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4337     }
4338   }
4339
4340   public void showTranslation_actionPerformed(ActionEvent e)
4341   {
4342     // /////////////////////////////
4343     // Collect Data to be translated/transferred
4344
4345     SequenceI[] selection = viewport.getSequenceSelection();
4346     String[] seqstring = viewport.getViewAsString(true);
4347     AlignmentI al = null;
4348     try
4349     {
4350       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
4351               viewport.getViewAsVisibleContigs(true), viewport
4352                       .getGapCharacter(), viewport.alignment
4353                       .getAlignmentAnnotation(), viewport.alignment
4354                       .getWidth(), viewport.getAlignment().getDataset());
4355     } catch (Exception ex)
4356     {
4357       al = null;
4358       jalview.bin.Cache.log.debug("Exception during translation.", ex);
4359     }
4360     if (al == null)
4361     {
4362       JOptionPane
4363               .showMessageDialog(
4364                       Desktop.desktop,
4365                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
4366                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
4367     }
4368     else
4369     {
4370       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4371       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
4372               DEFAULT_WIDTH, DEFAULT_HEIGHT);
4373     }
4374   }
4375
4376   /**
4377    * Try to load a features file onto the alignment.
4378    * 
4379    * @param file
4380    *          contents or path to retrieve file
4381    * @param type
4382    *          access mode of file (see jalview.io.AlignFile)
4383    * @return true if features file was parsed corectly.
4384    */
4385   public boolean parseFeaturesFile(String file, String type)
4386   {
4387     boolean featuresFile = false;
4388     try
4389     {
4390       featuresFile = new FeaturesFile(file, type)
4391               .parse(viewport.alignment.getDataset(),
4392                       alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureColours,
4393                       false, jalview.bin.Cache.getDefault(
4394                               "RELAXEDSEQIDMATCHING", false));
4395     } catch (Exception ex)
4396     {
4397       ex.printStackTrace();
4398     }
4399
4400     if (featuresFile)
4401     {
4402       viewport.showSequenceFeatures = true;
4403       showSeqFeatures.setSelected(true);
4404       if (alignPanel.seqPanel.seqCanvas.fr != null)
4405       {
4406         // update the min/max ranges where necessary
4407         alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
4408       }
4409       if (featureSettings != null)
4410       {
4411         featureSettings.setTableData();
4412       }
4413       alignPanel.paintAlignment(true);
4414     }
4415
4416     return featuresFile;
4417   }
4418
4419   public void dragEnter(DropTargetDragEvent evt)
4420   {
4421   }
4422
4423   public void dragExit(DropTargetEvent evt)
4424   {
4425   }
4426
4427   public void dragOver(DropTargetDragEvent evt)
4428   {
4429   }
4430
4431   public void dropActionChanged(DropTargetDragEvent evt)
4432   {
4433   }
4434
4435   public void drop(DropTargetDropEvent evt)
4436   {
4437     Transferable t = evt.getTransferable();
4438     java.util.List files = null;
4439
4440     try
4441     {
4442       DataFlavor uriListFlavor = new DataFlavor(
4443               "text/uri-list;class=java.lang.String");
4444       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
4445       {
4446         // Works on Windows and MacOSX
4447         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4448         files = (java.util.List) t
4449                 .getTransferData(DataFlavor.javaFileListFlavor);
4450       }
4451       else if (t.isDataFlavorSupported(uriListFlavor))
4452       {
4453         // This is used by Unix drag system
4454         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
4455         String data = (String) t.getTransferData(uriListFlavor);
4456         files = new java.util.ArrayList(1);
4457         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
4458                 data, "\r\n"); st.hasMoreTokens();)
4459         {
4460           String s = st.nextToken();
4461           if (s.startsWith("#"))
4462           {
4463             // the line is a comment (as per the RFC 2483)
4464             continue;
4465           }
4466
4467           java.net.URI uri = new java.net.URI(s);
4468           // check to see if we can handle this kind of URI
4469           if (uri.getScheme().toLowerCase().startsWith("http"))
4470           {
4471             files.add(uri.toString());
4472           }
4473           else
4474           {
4475             // otherwise preserve old behaviour: catch all for file objects
4476             java.io.File file = new java.io.File(uri);
4477             files.add(file.toString());
4478           }
4479         }
4480       }
4481     } catch (Exception e)
4482     {
4483       e.printStackTrace();
4484     }
4485     if (files != null)
4486     {
4487       try
4488       {
4489         // check to see if any of these files have names matching sequences in
4490         // the alignment
4491         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4492                 .getAlignment().getSequencesArray());
4493         /**
4494          * Object[] { String,SequenceI}
4495          */
4496         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
4497         ArrayList<String> filesnotmatched = new ArrayList<String>();
4498         for (int i = 0; i < files.size(); i++)
4499         {
4500           String file = files.get(i).toString();
4501           String pdbfn = "";
4502           String protocol = FormatAdapter.checkProtocol(file);
4503           if (protocol == jalview.io.FormatAdapter.FILE)
4504           {
4505             File fl = new File(file);
4506             pdbfn = fl.getName();
4507           }
4508           else if (protocol == jalview.io.FormatAdapter.URL)
4509           {
4510             URL url = new URL(file);
4511             pdbfn = url.getFile();
4512           }
4513           if (pdbfn.length() > 0)
4514           {
4515             // attempt to find a match in the alignment
4516             SequenceI mtch = idm.findIdMatch(pdbfn);
4517             int l = 0, c = pdbfn.indexOf(".");
4518             while (mtch == null && c != -1)
4519             {
4520               do 
4521               {
4522                 l = c;
4523               } while ((c = pdbfn.indexOf(".", l)) > l);
4524               if (l > -1)
4525               {
4526                 pdbfn = pdbfn.substring(0, l);
4527               }
4528               mtch = idm.findIdMatch(pdbfn);
4529             }
4530             if (mtch != null)
4531             {
4532               String type = null;
4533               try
4534               {
4535                 type = new IdentifyFile().Identify(file, protocol);
4536               } catch (Exception ex)
4537               {
4538                 type = null;
4539               }
4540               if (type != null)
4541               {
4542                 if (type.equalsIgnoreCase("PDB"))
4543                 {
4544                   filesmatched.add(new Object[]
4545                   { file, protocol, mtch });
4546                   continue;
4547                 }
4548               }
4549             }
4550             // File wasn't named like one of the sequences or wasn't a PDB file.
4551             filesnotmatched.add(file);
4552           }
4553         }
4554         int assocfiles = 0;
4555         if (filesmatched.size() > 0)
4556         {
4557           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
4558                   || JOptionPane
4559                           .showConfirmDialog(
4560                                   this,
4561                                   "Do you want to automatically associate the "
4562                                           + filesmatched.size()
4563                                           + " PDB files with sequences in the alignment that have the same name ?",
4564                                   "Automatically Associate PDB files by name",
4565                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
4566
4567           {
4568             for (Object[] fm : filesmatched)
4569             {
4570               // try and associate
4571               // TODO: may want to set a standard ID naming formalism for
4572               // associating PDB files which have no IDs.
4573               PDBEntry pe = new AssociatePdbFileWithSeq()
4574                       .associatePdbWithSeq((String) fm[0], (String) fm[1],
4575                               (SequenceI) fm[2], false);
4576               if (pe != null)
4577               {
4578                 System.err
4579                         .println("Associated file : " + ((String) fm[0])
4580                                 + " with "
4581                                 + ((SequenceI) fm[2]).getDisplayId(true));
4582                 assocfiles++;
4583               }
4584               alignPanel.paintAlignment(true);
4585             }
4586           }
4587         }
4588         if (filesnotmatched.size() > 0)
4589         {
4590           if (assocfiles > 0
4591                   && (Cache.getDefault(
4592                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
4593                           .showConfirmDialog(
4594                                   this,
4595                                   "<html>Do you want to <em>ignore</em> the "
4596                                           + filesnotmatched.size()
4597                                           + " files whose names did not match any sequence IDs ?</html>",
4598                                   "Ignore unmatched dropped files ?",
4599                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
4600           {
4601             return;
4602           }
4603           for (String fn : filesnotmatched)
4604           {
4605             loadJalviewDataFile(fn, null, null, null);
4606           }
4607
4608         }
4609       } catch (Exception ex)
4610       {
4611         ex.printStackTrace();
4612       }
4613     }
4614   }
4615
4616   /**
4617    * Attempt to load a "dropped" file or URL string: First by testing whether
4618    * it's and Annotation file, then a JNet file, and finally a features file. If
4619    * all are false then the user may have dropped an alignment file onto this
4620    * AlignFrame.
4621    * 
4622    * @param file
4623    *          either a filename or a URL string.
4624    */
4625   public void loadJalviewDataFile(String file, String protocol,
4626           String format, SequenceI assocSeq)
4627   {
4628     try
4629     {
4630       if (protocol == null)
4631       {
4632         protocol = jalview.io.FormatAdapter.checkProtocol(file);
4633       }
4634       // if the file isn't identified, or not positively identified as some
4635       // other filetype (PFAM is default unidentified alignment file type) then
4636       // try to parse as annotation.
4637       boolean isAnnotation = (format == null || format
4638               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
4639               .readAnnotationFile(viewport.alignment, file, protocol)
4640               : false;
4641
4642       if (!isAnnotation)
4643       {
4644         // try to see if its a JNet 'concise' style annotation file *before* we
4645         // try to parse it as a features file
4646         if (format == null)
4647         {
4648           format = new IdentifyFile().Identify(file, protocol);
4649         }
4650         if (format.equalsIgnoreCase("JnetFile"))
4651         {
4652           jalview.io.JPredFile predictions = new jalview.io.JPredFile(file,
4653                   protocol);
4654           new JnetAnnotationMaker().add_annotation(predictions,
4655                   viewport.getAlignment(), 0, false);
4656           isAnnotation = true;
4657         }
4658         else
4659         {
4660           /*
4661            * if (format.equalsIgnoreCase("PDB")) {
4662            * 
4663            * String pdbfn = ""; // try to match up filename with sequence id try
4664            * { if (protocol == jalview.io.FormatAdapter.FILE) { File fl = new
4665            * File(file); pdbfn = fl.getName(); } else if (protocol ==
4666            * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
4667            * url.getFile(); } } catch (Exception e) { } ; if (assocSeq == null)
4668            * { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
4669            * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) { //
4670            * attempt to find a match in the alignment SequenceI mtch =
4671            * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
4672            * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) > l)
4673            * { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch =
4674            * idm.findIdMatch(pdbfn); } if (mtch != null) { // try and associate
4675            * // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
4676            * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null) {
4677            * System.err.println("Associated file : " + file + " with " +
4678            * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
4679            * TODO: maybe need to load as normal otherwise return; } }
4680            */
4681           // try to parse it as a features file
4682           boolean isGroupsFile = parseFeaturesFile(file, protocol);
4683           // if it wasn't a features file then we just treat it as a general
4684           // alignment file to load into the current view.
4685           if (!isGroupsFile)
4686           {
4687             new FileLoader().LoadFile(viewport, file, protocol, format);
4688           }
4689           else
4690           {
4691             alignPanel.paintAlignment(true);
4692           }
4693         }
4694       }
4695       if (isAnnotation)
4696       {
4697
4698         alignPanel.adjustAnnotationHeight();
4699         viewport.updateSequenceIdColours();
4700         buildSortByAnnotationScoresMenu();
4701         alignPanel.paintAlignment(true);
4702       }
4703     } catch (Exception ex)
4704     {
4705       ex.printStackTrace();
4706     }
4707   }
4708
4709   public void tabSelectionChanged(int index)
4710   {
4711     if (index > -1)
4712     {
4713       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
4714       viewport = alignPanel.av;
4715       setMenusFromViewport(viewport);
4716     }
4717   }
4718
4719   public void tabbedPane_mousePressed(MouseEvent e)
4720   {
4721     if (SwingUtilities.isRightMouseButton(e))
4722     {
4723       String reply = JOptionPane.showInternalInputDialog(this,
4724               "Enter View Name", "Edit View Name",
4725               JOptionPane.QUESTION_MESSAGE);
4726
4727       if (reply != null)
4728       {
4729         viewport.viewName = reply;
4730         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
4731       }
4732     }
4733   }
4734
4735   public AlignViewport getCurrentView()
4736   {
4737     return viewport;
4738   }
4739
4740   /**
4741    * Open the dialog for regex description parsing.
4742    */
4743   protected void extractScores_actionPerformed(ActionEvent e)
4744   {
4745     ParseProperties pp = new jalview.analysis.ParseProperties(
4746             viewport.alignment);
4747     // TODO: verify regex and introduce GUI dialog for version 2.5
4748     // if (pp.getScoresFromDescription("col", "score column ",
4749     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
4750     // true)>0)
4751     if (pp.getScoresFromDescription("description column",
4752             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
4753     {
4754       buildSortByAnnotationScoresMenu();
4755     }
4756   }
4757
4758   /*
4759    * (non-Javadoc)
4760    * 
4761    * @see
4762    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
4763    * )
4764    */
4765   protected void showDbRefs_actionPerformed(ActionEvent e)
4766   {
4767     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
4768   }
4769
4770   /*
4771    * (non-Javadoc)
4772    * 
4773    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
4774    * ActionEvent)
4775    */
4776   protected void showNpFeats_actionPerformed(ActionEvent e)
4777   {
4778     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
4779   }
4780
4781   /**
4782    * find the viewport amongst the tabs in this alignment frame and close that
4783    * tab
4784    * 
4785    * @param av
4786    */
4787   public boolean closeView(AlignViewport av)
4788   {
4789     if (viewport == av)
4790     {
4791       this.closeMenuItem_actionPerformed(false);
4792       return true;
4793     }
4794     Component[] comp = tabbedPane.getComponents();
4795     for (int i = 0; comp != null && i < comp.length; i++)
4796     {
4797       if (comp[i] instanceof AlignmentPanel)
4798       {
4799         if (((AlignmentPanel) comp[i]).av == av)
4800         {
4801           // close the view.
4802           closeView((AlignmentPanel) comp[i]);
4803           return true;
4804         }
4805       }
4806     }
4807     return false;
4808   }
4809
4810   protected void build_fetchdbmenu(JMenu webService)
4811   {
4812     // Temporary hack - DBRef Fetcher always top level ws entry.
4813     // TODO We probably want to store a sequence database checklist in
4814     // preferences and have checkboxes.. rather than individual sources selected
4815     // here
4816     final JMenu rfetch = new JMenu("Fetch DB References");
4817     rfetch.setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
4818     webService.add(rfetch);
4819
4820     JMenuItem fetchr = new JMenuItem("Standard Databases");
4821     fetchr.setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
4822     fetchr.addActionListener(new ActionListener()
4823     {
4824
4825       public void actionPerformed(ActionEvent e)
4826       {
4827         new Thread(new Runnable()
4828         {
4829
4830           public void run()
4831           {
4832             new jalview.ws.DBRefFetcher(alignPanel.av
4833                     .getSequenceSelection(), alignPanel.alignFrame)
4834                     .fetchDBRefs(false);
4835           }
4836         }).start();
4837
4838       }
4839
4840     });
4841     rfetch.add(fetchr);
4842     final AlignFrame me = this;
4843     new Thread(new Runnable()
4844     {
4845       public void run()
4846       {
4847         final jalview.ws.SequenceFetcher sf = SequenceFetcher
4848                 .getSequenceFetcherSingleton(me);
4849         final String[] otherdb = sf.getOrderedSupportedSources();
4850         // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
4851         // jalview.util.QuickSort.sort(otherdb, otherdb);
4852         javax.swing.SwingUtilities.invokeLater(new Runnable()
4853         {
4854           public void run()
4855           {
4856
4857             JMenu dfetch = new JMenu();
4858             JMenuItem fetchr;
4859             rfetch.add(dfetch);
4860             int comp = 0, mcomp = 15;
4861             String mname = null;
4862             if (otherdb != null && otherdb.length > 0)
4863             {
4864               for (int i = 0; i < otherdb.length; i++)
4865               {
4866                 String dbname = sf.getSourceProxy(otherdb[i]).getDbName();
4867                 if (mname == null)
4868                 {
4869                   mname = "from '" + dbname + "'";
4870                 }
4871                 fetchr = new JMenuItem(otherdb[i]);
4872                 final String[] dassource = new String[]
4873                 { otherdb[i] };
4874                 fetchr.addActionListener(new ActionListener()
4875                 {
4876
4877                   public void actionPerformed(ActionEvent e)
4878                   {
4879                     new Thread(new Runnable()
4880                     {
4881
4882                       public void run()
4883                       {
4884                         new jalview.ws.DBRefFetcher(alignPanel.av
4885                                 .getSequenceSelection(),
4886                                 alignPanel.alignFrame, dassource)
4887                                 .fetchDBRefs(false);
4888                       }
4889                     }).start();
4890                   }
4891
4892                 });
4893                 fetchr.setToolTipText("Retrieve from " + dbname);
4894                 dfetch.add(fetchr);
4895                 if (comp++ == mcomp || i == (otherdb.length - 1))
4896                 {
4897                   dfetch.setText(mname + " to '" + dbname + "'");
4898                   rfetch.add(dfetch);
4899                   dfetch = new JMenu();
4900                   mname = null;
4901                   comp = 0;
4902                 }
4903               }
4904             }
4905           }
4906         });
4907       }
4908     }).start();
4909
4910   }
4911
4912   /**
4913    * Left justify the whole alignment.
4914    */
4915   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
4916   {
4917     AlignmentI al = viewport.getAlignment();
4918     al.justify(false);
4919     viewport.firePropertyChange("alignment", null, al);
4920   }
4921
4922   /**
4923    * Right justify the whole alignment.
4924    */
4925   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
4926   {
4927     AlignmentI al = viewport.getAlignment();
4928     al.justify(true);
4929     viewport.firePropertyChange("alignment", null, al);
4930   }
4931
4932   public void setShowSeqFeatures(boolean b)
4933   {
4934     showSeqFeatures.setSelected(true);
4935     viewport.setShowSequenceFeatures(true);
4936   }
4937
4938   /*
4939    * (non-Javadoc)
4940    * 
4941    * @see
4942    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
4943    * awt.event.ActionEvent)
4944    */
4945   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
4946   {
4947     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
4948     alignPanel.paintAlignment(true);
4949   }
4950
4951   /*
4952    * (non-Javadoc)
4953    * 
4954    * @see
4955    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
4956    * .ActionEvent)
4957    */
4958   protected void showGroupConsensus_actionPerformed(ActionEvent e)
4959   {
4960     viewport.setShowGroupConsensus(showGroupConsensus.getState());
4961     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4962
4963   }
4964
4965   /*
4966    * (non-Javadoc)
4967    * 
4968    * @see
4969    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
4970    * .event.ActionEvent)
4971    */
4972   protected void showGroupConservation_actionPerformed(ActionEvent e)
4973   {
4974     viewport.setShowGroupConservation(showGroupConservation.getState());
4975     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4976   }
4977
4978   /*
4979    * (non-Javadoc)
4980    * 
4981    * @see
4982    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
4983    * .event.ActionEvent)
4984    */
4985   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
4986   {
4987     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
4988     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
4989   }
4990
4991   /*
4992    * (non-Javadoc)
4993    * 
4994    * @see
4995    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
4996    * .event.ActionEvent)
4997    */
4998   protected void showSequenceLogo_actionPerformed(ActionEvent e)
4999   {
5000     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5001     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5002   }
5003
5004   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5005   {
5006     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5007   }
5008
5009   /*
5010    * (non-Javadoc)
5011    * 
5012    * @see
5013    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5014    * .event.ActionEvent)
5015    */
5016   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5017   {
5018     if (viewport.getSelectionGroup() != null)
5019     {
5020       SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
5021               viewport.getSequenceSelection(),
5022               viewport.getAlignmentView(true).getSequenceStrings(
5023                       viewport.getGapCharacter()),
5024               viewport.alignment.getGroups());
5025       viewport.alignment.deleteAllGroups();
5026       viewport.sequenceColours = null;
5027       viewport.setSelectionGroup(null);
5028       // set view properties for each group
5029       for (int g = 0; g < gps.length; g++)
5030       {
5031         gps[g].setShowNonconserved(viewport.getShowUnconserved());
5032         gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
5033         viewport.alignment.addGroup(gps[g]);
5034         Color col = new Color((int) (Math.random() * 255),
5035                 (int) (Math.random() * 255), (int) (Math.random() * 255));
5036         col = col.brighter();
5037         for (Enumeration sq = gps[g].getSequences(null).elements(); sq
5038                 .hasMoreElements(); viewport.setSequenceColour(
5039                 (SequenceI) sq.nextElement(), col))
5040           ;
5041       }
5042       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5043       alignPanel.updateAnnotation();
5044       alignPanel.paintAlignment(true);
5045     }
5046   }
5047
5048   /**
5049    * make the given alignmentPanel the currently selected tab
5050    * 
5051    * @param alignmentPanel
5052    */
5053   public void setDisplayedView(AlignmentPanel alignmentPanel)
5054   {
5055     if (!viewport.getSequenceSetId().equals(
5056             alignmentPanel.av.getSequenceSetId()))
5057     {
5058       throw new Error(
5059               "Implementation error: cannot show a view from another alignment in an AlignFrame.");
5060     }
5061     if (tabbedPane != null
5062             & alignPanels.indexOf(alignmentPanel) != tabbedPane.getSelectedIndex())
5063     {
5064       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5065     }
5066   }
5067 }
5068
5069 class PrintThread extends Thread
5070 {
5071   AlignmentPanel ap;
5072
5073   public PrintThread(AlignmentPanel ap)
5074   {
5075     this.ap = ap;
5076   }
5077
5078   static PageFormat pf;
5079
5080   public void run()
5081   {
5082     PrinterJob printJob = PrinterJob.getPrinterJob();
5083
5084     if (pf != null)
5085     {
5086       printJob.setPrintable(ap, pf);
5087     }
5088     else
5089     {
5090       printJob.setPrintable(ap);
5091     }
5092
5093     if (printJob.printDialog())
5094     {
5095       try
5096       {
5097         printJob.print();
5098       } catch (Exception PrintException)
5099       {
5100         PrintException.printStackTrace();
5101       }
5102     }
5103   }
5104 }