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