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