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