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