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