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