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