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