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