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