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