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