JAL-1780 more support for export setting for flatfile output
[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.SplitContainerI;
83 import jalview.api.ViewStyleI;
84 import jalview.api.analysis.ScoreModelI;
85 import jalview.bin.Cache;
86 import jalview.commands.CommandI;
87 import jalview.commands.EditCommand;
88 import jalview.commands.EditCommand.Action;
89 import jalview.commands.OrderCommand;
90 import jalview.commands.RemoveGapColCommand;
91 import jalview.commands.RemoveGapsCommand;
92 import jalview.commands.SlideSequencesCommand;
93 import jalview.commands.TrimRegionCommand;
94 import jalview.datamodel.AlignedCodonFrame;
95 import jalview.datamodel.Alignment;
96 import jalview.datamodel.AlignmentAnnotation;
97 import jalview.datamodel.AlignmentI;
98 import jalview.datamodel.AlignmentOrder;
99 import jalview.datamodel.AlignmentView;
100 import jalview.datamodel.ColumnSelection;
101 import jalview.datamodel.HiddenSequences;
102 import jalview.datamodel.PDBEntry;
103 import jalview.datamodel.SeqCigar;
104 import jalview.datamodel.Sequence;
105 import jalview.datamodel.SequenceGroup;
106 import jalview.datamodel.SequenceI;
107 import jalview.gui.ViewSelectionMenu.ViewSetProvider;
108 import jalview.io.AlignmentProperties;
109 import jalview.io.AnnotationFile;
110 import jalview.io.BioJsHTMLOutput;
111 import jalview.io.FeaturesFile;
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 void featureSettings_actionPerformed(ActionEvent e)
3157   {
3158     if (featureSettings != null)
3159     {
3160       featureSettings.close();
3161       featureSettings = null;
3162     }
3163     if (!showSeqFeatures.isSelected())
3164     {
3165       // make sure features are actually displayed
3166       showSeqFeatures.setSelected(true);
3167       showSeqFeatures_actionPerformed(null);
3168     }
3169     featureSettings = new FeatureSettings(this);
3170   }
3171
3172   /**
3173    * Set or clear 'Show Sequence Features'
3174    * 
3175    * @param evt
3176    *          DOCUMENT ME!
3177    */
3178   @Override
3179   public void showSeqFeatures_actionPerformed(ActionEvent evt)
3180   {
3181     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
3182     alignPanel.paintAlignment(true);
3183     if (alignPanel.getOverviewPanel() != null)
3184     {
3185       alignPanel.getOverviewPanel().updateOverviewImage();
3186     }
3187   }
3188
3189   /**
3190    * Set or clear 'Show Sequence Features'
3191    * 
3192    * @param evt
3193    *          DOCUMENT ME!
3194    */
3195   @Override
3196   public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
3197   {
3198     viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
3199             .isSelected());
3200     if (viewport.isShowSequenceFeaturesHeight())
3201     {
3202       // ensure we're actually displaying features
3203       viewport.setShowSequenceFeatures(true);
3204       showSeqFeatures.setSelected(true);
3205     }
3206     alignPanel.paintAlignment(true);
3207     if (alignPanel.getOverviewPanel() != null)
3208     {
3209       alignPanel.getOverviewPanel().updateOverviewImage();
3210     }
3211   }
3212
3213   /**
3214    * Action on toggle of the 'Show annotations' menu item. This shows or hides
3215    * the annotations panel as a whole.
3216    * 
3217    * The options to show/hide all annotations should be enabled when the panel
3218    * is shown, and disabled when the panel is hidden.
3219    * 
3220    * @param e
3221    */
3222   @Override
3223   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
3224   {
3225     final boolean setVisible = annotationPanelMenuItem.isSelected();
3226     viewport.setShowAnnotation(setVisible);
3227     this.showAllSeqAnnotations.setEnabled(setVisible);
3228     this.hideAllSeqAnnotations.setEnabled(setVisible);
3229     this.showAllAlAnnotations.setEnabled(setVisible);
3230     this.hideAllAlAnnotations.setEnabled(setVisible);
3231     alignPanel.updateLayout();
3232   }
3233
3234   @Override
3235   public void alignmentProperties()
3236   {
3237     JEditorPane editPane = new JEditorPane("text/html", "");
3238     editPane.setEditable(false);
3239     StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
3240             .formatAsHtml();
3241     editPane.setText(MessageManager.formatMessage("label.html_content",
3242             new Object[]
3243             { contents.toString() }));
3244     JInternalFrame frame = new JInternalFrame();
3245     frame.getContentPane().add(new JScrollPane(editPane));
3246
3247     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3248             "label.alignment_properties", new Object[]
3249             { getTitle() }), 500, 400);
3250   }
3251
3252   /**
3253    * DOCUMENT ME!
3254    * 
3255    * @param e
3256    *          DOCUMENT ME!
3257    */
3258   @Override
3259   public void overviewMenuItem_actionPerformed(ActionEvent e)
3260   {
3261     if (alignPanel.overviewPanel != null)
3262     {
3263       return;
3264     }
3265
3266     JInternalFrame frame = new JInternalFrame();
3267     OverviewPanel overview = new OverviewPanel(alignPanel);
3268     frame.setContentPane(overview);
3269     Desktop.addInternalFrame(frame, MessageManager.formatMessage(
3270             "label.overview_params", new Object[]
3271             { this.getTitle() }), frame.getWidth(), frame.getHeight());
3272     frame.pack();
3273     frame.setLayer(JLayeredPane.PALETTE_LAYER);
3274     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
3275     {
3276       @Override
3277       public void internalFrameClosed(
3278               javax.swing.event.InternalFrameEvent evt)
3279       {
3280         alignPanel.setOverviewPanel(null);
3281       };
3282     });
3283
3284     alignPanel.setOverviewPanel(overview);
3285   }
3286
3287   @Override
3288   public void textColour_actionPerformed(ActionEvent e)
3289   {
3290     new TextColourChooser().chooseColour(alignPanel, null);
3291   }
3292
3293   /**
3294    * DOCUMENT ME!
3295    * 
3296    * @param e
3297    *          DOCUMENT ME!
3298    */
3299   @Override
3300   protected void noColourmenuItem_actionPerformed(ActionEvent e)
3301   {
3302     changeColour(null);
3303   }
3304
3305   /**
3306    * DOCUMENT ME!
3307    * 
3308    * @param e
3309    *          DOCUMENT ME!
3310    */
3311   @Override
3312   public void clustalColour_actionPerformed(ActionEvent e)
3313   {
3314     changeColour(new ClustalxColourScheme(viewport.getAlignment(),
3315             viewport.getHiddenRepSequences()));
3316   }
3317
3318   /**
3319    * DOCUMENT ME!
3320    * 
3321    * @param e
3322    *          DOCUMENT ME!
3323    */
3324   @Override
3325   public void zappoColour_actionPerformed(ActionEvent e)
3326   {
3327     changeColour(new ZappoColourScheme());
3328   }
3329
3330   /**
3331    * DOCUMENT ME!
3332    * 
3333    * @param e
3334    *          DOCUMENT ME!
3335    */
3336   @Override
3337   public void taylorColour_actionPerformed(ActionEvent e)
3338   {
3339     changeColour(new TaylorColourScheme());
3340   }
3341
3342   /**
3343    * DOCUMENT ME!
3344    * 
3345    * @param e
3346    *          DOCUMENT ME!
3347    */
3348   @Override
3349   public void hydrophobicityColour_actionPerformed(ActionEvent e)
3350   {
3351     changeColour(new HydrophobicColourScheme());
3352   }
3353
3354   /**
3355    * DOCUMENT ME!
3356    * 
3357    * @param e
3358    *          DOCUMENT ME!
3359    */
3360   @Override
3361   public void helixColour_actionPerformed(ActionEvent e)
3362   {
3363     changeColour(new HelixColourScheme());
3364   }
3365
3366   /**
3367    * DOCUMENT ME!
3368    * 
3369    * @param e
3370    *          DOCUMENT ME!
3371    */
3372   @Override
3373   public void strandColour_actionPerformed(ActionEvent e)
3374   {
3375     changeColour(new StrandColourScheme());
3376   }
3377
3378   /**
3379    * DOCUMENT ME!
3380    * 
3381    * @param e
3382    *          DOCUMENT ME!
3383    */
3384   @Override
3385   public void turnColour_actionPerformed(ActionEvent e)
3386   {
3387     changeColour(new TurnColourScheme());
3388   }
3389
3390   /**
3391    * DOCUMENT ME!
3392    * 
3393    * @param e
3394    *          DOCUMENT ME!
3395    */
3396   @Override
3397   public void buriedColour_actionPerformed(ActionEvent e)
3398   {
3399     changeColour(new BuriedColourScheme());
3400   }
3401
3402   /**
3403    * DOCUMENT ME!
3404    * 
3405    * @param e
3406    *          DOCUMENT ME!
3407    */
3408   @Override
3409   public void nucleotideColour_actionPerformed(ActionEvent e)
3410   {
3411     changeColour(new NucleotideColourScheme());
3412   }
3413
3414   @Override
3415   public void purinePyrimidineColour_actionPerformed(ActionEvent e)
3416   {
3417     changeColour(new PurinePyrimidineColourScheme());
3418   }
3419
3420   /*
3421    * public void covariationColour_actionPerformed(ActionEvent e) {
3422    * changeColour(new
3423    * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
3424    * ()[0])); }
3425    */
3426   @Override
3427   public void annotationColour_actionPerformed(ActionEvent e)
3428   {
3429     new AnnotationColourChooser(viewport, alignPanel);
3430   }
3431
3432   @Override
3433   public void annotationColumn_actionPerformed(ActionEvent e)
3434   {
3435     new AnnotationColumnChooser(viewport, alignPanel);
3436   }
3437
3438   @Override
3439   public void rnahelicesColour_actionPerformed(ActionEvent e)
3440   {
3441     new RNAHelicesColourChooser(viewport, alignPanel);
3442   }
3443
3444   /**
3445    * DOCUMENT ME!
3446    * 
3447    * @param e
3448    *          DOCUMENT ME!
3449    */
3450   @Override
3451   protected void applyToAllGroups_actionPerformed(ActionEvent e)
3452   {
3453     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
3454   }
3455
3456   /**
3457    * DOCUMENT ME!
3458    * 
3459    * @param cs
3460    *          DOCUMENT ME!
3461    */
3462   public void changeColour(ColourSchemeI cs)
3463   {
3464     // TODO: compare with applet and pull up to model method
3465     int threshold = 0;
3466
3467     if (cs != null)
3468     {
3469       if (viewport.getAbovePIDThreshold())
3470       {
3471         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
3472                 "Background");
3473         cs.setThreshold(threshold, viewport.isIgnoreGapsConsensus());
3474       }
3475       else
3476       {
3477         cs.setThreshold(0, viewport.isIgnoreGapsConsensus());
3478       }
3479
3480       if (viewport.getConservationSelected())
3481       {
3482
3483         Alignment al = (Alignment) viewport.getAlignment();
3484         Conservation c = new Conservation("All",
3485                 ResidueProperties.propHash, 3, al.getSequences(), 0,
3486                 al.getWidth() - 1);
3487
3488         c.calculate();
3489         c.verdict(false, viewport.getConsPercGaps());
3490
3491         cs.setConservation(c);
3492
3493         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
3494                 cs, "Background"));
3495       }
3496       else
3497       {
3498         cs.setConservation(null);
3499       }
3500
3501       cs.setConsensus(viewport.getSequenceConsensusHash());
3502     }
3503
3504     viewport.setGlobalColourScheme(cs);
3505
3506     if (viewport.getColourAppliesToAllGroups())
3507     {
3508
3509       for (SequenceGroup sg : viewport.getAlignment().getGroups())
3510       {
3511         if (cs == null)
3512         {
3513           sg.cs = null;
3514           continue;
3515         }
3516
3517         if (cs instanceof ClustalxColourScheme)
3518         {
3519           sg.cs = new ClustalxColourScheme(sg,
3520                   viewport.getHiddenRepSequences());
3521         }
3522         else if (cs instanceof UserColourScheme)
3523         {
3524           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
3525         }
3526         else
3527         {
3528           try
3529           {
3530             sg.cs = cs.getClass().newInstance();
3531           } catch (Exception ex)
3532           {
3533           }
3534         }
3535
3536         if (viewport.getAbovePIDThreshold()
3537                 || cs instanceof PIDColourScheme
3538                 || cs instanceof Blosum62ColourScheme)
3539         {
3540           sg.cs.setThreshold(threshold, viewport.isIgnoreGapsConsensus());
3541
3542           sg.cs.setConsensus(AAFrequency.calculate(
3543                   sg.getSequences(viewport.getHiddenRepSequences()),
3544                   sg.getStartRes(), sg.getEndRes() + 1));
3545         }
3546         else
3547         {
3548           sg.cs.setThreshold(0, viewport.isIgnoreGapsConsensus());
3549         }
3550
3551         if (viewport.getConservationSelected())
3552         {
3553           Conservation c = new Conservation("Group",
3554                   ResidueProperties.propHash, 3, sg.getSequences(viewport
3555                           .getHiddenRepSequences()), sg.getStartRes(),
3556                   sg.getEndRes() + 1);
3557           c.calculate();
3558           c.verdict(false, viewport.getConsPercGaps());
3559           sg.cs.setConservation(c);
3560         }
3561         else
3562         {
3563           sg.cs.setConservation(null);
3564         }
3565       }
3566     }
3567
3568     if (alignPanel.getOverviewPanel() != null)
3569     {
3570       alignPanel.getOverviewPanel().updateOverviewImage();
3571     }
3572
3573     alignPanel.paintAlignment(true);
3574   }
3575
3576   /**
3577    * DOCUMENT ME!
3578    * 
3579    * @param e
3580    *          DOCUMENT ME!
3581    */
3582   @Override
3583   protected void modifyPID_actionPerformed(ActionEvent e)
3584   {
3585     if (viewport.getAbovePIDThreshold()
3586             && viewport.getGlobalColourScheme() != null)
3587     {
3588       SliderPanel.setPIDSliderSource(alignPanel,
3589               viewport.getGlobalColourScheme(), "Background");
3590       SliderPanel.showPIDSlider();
3591     }
3592   }
3593
3594   /**
3595    * DOCUMENT ME!
3596    * 
3597    * @param e
3598    *          DOCUMENT ME!
3599    */
3600   @Override
3601   protected void modifyConservation_actionPerformed(ActionEvent e)
3602   {
3603     if (viewport.getConservationSelected()
3604             && viewport.getGlobalColourScheme() != null)
3605     {
3606       SliderPanel.setConservationSlider(alignPanel,
3607               viewport.getGlobalColourScheme(), "Background");
3608       SliderPanel.showConservationSlider();
3609     }
3610   }
3611
3612   /**
3613    * DOCUMENT ME!
3614    * 
3615    * @param e
3616    *          DOCUMENT ME!
3617    */
3618   @Override
3619   protected void conservationMenuItem_actionPerformed(ActionEvent e)
3620   {
3621     viewport.setConservationSelected(conservationMenuItem.isSelected());
3622
3623     viewport.setAbovePIDThreshold(false);
3624     abovePIDThreshold.setSelected(false);
3625
3626     changeColour(viewport.getGlobalColourScheme());
3627
3628     modifyConservation_actionPerformed(null);
3629   }
3630
3631   /**
3632    * DOCUMENT ME!
3633    * 
3634    * @param e
3635    *          DOCUMENT ME!
3636    */
3637   @Override
3638   public void abovePIDThreshold_actionPerformed(ActionEvent e)
3639   {
3640     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
3641
3642     conservationMenuItem.setSelected(false);
3643     viewport.setConservationSelected(false);
3644
3645     changeColour(viewport.getGlobalColourScheme());
3646
3647     modifyPID_actionPerformed(null);
3648   }
3649
3650   /**
3651    * DOCUMENT ME!
3652    * 
3653    * @param e
3654    *          DOCUMENT ME!
3655    */
3656   @Override
3657   public void userDefinedColour_actionPerformed(ActionEvent e)
3658   {
3659     if (e.getActionCommand().equals(
3660             MessageManager.getString("action.user_defined")))
3661     {
3662       new UserDefinedColours(alignPanel, null);
3663     }
3664     else
3665     {
3666       UserColourScheme udc = (UserColourScheme) UserDefinedColours
3667               .getUserColourSchemes().get(e.getActionCommand());
3668
3669       changeColour(udc);
3670     }
3671   }
3672
3673   public void updateUserColourMenu()
3674   {
3675
3676     Component[] menuItems = colourMenu.getMenuComponents();
3677     int iSize = menuItems.length;
3678     for (int i = 0; i < iSize; i++)
3679     {
3680       if (menuItems[i].getName() != null
3681               && menuItems[i].getName().equals("USER_DEFINED"))
3682       {
3683         colourMenu.remove(menuItems[i]);
3684         iSize--;
3685       }
3686     }
3687     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
3688     {
3689       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
3690               .getUserColourSchemes().keys();
3691
3692       while (userColours.hasMoreElements())
3693       {
3694         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
3695                 userColours.nextElement().toString());
3696         radioItem.setName("USER_DEFINED");
3697         radioItem.addMouseListener(new MouseAdapter()
3698         {
3699           @Override
3700           public void mousePressed(MouseEvent evt)
3701           {
3702             if (evt.isControlDown()
3703                     || SwingUtilities.isRightMouseButton(evt))
3704             {
3705               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
3706
3707               int option = JOptionPane.showInternalConfirmDialog(
3708                       jalview.gui.Desktop.desktop,
3709                       MessageManager
3710                               .getString("label.remove_from_default_list"),
3711                       MessageManager
3712                               .getString("label.remove_user_defined_colour"),
3713                       JOptionPane.YES_NO_OPTION);
3714               if (option == JOptionPane.YES_OPTION)
3715               {
3716                 jalview.gui.UserDefinedColours
3717                         .removeColourFromDefaults(radioItem.getText());
3718                 colourMenu.remove(radioItem);
3719               }
3720               else
3721               {
3722                 radioItem.addActionListener(new ActionListener()
3723                 {
3724                   @Override
3725                   public void actionPerformed(ActionEvent evt)
3726                   {
3727                     userDefinedColour_actionPerformed(evt);
3728                   }
3729                 });
3730               }
3731             }
3732           }
3733         });
3734         radioItem.addActionListener(new ActionListener()
3735         {
3736           @Override
3737           public void actionPerformed(ActionEvent evt)
3738           {
3739             userDefinedColour_actionPerformed(evt);
3740           }
3741         });
3742
3743         colourMenu.insert(radioItem, 15);
3744         colours.add(radioItem);
3745       }
3746     }
3747   }
3748
3749   /**
3750    * DOCUMENT ME!
3751    * 
3752    * @param e
3753    *          DOCUMENT ME!
3754    */
3755   @Override
3756   public void PIDColour_actionPerformed(ActionEvent e)
3757   {
3758     changeColour(new PIDColourScheme());
3759   }
3760
3761   /**
3762    * DOCUMENT ME!
3763    * 
3764    * @param e
3765    *          DOCUMENT ME!
3766    */
3767   @Override
3768   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
3769   {
3770     changeColour(new Blosum62ColourScheme());
3771   }
3772
3773   /**
3774    * DOCUMENT ME!
3775    * 
3776    * @param e
3777    *          DOCUMENT ME!
3778    */
3779   @Override
3780   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3781   {
3782     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3783     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
3784             .getAlignment().getSequenceAt(0), null);
3785     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3786             viewport.getAlignment()));
3787     alignPanel.paintAlignment(true);
3788   }
3789
3790   /**
3791    * DOCUMENT ME!
3792    * 
3793    * @param e
3794    *          DOCUMENT ME!
3795    */
3796   @Override
3797   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3798   {
3799     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3800     AlignmentSorter.sortByID(viewport.getAlignment());
3801     addHistoryItem(new OrderCommand("ID Sort", oldOrder,
3802             viewport.getAlignment()));
3803     alignPanel.paintAlignment(true);
3804   }
3805
3806   /**
3807    * DOCUMENT ME!
3808    * 
3809    * @param e
3810    *          DOCUMENT ME!
3811    */
3812   @Override
3813   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3814   {
3815     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3816     AlignmentSorter.sortByLength(viewport.getAlignment());
3817     addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3818             viewport.getAlignment()));
3819     alignPanel.paintAlignment(true);
3820   }
3821
3822   /**
3823    * DOCUMENT ME!
3824    * 
3825    * @param e
3826    *          DOCUMENT ME!
3827    */
3828   @Override
3829   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3830   {
3831     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3832     AlignmentSorter.sortByGroup(viewport.getAlignment());
3833     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3834             viewport.getAlignment()));
3835
3836     alignPanel.paintAlignment(true);
3837   }
3838
3839   /**
3840    * DOCUMENT ME!
3841    * 
3842    * @param e
3843    *          DOCUMENT ME!
3844    */
3845   @Override
3846   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3847   {
3848     new RedundancyPanel(alignPanel, this);
3849   }
3850
3851   /**
3852    * DOCUMENT ME!
3853    * 
3854    * @param e
3855    *          DOCUMENT ME!
3856    */
3857   @Override
3858   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3859   {
3860     if ((viewport.getSelectionGroup() == null)
3861             || (viewport.getSelectionGroup().getSize() < 2))
3862     {
3863       JOptionPane.showInternalMessageDialog(this, MessageManager
3864               .getString("label.you_must_select_least_two_sequences"),
3865               MessageManager.getString("label.invalid_selection"),
3866               JOptionPane.WARNING_MESSAGE);
3867     }
3868     else
3869     {
3870       JInternalFrame frame = new JInternalFrame();
3871       frame.setContentPane(new PairwiseAlignPanel(viewport));
3872       Desktop.addInternalFrame(frame,
3873               MessageManager.getString("action.pairwise_alignment"), 600,
3874               500);
3875     }
3876   }
3877
3878   /**
3879    * DOCUMENT ME!
3880    * 
3881    * @param e
3882    *          DOCUMENT ME!
3883    */
3884   @Override
3885   public void PCAMenuItem_actionPerformed(ActionEvent e)
3886   {
3887     if (((viewport.getSelectionGroup() != null)
3888             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
3889             .getSelectionGroup().getSize() > 0))
3890             || (viewport.getAlignment().getHeight() < 4))
3891     {
3892       JOptionPane
3893               .showInternalMessageDialog(
3894                       this,
3895                       MessageManager
3896                               .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
3897                       MessageManager
3898                               .getString("label.sequence_selection_insufficient"),
3899                       JOptionPane.WARNING_MESSAGE);
3900
3901       return;
3902     }
3903
3904     new PCAPanel(alignPanel);
3905   }
3906
3907   @Override
3908   public void autoCalculate_actionPerformed(ActionEvent e)
3909   {
3910     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3911     if (viewport.autoCalculateConsensus)
3912     {
3913       viewport.firePropertyChange("alignment", null, viewport
3914               .getAlignment().getSequences());
3915     }
3916   }
3917
3918   @Override
3919   public void sortByTreeOption_actionPerformed(ActionEvent e)
3920   {
3921     viewport.sortByTree = sortByTree.isSelected();
3922   }
3923
3924   @Override
3925   protected void listenToViewSelections_actionPerformed(ActionEvent e)
3926   {
3927     viewport.followSelection = listenToViewSelections.isSelected();
3928   }
3929
3930   /**
3931    * DOCUMENT ME!
3932    * 
3933    * @param e
3934    *          DOCUMENT ME!
3935    */
3936   @Override
3937   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3938   {
3939     newTreePanel("AV", "PID", "Average distance tree using PID");
3940   }
3941
3942   /**
3943    * DOCUMENT ME!
3944    * 
3945    * @param e
3946    *          DOCUMENT ME!
3947    */
3948   @Override
3949   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3950   {
3951     newTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3952   }
3953
3954   /**
3955    * DOCUMENT ME!
3956    * 
3957    * @param e
3958    *          DOCUMENT ME!
3959    */
3960   @Override
3961   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3962   {
3963     newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3964   }
3965
3966   /**
3967    * DOCUMENT ME!
3968    * 
3969    * @param e
3970    *          DOCUMENT ME!
3971    */
3972   @Override
3973   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3974   {
3975     newTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3976   }
3977
3978   /**
3979    * DOCUMENT ME!
3980    * 
3981    * @param type
3982    *          DOCUMENT ME!
3983    * @param pwType
3984    *          DOCUMENT ME!
3985    * @param title
3986    *          DOCUMENT ME!
3987    */
3988   void newTreePanel(String type, String pwType, String title)
3989   {
3990     TreePanel tp;
3991
3992     if (viewport.getSelectionGroup() != null
3993             && viewport.getSelectionGroup().getSize() > 0)
3994     {
3995       if (viewport.getSelectionGroup().getSize() < 3)
3996       {
3997         JOptionPane
3998                 .showMessageDialog(
3999                         Desktop.desktop,
4000                         MessageManager
4001                                 .getString("label.you_need_more_two_sequences_selected_build_tree"),
4002                         MessageManager
4003                                 .getString("label.not_enough_sequences"),
4004                         JOptionPane.WARNING_MESSAGE);
4005         return;
4006       }
4007
4008       SequenceGroup sg = viewport.getSelectionGroup();
4009
4010       /* Decide if the selection is a column region */
4011       for (SequenceI _s : sg.getSequences())
4012       {
4013         if (_s.getLength() < sg.getEndRes())
4014         {
4015           JOptionPane
4016                   .showMessageDialog(
4017                           Desktop.desktop,
4018                           MessageManager
4019                                   .getString("label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
4020                           MessageManager
4021                                   .getString("label.sequences_selection_not_aligned"),
4022                           JOptionPane.WARNING_MESSAGE);
4023
4024           return;
4025         }
4026       }
4027
4028       title = title + " on region";
4029       tp = new TreePanel(alignPanel, type, pwType);
4030     }
4031     else
4032     {
4033       // are the visible sequences aligned?
4034       if (!viewport.getAlignment().isAligned(false))
4035       {
4036         JOptionPane
4037                 .showMessageDialog(
4038                         Desktop.desktop,
4039                         MessageManager
4040                                 .getString("label.sequences_must_be_aligned_before_creating_tree"),
4041                         MessageManager
4042                                 .getString("label.sequences_not_aligned"),
4043                         JOptionPane.WARNING_MESSAGE);
4044
4045         return;
4046       }
4047
4048       if (viewport.getAlignment().getHeight() < 2)
4049       {
4050         return;
4051       }
4052
4053       tp = new TreePanel(alignPanel, type, pwType);
4054     }
4055
4056     title += " from ";
4057
4058     if (viewport.viewName != null)
4059     {
4060       title += viewport.viewName + " of ";
4061     }
4062
4063     title += this.title;
4064
4065     Desktop.addInternalFrame(tp, title, 600, 500);
4066   }
4067
4068   /**
4069    * DOCUMENT ME!
4070    * 
4071    * @param title
4072    *          DOCUMENT ME!
4073    * @param order
4074    *          DOCUMENT ME!
4075    */
4076   public void addSortByOrderMenuItem(String title,
4077           final AlignmentOrder order)
4078   {
4079     final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new Object[]{title}));
4080     sort.add(item);
4081     item.addActionListener(new java.awt.event.ActionListener()
4082     {
4083       @Override
4084       public void actionPerformed(ActionEvent e)
4085       {
4086         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4087
4088         // TODO: JBPNote - have to map order entries to curent SequenceI
4089         // pointers
4090         AlignmentSorter.sortBy(viewport.getAlignment(), order);
4091
4092         addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport
4093                 .getAlignment()));
4094
4095         alignPanel.paintAlignment(true);
4096       }
4097     });
4098   }
4099
4100   /**
4101    * Add a new sort by annotation score menu item
4102    * 
4103    * @param sort
4104    *          the menu to add the option to
4105    * @param scoreLabel
4106    *          the label used to retrieve scores for each sequence on the
4107    *          alignment
4108    */
4109   public void addSortByAnnotScoreMenuItem(JMenu sort,
4110           final String scoreLabel)
4111   {
4112     final JMenuItem item = new JMenuItem(scoreLabel);
4113     sort.add(item);
4114     item.addActionListener(new java.awt.event.ActionListener()
4115     {
4116       @Override
4117       public void actionPerformed(ActionEvent e)
4118       {
4119         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4120         AlignmentSorter.sortByAnnotationScore(scoreLabel,
4121                 viewport.getAlignment());// ,viewport.getSelectionGroup());
4122         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
4123                 viewport.getAlignment()));
4124         alignPanel.paintAlignment(true);
4125       }
4126     });
4127   }
4128
4129   /**
4130    * last hash for alignment's annotation array - used to minimise cost of
4131    * rebuild.
4132    */
4133   protected int _annotationScoreVectorHash;
4134
4135   /**
4136    * search the alignment and rebuild the sort by annotation score submenu the
4137    * last alignment annotation vector hash is stored to minimize cost of
4138    * rebuilding in subsequence calls.
4139    * 
4140    */
4141   @Override
4142   public void buildSortByAnnotationScoresMenu()
4143   {
4144     if (viewport.getAlignment().getAlignmentAnnotation() == null)
4145     {
4146       return;
4147     }
4148
4149     if (viewport.getAlignment().getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
4150     {
4151       sortByAnnotScore.removeAll();
4152       // almost certainly a quicker way to do this - but we keep it simple
4153       Hashtable scoreSorts = new Hashtable();
4154       AlignmentAnnotation aann[];
4155       for (SequenceI sqa : viewport.getAlignment().getSequences())
4156       {
4157         aann = sqa.getAnnotation();
4158         for (int i = 0; aann != null && i < aann.length; i++)
4159         {
4160           if (aann[i].hasScore() && aann[i].sequenceRef != null)
4161           {
4162             scoreSorts.put(aann[i].label, aann[i].label);
4163           }
4164         }
4165       }
4166       Enumeration labels = scoreSorts.keys();
4167       while (labels.hasMoreElements())
4168       {
4169         addSortByAnnotScoreMenuItem(sortByAnnotScore,
4170                 (String) labels.nextElement());
4171       }
4172       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
4173       scoreSorts.clear();
4174
4175       _annotationScoreVectorHash = viewport.getAlignment()
4176               .getAlignmentAnnotation().hashCode();
4177     }
4178   }
4179
4180   /**
4181    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
4182    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
4183    * call. Listeners are added to remove the menu item when the treePanel is
4184    * closed, and adjust the tree leaf to sequence mapping when the alignment is
4185    * modified.
4186    * 
4187    * @param treePanel
4188    *          Displayed tree window.
4189    * @param title
4190    *          SortBy menu item title.
4191    */
4192   @Override
4193   public void buildTreeMenu()
4194   {
4195     calculateTree.removeAll();
4196     // build the calculate menu
4197
4198     for (final String type : new String[]
4199     { "NJ", "AV" })
4200     {
4201       String treecalcnm = MessageManager.getString("label.tree_calc_"
4202               + type.toLowerCase());
4203       for (final String pwtype : ResidueProperties.scoreMatrices.keySet())
4204       {
4205         JMenuItem tm = new JMenuItem();
4206         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
4207         if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
4208         {
4209           String smn = MessageManager.getStringOrReturn(
4210                   "label.score_model_", sm.getName());
4211           final String title = MessageManager.formatMessage(
4212                   "label.treecalc_title", treecalcnm, smn);
4213           tm.setText(title);//
4214           tm.addActionListener(new java.awt.event.ActionListener()
4215           {
4216             @Override
4217             public void actionPerformed(ActionEvent e)
4218             {
4219               newTreePanel(type, pwtype, title);
4220             }
4221           });
4222           calculateTree.add(tm);
4223         }
4224
4225       }
4226     }
4227     sortByTreeMenu.removeAll();
4228
4229     List<Component> comps = PaintRefresher.components.get(viewport
4230             .getSequenceSetId());
4231     List<TreePanel> treePanels = new ArrayList<TreePanel>();
4232     for (Component comp : comps)
4233     {
4234       if (comp instanceof TreePanel)
4235       {
4236         treePanels.add((TreePanel) comp);
4237       }
4238     }
4239
4240     if (treePanels.size() < 1)
4241     {
4242       sortByTreeMenu.setVisible(false);
4243       return;
4244     }
4245
4246     sortByTreeMenu.setVisible(true);
4247
4248     for (final TreePanel tp : treePanels)
4249     {
4250       final JMenuItem item = new JMenuItem(tp.getTitle());
4251       item.addActionListener(new java.awt.event.ActionListener()
4252       {
4253         @Override
4254         public void actionPerformed(ActionEvent e)
4255         {
4256           tp.sortByTree_actionPerformed();
4257           addHistoryItem(tp.sortAlignmentIn(alignPanel));
4258
4259         }
4260       });
4261
4262       sortByTreeMenu.add(item);
4263     }
4264   }
4265
4266   public boolean sortBy(AlignmentOrder alorder, String undoname)
4267   {
4268     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4269     AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
4270     if (undoname != null)
4271     {
4272       addHistoryItem(new OrderCommand(undoname, oldOrder,
4273               viewport.getAlignment()));
4274     }
4275     alignPanel.paintAlignment(true);
4276     return true;
4277   }
4278
4279   /**
4280    * Work out whether the whole set of sequences or just the selected set will
4281    * be submitted for multiple alignment.
4282    * 
4283    */
4284   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
4285   {
4286     // Now, check we have enough sequences
4287     AlignmentView msa = null;
4288
4289     if ((viewport.getSelectionGroup() != null)
4290             && (viewport.getSelectionGroup().getSize() > 1))
4291     {
4292       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
4293       // some common interface!
4294       /*
4295        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
4296        * SequenceI[sz = seqs.getSize(false)];
4297        * 
4298        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
4299        * seqs.getSequenceAt(i); }
4300        */
4301       msa = viewport.getAlignmentView(true);
4302     }
4303     else if (viewport.getSelectionGroup() != null
4304             && viewport.getSelectionGroup().getSize() == 1)
4305     {
4306       int option = JOptionPane.showConfirmDialog(this,
4307               MessageManager.getString("warn.oneseq_msainput_selection"),
4308               MessageManager.getString("label.invalid_selection"),
4309               JOptionPane.OK_CANCEL_OPTION);
4310       if (option == JOptionPane.OK_OPTION)
4311       {
4312         msa = viewport.getAlignmentView(false);
4313       }
4314     }
4315     else
4316     {
4317       msa = viewport.getAlignmentView(false);
4318     }
4319     return msa;
4320   }
4321
4322   /**
4323    * Decides what is submitted to a secondary structure prediction service: the
4324    * first sequence in the alignment, or in the current selection, or, if the
4325    * alignment is 'aligned' (ie padded with gaps), then the currently selected
4326    * region or the whole alignment. (where the first sequence in the set is the
4327    * one that the prediction will be for).
4328    */
4329   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4330   {
4331     AlignmentView seqs = null;
4332
4333     if ((viewport.getSelectionGroup() != null)
4334             && (viewport.getSelectionGroup().getSize() > 0))
4335     {
4336       seqs = viewport.getAlignmentView(true);
4337     }
4338     else
4339     {
4340       seqs = viewport.getAlignmentView(false);
4341     }
4342     // limit sequences - JBPNote in future - could spawn multiple prediction
4343     // jobs
4344     // TODO: viewport.getAlignment().isAligned is a global state - the local
4345     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4346     if (!viewport.getAlignment().isAligned(false))
4347     {
4348       seqs.setSequences(new SeqCigar[]
4349       { seqs.getSequences()[0] });
4350       // TODO: if seqs.getSequences().length>1 then should really have warned
4351       // user!
4352
4353     }
4354     return seqs;
4355   }
4356
4357   /**
4358    * DOCUMENT ME!
4359    * 
4360    * @param e
4361    *          DOCUMENT ME!
4362    */
4363   @Override
4364   protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
4365   {
4366     // Pick the tree file
4367     JalviewFileChooser chooser = new JalviewFileChooser(
4368             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
4369     chooser.setFileView(new JalviewFileView());
4370     chooser.setDialogTitle(MessageManager
4371             .getString("label.select_newick_like_tree_file"));
4372     chooser.setToolTipText(MessageManager.getString("label.load_tree_file"));
4373
4374     int value = chooser.showOpenDialog(null);
4375
4376     if (value == JalviewFileChooser.APPROVE_OPTION)
4377     {
4378       String choice = chooser.getSelectedFile().getPath();
4379       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
4380       jalview.io.NewickFile fin = null;
4381       try
4382       {
4383         fin = new jalview.io.NewickFile(choice, "File");
4384         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
4385       } catch (Exception ex)
4386       {
4387         JOptionPane
4388                 .showMessageDialog(
4389                         Desktop.desktop,
4390                         ex.getMessage(),
4391                         MessageManager
4392                                 .getString("label.problem_reading_tree_file"),
4393                         JOptionPane.WARNING_MESSAGE);
4394         ex.printStackTrace();
4395       }
4396       if (fin != null && fin.hasWarningMessage())
4397       {
4398         JOptionPane.showMessageDialog(Desktop.desktop, fin
4399                 .getWarningMessage(), MessageManager
4400                 .getString("label.possible_problem_with_tree_file"),
4401                 JOptionPane.WARNING_MESSAGE);
4402       }
4403     }
4404   }
4405
4406   @Override
4407   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
4408   {
4409     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
4410   }
4411
4412   public TreePanel ShowNewickTree(NewickFile nf, String title)
4413   {
4414     return ShowNewickTree(nf, title, 600, 500, 4, 5);
4415   }
4416
4417   public TreePanel ShowNewickTree(NewickFile nf, String title,
4418           AlignmentView input)
4419   {
4420     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
4421   }
4422
4423   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
4424           int h, int x, int y)
4425   {
4426     return ShowNewickTree(nf, title, null, w, h, x, y);
4427   }
4428
4429   /**
4430    * Add a treeviewer for the tree extracted from a newick file object to the
4431    * current alignment view
4432    * 
4433    * @param nf
4434    *          the tree
4435    * @param title
4436    *          tree viewer title
4437    * @param input
4438    *          Associated alignment input data (or null)
4439    * @param w
4440    *          width
4441    * @param h
4442    *          height
4443    * @param x
4444    *          position
4445    * @param y
4446    *          position
4447    * @return TreePanel handle
4448    */
4449   public TreePanel ShowNewickTree(NewickFile nf, String title,
4450           AlignmentView input, int w, int h, int x, int y)
4451   {
4452     TreePanel tp = null;
4453
4454     try
4455     {
4456       nf.parse();
4457
4458       if (nf.getTree() != null)
4459       {
4460         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
4461
4462         tp.setSize(w, h);
4463
4464         if (x > 0 && y > 0)
4465         {
4466           tp.setLocation(x, y);
4467         }
4468
4469         Desktop.addInternalFrame(tp, title, w, h);
4470       }
4471     } catch (Exception ex)
4472     {
4473       ex.printStackTrace();
4474     }
4475
4476     return tp;
4477   }
4478
4479   private boolean buildingMenu = false;
4480
4481   /**
4482    * Generates menu items and listener event actions for web service clients
4483    * 
4484    */
4485   public void BuildWebServiceMenu()
4486   {
4487     while (buildingMenu)
4488     {
4489       try
4490       {
4491         System.err.println("Waiting for building menu to finish.");
4492         Thread.sleep(10);
4493       } catch (Exception e)
4494       {
4495       }
4496     }
4497     final AlignFrame me = this;
4498     buildingMenu = true;
4499     new Thread(new Runnable()
4500     {
4501       @Override
4502       public void run()
4503       {
4504         final List<JMenuItem> legacyItems = new ArrayList<JMenuItem>();
4505         try
4506         {
4507           System.err.println("Building ws menu again "
4508                   + Thread.currentThread());
4509           // TODO: add support for context dependent disabling of services based
4510           // on
4511           // alignment and current selection
4512           // TODO: add additional serviceHandle parameter to specify abstract
4513           // handler
4514           // class independently of AbstractName
4515           // TODO: add in rediscovery GUI function to restart discoverer
4516           // TODO: group services by location as well as function and/or
4517           // introduce
4518           // object broker mechanism.
4519           final Vector<JMenu> wsmenu = new Vector<JMenu>();
4520           final IProgressIndicator af = me;
4521           final JMenu msawsmenu = new JMenu("Alignment");
4522           final JMenu secstrmenu = new JMenu(
4523                   "Secondary Structure Prediction");
4524           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4525           final JMenu analymenu = new JMenu("Analysis");
4526           final JMenu dismenu = new JMenu("Protein Disorder");
4527           // final JMenu msawsmenu = new
4528           // JMenu(MessageManager.getString("label.alignment"));
4529           // final JMenu secstrmenu = new
4530           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
4531           // final JMenu seqsrchmenu = new
4532           // JMenu(MessageManager.getString("label.sequence_database_search"));
4533           // final JMenu analymenu = new
4534           // JMenu(MessageManager.getString("label.analysis"));
4535           // final JMenu dismenu = new
4536           // JMenu(MessageManager.getString("label.protein_disorder"));
4537           // JAL-940 - only show secondary structure prediction services from
4538           // the legacy server
4539           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4540               // &&
4541           Discoverer.services != null && (Discoverer.services.size() > 0))
4542           {
4543             // TODO: refactor to allow list of AbstractName/Handler bindings to
4544             // be
4545             // stored or retrieved from elsewhere
4546             // No MSAWS used any more:
4547             // Vector msaws = null; // (Vector)
4548             // Discoverer.services.get("MsaWS");
4549             Vector secstrpr = (Vector) Discoverer.services
4550                     .get("SecStrPred");
4551             if (secstrpr != null)
4552             {
4553               // Add any secondary structure prediction services
4554               for (int i = 0, j = secstrpr.size(); i < j; i++)
4555               {
4556                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
4557                         .get(i);
4558                 jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4559                         .getServiceClient(sh);
4560                 int p = secstrmenu.getItemCount();
4561                 impl.attachWSMenuEntry(secstrmenu, me);
4562                 int q = secstrmenu.getItemCount();
4563                 for (int litm = p; litm < q; litm++)
4564                 {
4565                   legacyItems.add(secstrmenu.getItem(litm));
4566                 }
4567               }
4568             }
4569           }
4570
4571           // Add all submenus in the order they should appear on the web
4572           // services menu
4573           wsmenu.add(msawsmenu);
4574           wsmenu.add(secstrmenu);
4575           wsmenu.add(dismenu);
4576           wsmenu.add(analymenu);
4577           // No search services yet
4578           // wsmenu.add(seqsrchmenu);
4579
4580           javax.swing.SwingUtilities.invokeLater(new Runnable()
4581           {
4582             @Override
4583             public void run()
4584             {
4585               try
4586               {
4587                 webService.removeAll();
4588                 // first, add discovered services onto the webservices menu
4589                 if (wsmenu.size() > 0)
4590                 {
4591                   for (int i = 0, j = wsmenu.size(); i < j; i++)
4592                   {
4593                     webService.add(wsmenu.get(i));
4594                   }
4595                 }
4596                 else
4597                 {
4598                   webService.add(me.webServiceNoServices);
4599                 }
4600                 // TODO: move into separate menu builder class.
4601                 boolean new_sspred = false;
4602                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
4603                 {
4604                   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4605                   if (jws2servs != null)
4606                   {
4607                     if (jws2servs.hasServices())
4608                     {
4609                       jws2servs.attachWSMenuEntry(webService, me);
4610                       for (Jws2Instance sv : jws2servs.getServices())
4611                       {
4612                         if (sv.description.toLowerCase().contains("jpred"))
4613                         {
4614                           for (JMenuItem jmi : legacyItems)
4615                           {
4616                             jmi.setVisible(false);
4617                           }
4618                         }
4619                       }
4620
4621                     }
4622                     if (jws2servs.isRunning())
4623                     {
4624                       JMenuItem tm = new JMenuItem(
4625                               "Still discovering JABA Services");
4626                       tm.setEnabled(false);
4627                       webService.add(tm);
4628                     }
4629                   }
4630                 }
4631                 build_urlServiceMenu(me.webService);
4632                 build_fetchdbmenu(webService);
4633                 for (JMenu item : wsmenu)
4634                 {
4635                   if (item.getItemCount() == 0)
4636                   {
4637                     item.setEnabled(false);
4638                   }
4639                   else
4640                   {
4641                     item.setEnabled(true);
4642                   }
4643                 }
4644               } catch (Exception e)
4645               {
4646                 Cache.log
4647                         .debug("Exception during web service menu building process.",
4648                                 e);
4649               }
4650             }
4651           });
4652         } catch (Exception e)
4653         {
4654         }
4655         buildingMenu = false;
4656       }
4657     }).start();
4658
4659   }
4660
4661   /**
4662    * construct any groupURL type service menu entries.
4663    * 
4664    * @param webService
4665    */
4666   private void build_urlServiceMenu(JMenu webService)
4667   {
4668     // TODO: remove this code when 2.7 is released
4669     // DEBUG - alignmentView
4670     /*
4671      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
4672      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
4673      * 
4674      * @Override public void actionPerformed(ActionEvent e) {
4675      * jalview.datamodel.AlignmentView
4676      * .testSelectionViews(af.viewport.getAlignment(),
4677      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4678      * 
4679      * }); webService.add(testAlView);
4680      */
4681     // TODO: refactor to RestClient discoverer and merge menu entries for
4682     // rest-style services with other types of analysis/calculation service
4683     // SHmmr test client - still being implemented.
4684     // DEBUG - alignmentView
4685
4686     for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4687             .getRestClients())
4688     {
4689       client.attachWSMenuEntry(
4690               JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4691               this);
4692     }
4693   }
4694
4695   /*
4696    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
4697    * chooser = new JalviewFileChooser(jalview.bin.Cache.
4698    * getProperty("LAST_DIRECTORY"));
4699    * 
4700    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
4701    * to Vamsas file"); chooser.setToolTipText("Export");
4702    * 
4703    * int value = chooser.showSaveDialog(this);
4704    * 
4705    * if (value == JalviewFileChooser.APPROVE_OPTION) {
4706    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
4707    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
4708    * chooser.getSelectedFile().getAbsolutePath(), this); } }
4709    */
4710   /**
4711    * prototype of an automatically enabled/disabled analysis function
4712    * 
4713    */
4714   protected void setShowProductsEnabled()
4715   {
4716     SequenceI[] selection = viewport.getSequenceSelection();
4717     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
4718             viewport.getAlignment().getDataset()))
4719     {
4720       showProducts.setEnabled(true);
4721
4722     }
4723     else
4724     {
4725       showProducts.setEnabled(false);
4726     }
4727   }
4728
4729   /**
4730    * search selection for sequence xRef products and build the show products
4731    * menu.
4732    * 
4733    * @param selection
4734    * @param dataset
4735    * @return true if showProducts menu should be enabled.
4736    */
4737   public boolean canShowProducts(SequenceI[] selection,
4738           boolean isRegionSelection, Alignment dataset)
4739   {
4740     boolean showp = false;
4741     try
4742     {
4743       showProducts.removeAll();
4744       final boolean dna = viewport.getAlignment().isNucleotide();
4745       final Alignment ds = dataset;
4746       String[] ptypes = (selection == null || selection.length == 0) ? null
4747               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
4748       // Object[] prods =
4749       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
4750       // selection, dataset, true);
4751       final SequenceI[] sel = selection;
4752       for (int t = 0; ptypes != null && t < ptypes.length; t++)
4753       {
4754         showp = true;
4755         final boolean isRegSel = isRegionSelection;
4756         final AlignFrame af = this;
4757         final String source = ptypes[t];
4758         JMenuItem xtype = new JMenuItem(ptypes[t]);
4759         xtype.addActionListener(new ActionListener()
4760         {
4761
4762           @Override
4763           public void actionPerformed(ActionEvent e)
4764           {
4765             // TODO: new thread for this call with vis-delay
4766             af.showProductsFor(af.viewport.getSequenceSelection(),
4767                     isRegSel, dna, source);
4768           }
4769
4770         });
4771         showProducts.add(xtype);
4772       }
4773       showProducts.setVisible(showp);
4774       showProducts.setEnabled(showp);
4775     } catch (Exception e)
4776     {
4777       jalview.bin.Cache.log
4778               .warn("canTranslate threw an exception - please report to help@jalview.org",
4779                       e);
4780       return false;
4781     }
4782     return showp;
4783   }
4784
4785   protected void showProductsFor(final SequenceI[] sel,
4786           final boolean isRegSel, final boolean dna, final String source)
4787   {
4788     Runnable foo = new Runnable()
4789     {
4790
4791       @Override
4792       public void run()
4793       {
4794         final long sttime = System.currentTimeMillis();
4795         AlignFrame.this.setProgressBar(MessageManager.formatMessage(
4796                 "status.searching_for_sequences_from", new Object[]
4797                 { source }), sttime);
4798         try
4799         {
4800           // update our local dataset reference
4801           Alignment ds = AlignFrame.this.getViewport().getAlignment()
4802                   .getDataset();
4803           Alignment prods = CrossRef
4804                   .findXrefSequences(sel, dna, source, ds);
4805           if (prods != null)
4806           {
4807             SequenceI[] sprods = new SequenceI[prods.getHeight()];
4808             for (int s = 0; s < sprods.length; s++)
4809             {
4810               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
4811               if (ds.getSequences() == null
4812                       || !ds.getSequences().contains(
4813                               sprods[s].getDatasetSequence()))
4814               {
4815                 ds.addSequence(sprods[s].getDatasetSequence());
4816               }
4817               sprods[s].updatePDBIds();
4818             }
4819             Alignment al = new Alignment(sprods);
4820             al.setDataset(ds);
4821
4822             /*
4823              * Copy dna-to-protein mappings to new alignment
4824              */
4825             // TODO 1: no mappings are set up for EMBL product
4826             // TODO 2: if they were, should add them to protein alignment, not
4827             // dna
4828             Set<AlignedCodonFrame> cf = prods.getCodonFrames();
4829             for (AlignedCodonFrame acf : cf)
4830             {
4831               al.addCodonFrame(acf);
4832             }
4833             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
4834                     DEFAULT_HEIGHT);
4835             String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
4836                     + " for " + ((isRegSel) ? "selected region of " : "")
4837                     + getTitle();
4838             naf.setTitle(newtitle);
4839
4840             // temporary flag until SplitFrame is released
4841             boolean asSplitFrame = Cache.getDefault(
4842                     Preferences.ENABLE_SPLIT_FRAME, false);
4843             if (asSplitFrame)
4844             {
4845               /*
4846                * Make a copy of this alignment (sharing the same dataset
4847                * sequences). If we are DNA, drop introns and update mappings
4848                */
4849               AlignmentI copyAlignment = null;
4850               final SequenceI[] sequenceSelection = AlignFrame.this.viewport
4851                       .getSequenceSelection();
4852               if (dna)
4853               {
4854                 copyAlignment = AlignmentUtils.makeExonAlignment(
4855                         sequenceSelection, cf);
4856                 al.getCodonFrames().clear();
4857                 al.getCodonFrames().addAll(cf);
4858                 final StructureSelectionManager ssm = StructureSelectionManager
4859                         .getStructureSelectionManager(Desktop.instance);
4860                 ssm.addMappings(cf);
4861               }
4862               else
4863               {
4864                 copyAlignment = new Alignment(new Alignment(
4865                         sequenceSelection));
4866               }
4867               AlignFrame copyThis = new AlignFrame(copyAlignment,
4868                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
4869               copyThis.setTitle(AlignFrame.this.getTitle());
4870               // SplitFrame with dna above, protein below
4871               SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
4872                       dna ? naf : copyThis);
4873               naf.setVisible(true);
4874               copyThis.setVisible(true);
4875               String linkedTitle = MessageManager
4876                       .getString("label.linked_view_title");
4877               Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
4878             }
4879             else
4880             {
4881               Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
4882                       DEFAULT_HEIGHT);
4883             }
4884           }
4885           else
4886           {
4887             System.err.println("No Sequences generated for xRef type "
4888                     + source);
4889           }
4890         } catch (Exception e)
4891         {
4892           jalview.bin.Cache.log.error(
4893                   "Exception when finding crossreferences", e);
4894         } catch (OutOfMemoryError e)
4895         {
4896           new OOMWarning("whilst fetching crossreferences", e);
4897         } catch (Error e)
4898         {
4899           jalview.bin.Cache.log.error("Error when finding crossreferences",
4900                   e);
4901         }
4902         AlignFrame.this.setProgressBar(MessageManager.formatMessage(
4903                 "status.finished_searching_for_sequences_from",
4904                 new Object[]
4905                 { source }),
4906                 sttime);
4907       }
4908
4909     };
4910     Thread frunner = new Thread(foo);
4911     frunner.start();
4912   }
4913
4914   public boolean canShowTranslationProducts(SequenceI[] selection,
4915           AlignmentI alignment)
4916   {
4917     // old way
4918     try
4919     {
4920       return (jalview.analysis.Dna.canTranslate(selection,
4921               viewport.getViewAsVisibleContigs(true)));
4922     } catch (Exception e)
4923     {
4924       jalview.bin.Cache.log
4925               .warn("canTranslate threw an exception - please report to help@jalview.org",
4926                       e);
4927       return false;
4928     }
4929   }
4930
4931   /**
4932    * Construct and display a new frame containing the translation of this
4933    * frame's DNA sequences to their aligned protein (amino acid) equivalents.
4934    */
4935   @Override
4936   public void showTranslation_actionPerformed(ActionEvent e)
4937   {
4938     AlignmentI al = null;
4939     try
4940     {
4941       Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
4942
4943       al = dna.translateCdna();
4944     } catch (Exception ex)
4945     {
4946       jalview.bin.Cache.log.error(
4947               "Exception during translation. Please report this !", ex);
4948       final String msg = MessageManager
4949               .getString("label.error_when_translating_sequences_submit_bug_report");
4950       final String title = MessageManager
4951               .getString("label.implementation_error")
4952               + MessageManager.getString("translation_failed");
4953       JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
4954               JOptionPane.ERROR_MESSAGE);
4955       return;
4956     }
4957     if (al == null || al.getHeight() == 0)
4958     {
4959       final String msg = MessageManager
4960               .getString("label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
4961       final String title = MessageManager
4962               .getString("label.translation_failed");
4963       JOptionPane.showMessageDialog(Desktop.desktop, msg, title,
4964               JOptionPane.WARNING_MESSAGE);
4965     }
4966     else
4967     {
4968       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4969       af.setFileFormat(this.currentFileFormat);
4970       final String newTitle = MessageManager.formatMessage(
4971               "label.translation_of_params", new Object[]
4972               { this.getTitle() });
4973       af.setTitle(newTitle);
4974       if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, false))
4975       {
4976         final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
4977         viewport.openSplitFrame(af, new Alignment(seqs),
4978                 al.getCodonFrames());
4979       }
4980       else
4981       {
4982         Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH,
4983                 DEFAULT_HEIGHT);
4984       }
4985     }
4986   }
4987
4988   /**
4989    * Set the file format
4990    * 
4991    * @param fileFormat
4992    */
4993   public void setFileFormat(String fileFormat)
4994   {
4995     this.currentFileFormat = fileFormat;
4996   }
4997
4998   /**
4999    * Try to load a features file onto the alignment.
5000    * 
5001    * @param file
5002    *          contents or path to retrieve file
5003    * @param type
5004    *          access mode of file (see jalview.io.AlignFile)
5005    * @return true if features file was parsed corectly.
5006    */
5007   public boolean parseFeaturesFile(String file, String type)
5008   {
5009     boolean featuresFile = false;
5010     try
5011     {
5012       featuresFile = new FeaturesFile(file, type).parse(viewport
5013               .getAlignment().getDataset(), alignPanel.getSeqPanel().seqCanvas
5014               .getFeatureRenderer().getFeatureColours(), false,
5015               jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
5016     } catch (Exception ex)
5017     {
5018       ex.printStackTrace();
5019     }
5020
5021     if (featuresFile)
5022     {
5023       viewport.setShowSequenceFeatures(true);
5024       showSeqFeatures.setSelected(true);
5025       if (alignPanel.getSeqPanel().seqCanvas.fr != null)
5026       {
5027         // update the min/max ranges where necessary
5028         alignPanel.getSeqPanel().seqCanvas.fr.findAllFeatures(true);
5029       }
5030       if (featureSettings != null)
5031       {
5032         featureSettings.setTableData();
5033       }
5034       alignPanel.paintAlignment(true);
5035     }
5036
5037     return featuresFile;
5038   }
5039
5040   @Override
5041   public void dragEnter(DropTargetDragEvent evt)
5042   {
5043   }
5044
5045   @Override
5046   public void dragExit(DropTargetEvent evt)
5047   {
5048   }
5049
5050   @Override
5051   public void dragOver(DropTargetDragEvent evt)
5052   {
5053   }
5054
5055   @Override
5056   public void dropActionChanged(DropTargetDragEvent evt)
5057   {
5058   }
5059
5060   @Override
5061   public void drop(DropTargetDropEvent evt)
5062   {
5063     Transferable t = evt.getTransferable();
5064     java.util.List files = null;
5065
5066     try
5067     {
5068       DataFlavor uriListFlavor = new DataFlavor(
5069               "text/uri-list;class=java.lang.String");
5070       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
5071       {
5072         // Works on Windows and MacOSX
5073         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5074         files = (java.util.List) t
5075                 .getTransferData(DataFlavor.javaFileListFlavor);
5076       }
5077       else if (t.isDataFlavorSupported(uriListFlavor))
5078       {
5079         // This is used by Unix drag system
5080         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5081         String data = (String) t.getTransferData(uriListFlavor);
5082         files = new java.util.ArrayList(1);
5083         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
5084                 data, "\r\n"); st.hasMoreTokens();)
5085         {
5086           String s = st.nextToken();
5087           if (s.startsWith("#"))
5088           {
5089             // the line is a comment (as per the RFC 2483)
5090             continue;
5091           }
5092
5093           java.net.URI uri = new java.net.URI(s);
5094           // check to see if we can handle this kind of URI
5095           if (uri.getScheme().toLowerCase().startsWith("http"))
5096           {
5097             files.add(uri.toString());
5098           }
5099           else
5100           {
5101             // otherwise preserve old behaviour: catch all for file objects
5102             java.io.File file = new java.io.File(uri);
5103             files.add(file.toString());
5104           }
5105         }
5106       }
5107     } catch (Exception e)
5108     {
5109       e.printStackTrace();
5110     }
5111     if (files != null)
5112     {
5113       try
5114       {
5115         // check to see if any of these files have names matching sequences in
5116         // the alignment
5117         SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5118                 .getAlignment().getSequencesArray());
5119         /**
5120          * Object[] { String,SequenceI}
5121          */
5122         ArrayList<Object[]> filesmatched = new ArrayList<Object[]>();
5123         ArrayList<String> filesnotmatched = new ArrayList<String>();
5124         for (int i = 0; i < files.size(); i++)
5125         {
5126           String file = files.get(i).toString();
5127           String pdbfn = "";
5128           String protocol = FormatAdapter.checkProtocol(file);
5129           if (protocol == jalview.io.FormatAdapter.FILE)
5130           {
5131             File fl = new File(file);
5132             pdbfn = fl.getName();
5133           }
5134           else if (protocol == jalview.io.FormatAdapter.URL)
5135           {
5136             URL url = new URL(file);
5137             pdbfn = url.getFile();
5138           }
5139           if (pdbfn.length() > 0)
5140           {
5141             // attempt to find a match in the alignment
5142             SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
5143             int l = 0, c = pdbfn.indexOf(".");
5144             while (mtch == null && c != -1)
5145             {
5146               do
5147               {
5148                 l = c;
5149               } while ((c = pdbfn.indexOf(".", l)) > l);
5150               if (l > -1)
5151               {
5152                 pdbfn = pdbfn.substring(0, l);
5153               }
5154               mtch = idm.findAllIdMatches(pdbfn);
5155             }
5156             if (mtch != null)
5157             {
5158               String type = null;
5159               try
5160               {
5161                 type = new IdentifyFile().Identify(file, protocol);
5162               } catch (Exception ex)
5163               {
5164                 type = null;
5165               }
5166               if (type != null)
5167               {
5168                 if (type.equalsIgnoreCase("PDB"))
5169                 {
5170                   filesmatched.add(new Object[]
5171                   { file, protocol, mtch });
5172                   continue;
5173                 }
5174               }
5175             }
5176             // File wasn't named like one of the sequences or wasn't a PDB file.
5177             filesnotmatched.add(file);
5178           }
5179         }
5180         int assocfiles = 0;
5181         if (filesmatched.size() > 0)
5182         {
5183           if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false)
5184                   || JOptionPane
5185                           .showConfirmDialog(
5186                                   this,
5187                                   MessageManager
5188                                           .formatMessage(
5189                                                   "label.automatically_associate_pdb_files_with_sequences_same_name",
5190                                                   new Object[]
5191                                                   { Integer.valueOf(
5192                                                           filesmatched
5193                                                                   .size())
5194                                                           .toString() }),
5195                                   MessageManager
5196                                           .getString("label.automatically_associate_pdb_files_by_name"),
5197                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
5198
5199           {
5200             for (Object[] fm : filesmatched)
5201             {
5202               // try and associate
5203               // TODO: may want to set a standard ID naming formalism for
5204               // associating PDB files which have no IDs.
5205               for (SequenceI toassoc : (SequenceI[]) fm[2])
5206               {
5207                 PDBEntry pe = new AssociatePdbFileWithSeq()
5208                         .associatePdbWithSeq((String) fm[0],
5209                                 (String) fm[1], toassoc, false,
5210                                 Desktop.instance);
5211                 if (pe != null)
5212                 {
5213                   System.err.println("Associated file : "
5214                           + ((String) fm[0]) + " with "
5215                           + toassoc.getDisplayId(true));
5216                   assocfiles++;
5217                 }
5218               }
5219               alignPanel.paintAlignment(true);
5220             }
5221           }
5222         }
5223         if (filesnotmatched.size() > 0)
5224         {
5225           if (assocfiles > 0
5226                   && (Cache.getDefault(
5227                           "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
5228                           .showConfirmDialog(
5229                                   this,
5230                                   "<html>"+MessageManager
5231                                           .formatMessage(
5232                                                   "label.ignore_unmatched_dropped_files_info",
5233                                                   new Object[]
5234                                                   { Integer.valueOf(
5235                                                           filesnotmatched
5236                                                                   .size())
5237                                                           .toString() })+"</html>",
5238                                   MessageManager
5239                                           .getString("label.ignore_unmatched_dropped_files"),
5240                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
5241           {
5242             return;
5243           }
5244           for (String fn : filesnotmatched)
5245           {
5246             loadJalviewDataFile(fn, null, null, null);
5247           }
5248
5249         }
5250       } catch (Exception ex)
5251       {
5252         ex.printStackTrace();
5253       }
5254     }
5255   }
5256
5257   /**
5258    * Attempt to load a "dropped" file or URL string: First by testing whether
5259    * it's and Annotation file, then a JNet file, and finally a features file. If
5260    * all are false then the user may have dropped an alignment file onto this
5261    * AlignFrame.
5262    * 
5263    * @param file
5264    *          either a filename or a URL string.
5265    */
5266   public void loadJalviewDataFile(String file, String protocol,
5267           String format, SequenceI assocSeq)
5268   {
5269     try
5270     {
5271       if (protocol == null)
5272       {
5273         protocol = jalview.io.FormatAdapter.checkProtocol(file);
5274       }
5275       // if the file isn't identified, or not positively identified as some
5276       // other filetype (PFAM is default unidentified alignment file type) then
5277       // try to parse as annotation.
5278       boolean isAnnotation = (format == null || format
5279               .equalsIgnoreCase("PFAM")) ? new AnnotationFile()
5280               .annotateAlignmentView(viewport, file, protocol)
5281               : false;
5282
5283       if (!isAnnotation)
5284       {
5285         // first see if its a T-COFFEE score file
5286         TCoffeeScoreFile tcf = null;
5287         try
5288         {
5289           tcf = new TCoffeeScoreFile(file, protocol);
5290           if (tcf.isValid())
5291           {
5292             if (tcf.annotateAlignment(viewport.getAlignment(), true))
5293             {
5294               tcoffeeColour.setEnabled(true);
5295               tcoffeeColour.setSelected(true);
5296               changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
5297               isAnnotation = true;
5298               statusBar
5299                       .setText(MessageManager
5300                               .getString("label.successfully_pasted_tcoffee_scores_to_alignment"));
5301             }
5302             else
5303             {
5304               // some problem - if no warning its probable that the ID matching
5305               // process didn't work
5306               JOptionPane
5307                       .showMessageDialog(
5308                               Desktop.desktop,
5309                               tcf.getWarningMessage() == null ? MessageManager
5310                                       .getString("label.check_file_matches_sequence_ids_alignment")
5311                                       : tcf.getWarningMessage(),
5312                               MessageManager
5313                                       .getString("label.problem_reading_tcoffee_score_file"),
5314                               JOptionPane.WARNING_MESSAGE);
5315             }
5316           }
5317           else
5318           {
5319             tcf = null;
5320           }
5321         } catch (Exception x)
5322         {
5323           Cache.log
5324                   .debug("Exception when processing data source as T-COFFEE score file",
5325                           x);
5326           tcf = null;
5327         }
5328         if (tcf == null)
5329         {
5330           // try to see if its a JNet 'concise' style annotation file *before*
5331           // we
5332           // try to parse it as a features file
5333           if (format == null)
5334           {
5335             format = new IdentifyFile().Identify(file, protocol);
5336           }
5337           if (format.equalsIgnoreCase("JnetFile"))
5338           {
5339             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
5340                     file, protocol);
5341             new JnetAnnotationMaker();
5342             JnetAnnotationMaker.add_annotation(predictions,
5343                     viewport.getAlignment(), 0, false);
5344             SequenceI repseq = viewport.getAlignment().getSequenceAt(0);
5345             viewport.getAlignment().setSeqrep(repseq);
5346             ColumnSelection cs = new ColumnSelection();
5347             cs.hideInsertionsFor(repseq);
5348             viewport.setColumnSelection(cs);
5349             isAnnotation = true;
5350           }
5351           else
5352           {
5353             /*
5354              * if (format.equalsIgnoreCase("PDB")) {
5355              * 
5356              * String pdbfn = ""; // try to match up filename with sequence id
5357              * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
5358              * new File(file); pdbfn = fl.getName(); } else if (protocol ==
5359              * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
5360              * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
5361              * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
5362              * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
5363              * // attempt to find a match in the alignment SequenceI mtch =
5364              * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
5365              * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
5366              * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
5367              * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
5368              * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
5369              * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
5370              * { System.err.println("Associated file : " + file + " with " +
5371              * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
5372              * TODO: maybe need to load as normal otherwise return; } }
5373              */
5374             // try to parse it as a features file
5375             boolean isGroupsFile = parseFeaturesFile(file, protocol);
5376             // if it wasn't a features file then we just treat it as a general
5377             // alignment file to load into the current view.
5378             if (!isGroupsFile)
5379             {
5380               new FileLoader().LoadFile(viewport, file, protocol, format);
5381             }
5382             else
5383             {
5384               alignPanel.paintAlignment(true);
5385             }
5386           }
5387         }
5388       }
5389       if (isAnnotation)
5390       {
5391
5392         alignPanel.adjustAnnotationHeight();
5393         viewport.updateSequenceIdColours();
5394         buildSortByAnnotationScoresMenu();
5395         alignPanel.paintAlignment(true);
5396       }
5397     } catch (Exception ex)
5398     {
5399       ex.printStackTrace();
5400     } catch (OutOfMemoryError oom)
5401     {
5402       try
5403       {
5404         System.gc();
5405       } catch (Exception x)
5406       {
5407       }
5408       ;
5409       new OOMWarning(
5410               "loading data "
5411                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
5412                               : "using " + protocol + " from " + file)
5413                               : ".")
5414                       + (format != null ? "(parsing as '" + format
5415                               + "' file)" : ""), oom, Desktop.desktop);
5416     }
5417   }
5418
5419   /**
5420    * Method invoked by the ChangeListener on the tabbed pane, in other words
5421    * when a different tabbed pane is selected by the user or programmatically.
5422    */
5423   @Override
5424   public void tabSelectionChanged(int index)
5425   {
5426     if (index > -1)
5427     {
5428       alignPanel = alignPanels.get(index);
5429       viewport = alignPanel.av;
5430       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
5431       setMenusFromViewport(viewport);
5432     }
5433
5434     /*
5435      * If there is a frame linked to this one in a SplitPane, switch it to the
5436      * same view tab index. No infinite recursion of calls should happen, since
5437      * tabSelectionChanged() should not get invoked on setting the selected
5438      * index to an unchanged value. Guard against setting an invalid index
5439      * before the new view peer tab has been created.
5440      */
5441     final AlignViewportI peer = viewport.getCodingComplement();
5442     if (peer != null)
5443     {
5444       AlignFrame linkedAlignFrame = ((AlignViewport) peer).getAlignPanel().alignFrame;
5445       if (linkedAlignFrame.tabbedPane.getTabCount() > index)
5446       {
5447         linkedAlignFrame.tabbedPane.setSelectedIndex(index);
5448       }
5449     }
5450   }
5451
5452   /**
5453    * On right mouse click on view tab, prompt for and set new view name.
5454    */
5455   @Override
5456   public void tabbedPane_mousePressed(MouseEvent e)
5457   {
5458     if (SwingUtilities.isRightMouseButton(e))
5459     {
5460       String msg = MessageManager.getString("label.enter_view_name");
5461       String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
5462               JOptionPane.QUESTION_MESSAGE);
5463
5464       if (reply != null)
5465       {
5466         viewport.viewName = reply;
5467         // TODO warn if reply is in getExistingViewNames()?
5468         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
5469       }
5470     }
5471   }
5472
5473   public AlignViewport getCurrentView()
5474   {
5475     return viewport;
5476   }
5477
5478   /**
5479    * Open the dialog for regex description parsing.
5480    */
5481   @Override
5482   protected void extractScores_actionPerformed(ActionEvent e)
5483   {
5484     ParseProperties pp = new jalview.analysis.ParseProperties(
5485             viewport.getAlignment());
5486     // TODO: verify regex and introduce GUI dialog for version 2.5
5487     // if (pp.getScoresFromDescription("col", "score column ",
5488     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
5489     // true)>0)
5490     if (pp.getScoresFromDescription("description column",
5491             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
5492     {
5493       buildSortByAnnotationScoresMenu();
5494     }
5495   }
5496
5497   /*
5498    * (non-Javadoc)
5499    * 
5500    * @see
5501    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
5502    * )
5503    */
5504   @Override
5505   protected void showDbRefs_actionPerformed(ActionEvent e)
5506   {
5507     viewport.setShowDBRefs(showDbRefsMenuitem.isSelected());
5508   }
5509
5510   /*
5511    * (non-Javadoc)
5512    * 
5513    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
5514    * ActionEvent)
5515    */
5516   @Override
5517   protected void showNpFeats_actionPerformed(ActionEvent e)
5518   {
5519     viewport.setShowNPFeats(showNpFeatsMenuitem.isSelected());
5520   }
5521
5522   /**
5523    * find the viewport amongst the tabs in this alignment frame and close that
5524    * tab
5525    * 
5526    * @param av
5527    */
5528   public boolean closeView(AlignViewportI av)
5529   {
5530     if (viewport == av)
5531     {
5532       this.closeMenuItem_actionPerformed(false);
5533       return true;
5534     }
5535     Component[] comp = tabbedPane.getComponents();
5536     for (int i = 0; comp != null && i < comp.length; i++)
5537     {
5538       if (comp[i] instanceof AlignmentPanel)
5539       {
5540         if (((AlignmentPanel) comp[i]).av == av)
5541         {
5542           // close the view.
5543           closeView((AlignmentPanel) comp[i]);
5544           return true;
5545         }
5546       }
5547     }
5548     return false;
5549   }
5550
5551   protected void build_fetchdbmenu(JMenu webService)
5552   {
5553     // Temporary hack - DBRef Fetcher always top level ws entry.
5554     // TODO We probably want to store a sequence database checklist in
5555     // preferences and have checkboxes.. rather than individual sources selected
5556     // here
5557     final JMenu rfetch = new JMenu(
5558             MessageManager.getString("action.fetch_db_references"));
5559     rfetch.setToolTipText(MessageManager
5560             .getString("label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
5561     webService.add(rfetch);
5562
5563     final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
5564             MessageManager.getString("option.trim_retrieved_seqs"));
5565     trimrs.setToolTipText(MessageManager
5566             .getString("label.trim_retrieved_sequences"));
5567     trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
5568     trimrs.addActionListener(new ActionListener()
5569     {
5570       @Override
5571       public void actionPerformed(ActionEvent e)
5572       {
5573         trimrs.setSelected(trimrs.isSelected());
5574         Cache.setProperty("TRIM_FETCHED_DATASET_SEQS",
5575                 Boolean.valueOf(trimrs.isSelected()).toString());
5576       };
5577     });
5578     rfetch.add(trimrs);
5579     JMenuItem fetchr = new JMenuItem(
5580             MessageManager.getString("label.standard_databases"));
5581     fetchr.setToolTipText(MessageManager
5582             .getString("label.fetch_embl_uniprot"));
5583     fetchr.addActionListener(new ActionListener()
5584     {
5585
5586       @Override
5587       public void actionPerformed(ActionEvent e)
5588       {
5589         new Thread(new Runnable()
5590         {
5591
5592           @Override
5593           public void run()
5594           {
5595             new jalview.ws.DBRefFetcher(alignPanel.av
5596                     .getSequenceSelection(), alignPanel.alignFrame)
5597                     .fetchDBRefs(false);
5598           }
5599         }).start();
5600
5601       }
5602
5603     });
5604     rfetch.add(fetchr);
5605     final AlignFrame me = this;
5606     new Thread(new Runnable()
5607     {
5608       @Override
5609       public void run()
5610       {
5611         final jalview.ws.SequenceFetcher sf = SequenceFetcher
5612                 .getSequenceFetcherSingleton(me);
5613         javax.swing.SwingUtilities.invokeLater(new Runnable()
5614         {
5615           @Override
5616           public void run()
5617           {
5618             String[] dbclasses = sf.getOrderedSupportedSources();
5619             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
5620             // jalview.util.QuickSort.sort(otherdb, otherdb);
5621             List<DbSourceProxy> otherdb;
5622             JMenu dfetch = new JMenu();
5623             JMenu ifetch = new JMenu();
5624             JMenuItem fetchr = null;
5625             int comp = 0, icomp = 0, mcomp = 15;
5626             String mname = null;
5627             int dbi = 0;
5628             for (String dbclass : dbclasses)
5629             {
5630               otherdb = sf.getSourceProxy(dbclass);
5631               // add a single entry for this class, or submenu allowing 'fetch
5632               // all' or pick one
5633               if (otherdb == null || otherdb.size() < 1)
5634               {
5635                 continue;
5636               }
5637               // List<DbSourceProxy> dbs=otherdb;
5638               // otherdb=new ArrayList<DbSourceProxy>();
5639               // for (DbSourceProxy db:dbs)
5640               // {
5641               // if (!db.isA(DBRefSource.ALIGNMENTDB)
5642               // }
5643               if (mname == null)
5644               {
5645                 mname = "From " + dbclass;
5646               }
5647               if (otherdb.size() == 1)
5648               {
5649                 final DbSourceProxy[] dassource = otherdb
5650                         .toArray(new DbSourceProxy[0]);
5651                 DbSourceProxy src = otherdb.get(0);
5652                 fetchr = new JMenuItem(src.getDbSource());
5653                 fetchr.addActionListener(new ActionListener()
5654                 {
5655
5656                   @Override
5657                   public void actionPerformed(ActionEvent e)
5658                   {
5659                     new Thread(new Runnable()
5660                     {
5661
5662                       @Override
5663                       public void run()
5664                       {
5665                         new jalview.ws.DBRefFetcher(alignPanel.av
5666                                 .getSequenceSelection(),
5667                                 alignPanel.alignFrame, dassource)
5668                                 .fetchDBRefs(false);
5669                       }
5670                     }).start();
5671                   }
5672
5673                 });
5674                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{src.getDbName()})));
5675                 dfetch.add(fetchr);
5676                 comp++;
5677               }
5678               else
5679               {
5680                 final DbSourceProxy[] dassource = otherdb
5681                         .toArray(new DbSourceProxy[0]);
5682                 // fetch all entry
5683                 DbSourceProxy src = otherdb.get(0);
5684                 fetchr = new JMenuItem(MessageManager.formatMessage(
5685                         "label.fetch_all_param", new Object[]
5686                         { src.getDbSource() }));
5687                 fetchr.addActionListener(new ActionListener()
5688                 {
5689                   @Override
5690                   public void actionPerformed(ActionEvent e)
5691                   {
5692                     new Thread(new Runnable()
5693                     {
5694
5695                       @Override
5696                       public void run()
5697                       {
5698                         new jalview.ws.DBRefFetcher(alignPanel.av
5699                                 .getSequenceSelection(),
5700                                 alignPanel.alignFrame, dassource)
5701                                 .fetchDBRefs(false);
5702                       }
5703                     }).start();
5704                   }
5705                 });
5706
5707                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new Object[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
5708                 dfetch.add(fetchr);
5709                 comp++;
5710                 // and then build the rest of the individual menus
5711                 ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new Object[]{src.getDbSource()}));
5712                 icomp = 0;
5713                 String imname = null;
5714                 int i = 0;
5715                 for (DbSourceProxy sproxy : otherdb)
5716                 {
5717                   String dbname = sproxy.getDbName();
5718                   String sname = dbname.length() > 5 ? dbname.substring(0,
5719                           5) + "..." : dbname;
5720                   String msname = dbname.length() > 10 ? dbname.substring(
5721                           0, 10) + "..." : dbname;
5722                   if (imname == null)
5723                   {
5724                     imname = MessageManager.formatMessage("label.from_msname", new Object[]{sname});
5725                   }
5726                   fetchr = new JMenuItem(msname);
5727                   final DbSourceProxy[] dassrc =
5728                   { sproxy };
5729                   fetchr.addActionListener(new ActionListener()
5730                   {
5731
5732                     @Override
5733                     public void actionPerformed(ActionEvent e)
5734                     {
5735                       new Thread(new Runnable()
5736                       {
5737
5738                         @Override
5739                         public void run()
5740                         {
5741                           new jalview.ws.DBRefFetcher(alignPanel.av
5742                                   .getSequenceSelection(),
5743                                   alignPanel.alignFrame, dassrc)
5744                                   .fetchDBRefs(false);
5745                         }
5746                       }).start();
5747                     }
5748
5749                   });
5750                   fetchr.setToolTipText("<html>"
5751                           + MessageManager.formatMessage("label.fetch_retrieve_from", new Object[]{dbname}));
5752                   ifetch.add(fetchr);
5753                   ++i;
5754                   if (++icomp >= mcomp || i == (otherdb.size()))
5755                   {
5756                     ifetch.setText(MessageManager.formatMessage(
5757                             "label.source_to_target", imname, sname));
5758                     dfetch.add(ifetch);
5759                     ifetch = new JMenu();
5760                     imname = null;
5761                     icomp = 0;
5762                     comp++;
5763                   }
5764                 }
5765               }
5766               ++dbi;
5767               if (comp >= mcomp || dbi >= (dbclasses.length))
5768               {
5769                 dfetch.setText(MessageManager.formatMessage(
5770                         "label.source_to_target", mname, dbclass));
5771                 rfetch.add(dfetch);
5772                 dfetch = new JMenu();
5773                 mname = null;
5774                 comp = 0;
5775               }
5776             }
5777           }
5778         });
5779       }
5780     }).start();
5781
5782   }
5783
5784   /**
5785    * Left justify the whole alignment.
5786    */
5787   @Override
5788   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
5789   {
5790     AlignmentI al = viewport.getAlignment();
5791     al.justify(false);
5792     viewport.firePropertyChange("alignment", null, al);
5793   }
5794
5795   /**
5796    * Right justify the whole alignment.
5797    */
5798   @Override
5799   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
5800   {
5801     AlignmentI al = viewport.getAlignment();
5802     al.justify(true);
5803     viewport.firePropertyChange("alignment", null, al);
5804   }
5805
5806   public void setShowSeqFeatures(boolean b)
5807   {
5808     showSeqFeatures.setSelected(b);
5809     viewport.setShowSequenceFeatures(b);
5810   }
5811
5812   /*
5813    * (non-Javadoc)
5814    * 
5815    * @see
5816    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
5817    * awt.event.ActionEvent)
5818    */
5819   @Override
5820   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
5821   {
5822     viewport.setShowUnconserved(showNonconservedMenuItem.getState());
5823     alignPanel.paintAlignment(true);
5824   }
5825
5826   /*
5827    * (non-Javadoc)
5828    * 
5829    * @see
5830    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
5831    * .ActionEvent)
5832    */
5833   @Override
5834   protected void showGroupConsensus_actionPerformed(ActionEvent e)
5835   {
5836     viewport.setShowGroupConsensus(showGroupConsensus.getState());
5837     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5838
5839   }
5840
5841   /*
5842    * (non-Javadoc)
5843    * 
5844    * @see
5845    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
5846    * .event.ActionEvent)
5847    */
5848   @Override
5849   protected void showGroupConservation_actionPerformed(ActionEvent e)
5850   {
5851     viewport.setShowGroupConservation(showGroupConservation.getState());
5852     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5853   }
5854
5855   /*
5856    * (non-Javadoc)
5857    * 
5858    * @see
5859    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
5860    * .event.ActionEvent)
5861    */
5862   @Override
5863   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
5864   {
5865     viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
5866     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5867   }
5868
5869   /*
5870    * (non-Javadoc)
5871    * 
5872    * @see
5873    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
5874    * .event.ActionEvent)
5875    */
5876   @Override
5877   protected void showSequenceLogo_actionPerformed(ActionEvent e)
5878   {
5879     viewport.setShowSequenceLogo(showSequenceLogo.getState());
5880     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5881   }
5882
5883   @Override
5884   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
5885   {
5886     showSequenceLogo.setState(true);
5887     viewport.setShowSequenceLogo(true);
5888     viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
5889     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5890   }
5891
5892   @Override
5893   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
5894   {
5895     alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
5896   }
5897
5898   /*
5899    * (non-Javadoc)
5900    * 
5901    * @see
5902    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
5903    * .event.ActionEvent)
5904    */
5905   @Override
5906   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
5907   {
5908     if (avc.makeGroupsFromSelection())
5909     {
5910       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5911       alignPanel.updateAnnotation();
5912       alignPanel.paintAlignment(true);
5913     }
5914   }
5915   public void clearAlignmentSeqRep()
5916   {
5917     // TODO refactor alignmentseqrep to controller
5918     if (viewport.getAlignment().hasSeqrep()) {
5919       viewport.getAlignment().setSeqrep(null);
5920       PaintRefresher.Refresh(this, viewport.getSequenceSetId());
5921       alignPanel.updateAnnotation();
5922       alignPanel.paintAlignment(true);
5923     }
5924   }
5925
5926   @Override
5927   protected void createGroup_actionPerformed(ActionEvent e)
5928   {
5929     if (avc.createGroup())
5930     {
5931       alignPanel.alignmentChanged();
5932     }
5933   }
5934
5935   @Override
5936   protected void unGroup_actionPerformed(ActionEvent e)
5937   {
5938     if (avc.unGroup())
5939     {
5940       alignPanel.alignmentChanged();
5941     }
5942   }
5943
5944   /**
5945    * make the given alignmentPanel the currently selected tab
5946    * 
5947    * @param alignmentPanel
5948    */
5949   public void setDisplayedView(AlignmentPanel alignmentPanel)
5950   {
5951     if (!viewport.getSequenceSetId().equals(
5952             alignmentPanel.av.getSequenceSetId()))
5953     {
5954       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
5955     }
5956     if (tabbedPane != null
5957             && tabbedPane.getTabCount() > 0
5958             && alignPanels.indexOf(alignmentPanel) != tabbedPane
5959                     .getSelectedIndex())
5960     {
5961       tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
5962     }
5963   }
5964
5965   /**
5966    * Action on selection of menu options to Show or Hide annotations.
5967    * 
5968    * @param visible
5969    * @param forSequences
5970    *          update sequence-related annotations
5971    * @param forAlignment
5972    *          update non-sequence-related annotations
5973    */
5974   @Override
5975   protected void setAnnotationsVisibility(boolean visible,
5976           boolean forSequences, boolean forAlignment)
5977   {
5978     for (AlignmentAnnotation aa : alignPanel.getAlignment()
5979             .getAlignmentAnnotation())
5980     {
5981       /*
5982        * don't display non-positional annotations on an alignment
5983        */
5984       if (aa.annotations == null)
5985       {
5986         continue;
5987       }
5988       boolean apply = (aa.sequenceRef == null && forAlignment)
5989               || (aa.sequenceRef != null && forSequences);
5990       if (apply)
5991       {
5992         aa.visible = visible;
5993       }
5994     }
5995     alignPanel.validateAnnotationDimensions(false);
5996     alignPanel.alignmentChanged();
5997   }
5998
5999   /**
6000    * Store selected annotation sort order for the view and repaint.
6001    */
6002   @Override
6003   protected void sortAnnotations_actionPerformed()
6004   {
6005     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
6006     this.alignPanel.av
6007             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
6008     alignPanel.paintAlignment(true);
6009   }
6010
6011   /**
6012    * 
6013    * @return alignment panels in this alignment frame
6014    */
6015   public List<? extends AlignmentViewPanel> getAlignPanels()
6016   {
6017     return alignPanels == null ? Arrays.asList(alignPanel)
6018             : alignPanels;
6019   }
6020
6021   /**
6022    * Open a new alignment window, with the cDNA associated with this (protein)
6023    * alignment, aligned as is the protein.
6024    */
6025   protected void viewAsCdna_actionPerformed()
6026   {
6027     // TODO no longer a menu action - refactor as required
6028     final AlignmentI alignment = getViewport().getAlignment();
6029     Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
6030     if (mappings == null)
6031     {
6032       return;
6033     }
6034     List<SequenceI> cdnaSeqs = new ArrayList<SequenceI>();
6035     for (SequenceI aaSeq : alignment.getSequences()) {
6036       for (AlignedCodonFrame acf : mappings) {
6037         SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence());
6038         if (dnaSeq != null)
6039         {
6040           /*
6041            * There is a cDNA mapping for this protein sequence - add to new
6042            * alignment. It will share the same dataset sequence as other mapped
6043            * cDNA (no new mappings need to be created).
6044            */
6045           final Sequence newSeq = new Sequence(dnaSeq);
6046           newSeq.setDatasetSequence(dnaSeq);
6047           cdnaSeqs.add(newSeq);
6048         }
6049       }
6050     }
6051     if (cdnaSeqs.size() == 0)
6052     {
6053       // show a warning dialog no mapped cDNA
6054       return;
6055     }
6056     AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs
6057             .size()]));
6058     AlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
6059             AlignFrame.DEFAULT_HEIGHT);
6060     cdna.alignAs(alignment);
6061     String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
6062             + this.title;
6063     Desktop.addInternalFrame(alignFrame, newtitle,
6064             AlignFrame.DEFAULT_WIDTH,
6065             AlignFrame.DEFAULT_HEIGHT);
6066   }
6067
6068   /**
6069    * Set visibility of dna/protein complement view (available when shown in a
6070    * split frame).
6071    * 
6072    * @param show
6073    */
6074   @Override
6075   protected void showComplement_actionPerformed(boolean show)
6076   {
6077     SplitContainerI sf = getSplitViewContainer();
6078     if (sf != null) {
6079       sf.setComplementVisible(this, show);
6080     }
6081   }
6082
6083   public class ExportData
6084   {
6085     private AlignmentI alignment;
6086
6087     private String[] omitHidden;
6088
6089     private int[] startEnd;
6090
6091     private AlignExportSettingI settings;
6092
6093     public ExportData(AlignmentI align, String[] ommit, int[] startEnd,
6094             AlignExportSettingI settings)
6095     {
6096       this.alignment = align;
6097       this.omitHidden = ommit;
6098       this.startEnd = startEnd;
6099       this.settings = settings;
6100     }
6101
6102     public AlignmentI getAlignment()
6103     {
6104       return alignment;
6105     }
6106
6107     public void setAlignment(AlignmentI alignment)
6108     {
6109       this.alignment = alignment;
6110     }
6111
6112     public String[] getOmitHidden()
6113     {
6114       return omitHidden;
6115     }
6116
6117     public void setOmitHidden(String[] omitHidden)
6118     {
6119       this.omitHidden = omitHidden;
6120     }
6121
6122     public int[] getStartEndPostions()
6123     {
6124       return startEnd;
6125     }
6126
6127     public void setStartEndPostions(int[] startEnd)
6128     {
6129       this.startEnd = startEnd;
6130     }
6131
6132     public AlignExportSettingI getSettings()
6133     {
6134       return settings;
6135     }
6136
6137     public void setSettings(AlignExportSettingI settings)
6138     {
6139       this.settings = settings;
6140     }
6141   }
6142
6143 }
6144
6145 class PrintThread extends Thread
6146 {
6147   AlignmentPanel ap;
6148
6149   public PrintThread(AlignmentPanel ap)
6150   {
6151     this.ap = ap;
6152   }
6153
6154   static PageFormat pf;
6155
6156   @Override
6157   public void run()
6158   {
6159     PrinterJob printJob = PrinterJob.getPrinterJob();
6160
6161     if (pf != null)
6162     {
6163       printJob.setPrintable(ap, pf);
6164     }
6165     else
6166     {
6167       printJob.setPrintable(ap);
6168     }
6169
6170     if (printJob.printDialog())
6171     {
6172       try
6173       {
6174         printJob.print();
6175       } catch (Exception PrintException)
6176       {
6177         PrintException.printStackTrace();
6178       }
6179     }
6180   }
6181 }