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