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