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