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