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