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