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