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