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