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