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