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