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