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