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