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