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