Check for all gapped seqs
[jalview.git] / src / jalview / gui / Desktop.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.gui;\r
20 \r
21 import jalview.io.*;\r
22 \r
23 import java.awt.*;\r
24 import java.awt.datatransfer.*;\r
25 import java.awt.dnd.*;\r
26 import java.awt.event.*;\r
27 import java.util.*;\r
28 \r
29 import javax.swing.*;\r
30 \r
31 \r
32 /**\r
33  * DOCUMENT ME!\r
34  *\r
35  * @author $author$\r
36  * @version $Revision$\r
37  */\r
38 public class Desktop extends jalview.jbgui.GDesktop\r
39     implements DropTargetListener, ClipboardOwner\r
40 {\r
41     /** DOCUMENT ME!! */\r
42     public static Desktop instance;\r
43     public static JDesktopPane desktop;\r
44     static int openFrameCount = 0;\r
45     static final int xOffset = 30;\r
46     static final int yOffset = 30;\r
47     public static jalview.ws.Discoverer discoverer;\r
48 \r
49     public static Object [] jalviewClipboard;\r
50 \r
51     static int fileLoadingCount= 0;\r
52 \r
53     /**\r
54      * Creates a new Desktop object.\r
55      */\r
56     public Desktop()\r
57     {\r
58         instance = this;\r
59 \r
60         Image image = null;\r
61 \r
62         try\r
63         {\r
64             java.net.URL url = getClass().getResource("/images/logo.gif");\r
65 \r
66             if (url != null)\r
67             {\r
68                 image = java.awt.Toolkit.getDefaultToolkit().createImage(url);\r
69 \r
70                 MediaTracker mt = new MediaTracker(this);\r
71                 mt.addImage(image, 0);\r
72                 mt.waitForID(0);\r
73                 setIconImage(image);\r
74             }\r
75         }\r
76         catch (Exception ex)\r
77         {\r
78         }\r
79 \r
80         setTitle("Jalview "+jalview.bin.Cache.getProperty("VERSION"));\r
81         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
82         desktop = new JDesktopPane();\r
83         desktop.setBackground(Color.white);\r
84         getContentPane().setLayout(new BorderLayout());\r
85         getContentPane().add(desktop, BorderLayout.CENTER);\r
86         desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);\r
87 \r
88         // This line prevents Windows Look&Feel resizing all new windows to maximum\r
89         // if previous window was maximised\r
90         desktop.setDesktopManager(new DefaultDesktopManager());\r
91 \r
92         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\r
93         String x = jalview.bin.Cache.getProperty("SCREEN_X");\r
94         String y = jalview.bin.Cache.getProperty("SCREEN_Y");\r
95         String width = jalview.bin.Cache.getProperty("SCREEN_WIDTH");\r
96         String height = jalview.bin.Cache.getProperty("SCREEN_HEIGHT");\r
97 \r
98         if ((x != null) && (y != null) && (width != null) && (height != null))\r
99         {\r
100             setBounds(Integer.parseInt(x), Integer.parseInt(y),\r
101                 Integer.parseInt(width), Integer.parseInt(height));\r
102         }\r
103         else\r
104         {\r
105             setBounds((int) (screenSize.width - 900) / 2,\r
106                 (int) (screenSize.height - 650) / 2, 900, 650);\r
107         }\r
108 \r
109         this.addWindowListener(new WindowAdapter()\r
110             {\r
111                 public void windowClosing(WindowEvent evt)\r
112                 {\r
113                     quit();\r
114                 }\r
115             });\r
116 \r
117         this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));\r
118 \r
119         /////////Add a splashscreen on startup\r
120         /////////Add a splashscreen on startup\r
121         JInternalFrame frame = new JInternalFrame();\r
122 \r
123         SplashScreen splash = new SplashScreen(frame, image);\r
124         frame.setContentPane(splash);\r
125         frame.setLayer(JLayeredPane.PALETTE_LAYER);\r
126         frame.setLocation((int) ((getWidth() - 750) / 2),\r
127             (int) ((getHeight() - 160) / 2));\r
128 \r
129         addInternalFrame(frame, "", 750, 160, false);\r
130 \r
131         discoverer=new jalview.ws.Discoverer(); // Only gets started if gui is displayed.\r
132 \r
133     }\r
134 \r
135     /**\r
136      * DOCUMENT ME!\r
137      *\r
138      * @param frame DOCUMENT ME!\r
139      * @param title DOCUMENT ME!\r
140      * @param w DOCUMENT ME!\r
141      * @param h DOCUMENT ME!\r
142      */\r
143     public static synchronized void addInternalFrame(final JInternalFrame frame,\r
144         String title, int w, int h)\r
145     {\r
146         addInternalFrame(frame, title, w, h, true);\r
147     }\r
148 \r
149     /**\r
150      * DOCUMENT ME!\r
151      *\r
152      * @param frame DOCUMENT ME!\r
153      * @param title DOCUMENT ME!\r
154      * @param w DOCUMENT ME!\r
155      * @param h DOCUMENT ME!\r
156      * @param resizable DOCUMENT ME!\r
157      */\r
158     public static synchronized void addInternalFrame(final JInternalFrame frame,\r
159         String title, int w, int h, boolean resizable)\r
160     {\r
161 \r
162       frame.setTitle(title);\r
163       if(frame.getWidth()<1 || frame.getHeight()<1)\r
164       {\r
165         frame.setSize(w, h);\r
166       }\r
167       // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN\r
168       // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN\r
169       // IF JALVIEW IS RUNNING HEADLESS\r
170       /////////////////////////////////////////////////\r
171       if (System.getProperty("java.awt.headless") != null\r
172           && System.getProperty("java.awt.headless").equals("true"))\r
173       {\r
174         return;\r
175       }\r
176 \r
177 \r
178         openFrameCount++;\r
179 \r
180         frame.setVisible(true);\r
181         frame.setClosable(true);\r
182         frame.setResizable(resizable);\r
183         frame.setMaximizable(resizable);\r
184         frame.setIconifiable(resizable);\r
185         frame.setFrameIcon(null);\r
186 \r
187         if (frame.getX()<1 && frame.getY()<1)\r
188        {\r
189          frame.setLocation(xOffset * openFrameCount, yOffset * ((openFrameCount-1)%10)+yOffset);\r
190        }\r
191 \r
192         final JMenuItem menuItem = new JMenuItem(title);\r
193         frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()\r
194             {\r
195               public void internalFrameActivated(javax.swing.event.\r
196                                                  InternalFrameEvent evt)\r
197               {\r
198                 JInternalFrame itf = desktop.getSelectedFrame();\r
199                 if (itf != null)\r
200                   itf.requestFocus();\r
201 \r
202               }\r
203 \r
204                 public void internalFrameClosed(\r
205                     javax.swing.event.InternalFrameEvent evt)\r
206                 {\r
207                     openFrameCount--;\r
208                     windowMenu.remove(menuItem);\r
209                     JInternalFrame itf = desktop.getSelectedFrame();\r
210                        if (itf != null)\r
211                         itf.requestFocus();\r
212                 }\r
213                 ;\r
214             });\r
215 \r
216         menuItem.addActionListener(new ActionListener()\r
217             {\r
218                 public void actionPerformed(ActionEvent e)\r
219                 {\r
220                     try\r
221                     {\r
222                         frame.setSelected(true);\r
223                         frame.setIcon(false);\r
224                     }\r
225                     catch (java.beans.PropertyVetoException ex)\r
226                     {\r
227 \r
228                     }\r
229                 }\r
230             });\r
231 \r
232         windowMenu.add(menuItem);\r
233 \r
234         desktop.add(frame);\r
235         frame.toFront();\r
236         try{\r
237           frame.setSelected(true);\r
238           frame.requestFocus();\r
239         }catch(java.beans.PropertyVetoException ve)\r
240         {}\r
241     }\r
242 \r
243     public void lostOwnership(Clipboard clipboard, Transferable contents)\r
244     {\r
245       Desktop.jalviewClipboard = null;\r
246     }\r
247 \r
248     public void dragEnter(DropTargetDragEvent evt)\r
249     {}\r
250 \r
251     public void dragExit(DropTargetEvent evt)\r
252     {}\r
253 \r
254     public void dragOver(DropTargetDragEvent evt)\r
255     {}\r
256 \r
257     public void dropActionChanged(DropTargetDragEvent evt)\r
258     {}\r
259 \r
260     /**\r
261      * DOCUMENT ME!\r
262      *\r
263      * @param evt DOCUMENT ME!\r
264      */\r
265     public void drop(DropTargetDropEvent evt)\r
266     {\r
267         Transferable t = evt.getTransferable();\r
268         java.util.List files = null;\r
269 \r
270         try\r
271         {\r
272           DataFlavor uriListFlavor = new DataFlavor("text/uri-list;class=java.lang.String");\r
273           if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))\r
274           {\r
275             //Works on Windows and MacOSX\r
276             evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);\r
277             files = (java.util.List) t.getTransferData(DataFlavor.javaFileListFlavor);\r
278           }\r
279           else if (t.isDataFlavorSupported(uriListFlavor))\r
280           {\r
281             // This is used by Unix drag system\r
282             evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);\r
283             String data = (String) t.getTransferData(uriListFlavor);\r
284             files = new java.util.ArrayList(1);\r
285             for (java.util.StringTokenizer st = new java.util.StringTokenizer(\r
286                 data,\r
287                 "\r\n");\r
288                  st.hasMoreTokens(); )\r
289             {\r
290               String s = st.nextToken();\r
291               if (s.startsWith("#"))\r
292               {\r
293                 // the line is a comment (as per the RFC 2483)\r
294                 continue;\r
295               }\r
296 \r
297               java.net.URI uri = new java.net.URI(s);\r
298               java.io.File file = new java.io.File(uri);\r
299               files.add(file);\r
300             }\r
301           }\r
302         }\r
303         catch (Exception e)\r
304         {\r
305           e.printStackTrace();\r
306         }\r
307 \r
308         if (files != null)\r
309         {\r
310           try\r
311           {\r
312             for (int i = 0; i < files.size(); i++)\r
313             {\r
314               String file = files.get(i).toString();\r
315               String protocol = FormatAdapter.FILE;\r
316               String format = null;\r
317 \r
318               if (file.endsWith(".jar"))\r
319               {\r
320                 format = "Jalview";\r
321 \r
322               }\r
323               else\r
324               {\r
325                 format = new IdentifyFile().Identify(file,\r
326                                                           protocol);\r
327               }\r
328 \r
329 \r
330               new FileLoader().LoadFile(file, protocol, format);\r
331 \r
332             }\r
333           }\r
334           catch (Exception ex)\r
335           {\r
336             ex.printStackTrace();\r
337           }\r
338         }\r
339     }\r
340 \r
341     /**\r
342      * DOCUMENT ME!\r
343      *\r
344      * @param e DOCUMENT ME!\r
345      */\r
346     public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)\r
347     {\r
348         JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(\r
349                     "LAST_DIRECTORY"),\r
350                 new String[]\r
351                 {\r
352                     "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc",\r
353                     "jar"\r
354                 },\r
355                 new String[]\r
356                 {\r
357                     "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "Jalview"\r
358                 }, jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));\r
359 \r
360         chooser.setFileView(new JalviewFileView());\r
361         chooser.setDialogTitle("Open local file");\r
362         chooser.setToolTipText("Open");\r
363 \r
364         int value = chooser.showOpenDialog(this);\r
365 \r
366         if (value == JalviewFileChooser.APPROVE_OPTION)\r
367         {\r
368             String choice = chooser.getSelectedFile().getPath();\r
369             jalview.bin.Cache.setProperty("LAST_DIRECTORY",\r
370                 chooser.getSelectedFile().getParent());\r
371 \r
372             String format = null;\r
373             if (chooser.getSelectedFormat().equals("Jalview"))\r
374             {\r
375                 format = "Jalview";\r
376             }\r
377             else\r
378             {\r
379                 format = new IdentifyFile().Identify(choice, FormatAdapter.FILE);\r
380             }\r
381 \r
382             if (viewport != null)\r
383               new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE, format);\r
384             else\r
385               new FileLoader().LoadFile(choice, FormatAdapter.FILE, format);\r
386         }\r
387     }\r
388 \r
389 \r
390 \r
391     /**\r
392      * DOCUMENT ME!\r
393      *\r
394      * @param e DOCUMENT ME!\r
395      */\r
396     public void inputURLMenuItem_actionPerformed(AlignViewport viewport)\r
397     {\r
398       // This construct allows us to have a wider textfield\r
399       // for viewing\r
400       JLabel label = new JLabel("Enter URL of Input File");\r
401       final JComboBox history = new JComboBox();\r
402 \r
403       JPanel panel = new JPanel(new GridLayout(2,1));\r
404       panel.add(label);\r
405       panel.add(history);\r
406       history.setPreferredSize(new Dimension(400,20));\r
407       history.setEditable(true);\r
408       history.addItem("http://www.");\r
409 \r
410       String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");\r
411 \r
412       StringTokenizer st;\r
413 \r
414       if (historyItems != null)\r
415       {\r
416         st = new StringTokenizer(historyItems, "\t");\r
417 \r
418         while (st.hasMoreTokens())\r
419         {\r
420           history.addItem(st.nextElement());\r
421         }\r
422       }\r
423 \r
424        int reply = JOptionPane.showInternalConfirmDialog(desktop,\r
425           panel, "Input Alignment From URL",\r
426           JOptionPane.OK_CANCEL_OPTION );\r
427 \r
428 \r
429         if (reply != JOptionPane.OK_OPTION )\r
430         {\r
431             return;\r
432         }\r
433 \r
434         String url = history.getSelectedItem().toString();\r
435 \r
436         if (url.toLowerCase().endsWith(".jar"))\r
437         {\r
438           if (viewport != null)\r
439             new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, "Jalview");\r
440           else\r
441             new FileLoader().LoadFile(url, FormatAdapter.URL, "Jalview");\r
442         }\r
443         else\r
444         {\r
445           String format = new IdentifyFile().Identify(url, FormatAdapter.URL);\r
446 \r
447           if (format.equals("URL NOT FOUND"))\r
448           {\r
449             JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
450                                                   "Couldn't locate " + url,\r
451                                                   "URL not found",\r
452                                                   JOptionPane.WARNING_MESSAGE);\r
453 \r
454             return;\r
455           }\r
456 \r
457           if (viewport != null)\r
458             new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, format);\r
459           else\r
460             new FileLoader().LoadFile(url, FormatAdapter.URL, format);\r
461         }\r
462     }\r
463 \r
464     /**\r
465      * DOCUMENT ME!\r
466      *\r
467      * @param e DOCUMENT ME!\r
468      */\r
469     public void inputTextboxMenuItem_actionPerformed(AlignViewport viewport)\r
470     {\r
471         CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
472         cap.setForInput(viewport);\r
473         Desktop.addInternalFrame(cap, "Cut & Paste Alignment File", 600, 500);\r
474     }\r
475 \r
476     /*\r
477      * Exit the program\r
478      */\r
479     public void quit()\r
480     {\r
481         jalview.bin.Cache.setProperty("SCREEN_X", getBounds().x + "");\r
482         jalview.bin.Cache.setProperty("SCREEN_Y", getBounds().y + "");\r
483         jalview.bin.Cache.setProperty("SCREEN_WIDTH", getWidth() + "");\r
484         jalview.bin.Cache.setProperty("SCREEN_HEIGHT", getHeight() + "");\r
485         System.exit(0);\r
486     }\r
487 \r
488     /**\r
489      * DOCUMENT ME!\r
490      *\r
491      * @param e DOCUMENT ME!\r
492      */\r
493     public void aboutMenuItem_actionPerformed(ActionEvent e)\r
494     {\r
495       StringBuffer message = new StringBuffer("JalView version " +\r
496                                               jalview.bin.Cache.getProperty(\r
497           "VERSION") +\r
498                                               "; last updated: " +\r
499                                               jalview.bin.\r
500                                               Cache.getDefault("BUILD_DATE", "unknown"));\r
501 \r
502       if (!jalview.bin.Cache.getProperty("LATEST_VERSION").equals(\r
503           jalview.bin.Cache.getProperty("VERSION")))\r
504       {\r
505         message.append("\n\n!! Jalview version "\r
506                        + jalview.bin.Cache.getProperty("LATEST_VERSION")\r
507                        + " is available for download from http://www.jalview.org !!\n");\r
508 \r
509       }\r
510 \r
511       message.append( "\nAuthors:  Michele Clamp, James Cuff, Steve Searle, Andrew Waterhouse, Jim Procter & Geoff Barton." +\r
512             "\nCurrent development managed by Andrew Waterhouse; Barton Group, University of Dundee." +\r
513             "\nFor all issues relating to Jalview, email help@jalview.org" +\r
514             "\n\nIf  you use JalView, please cite:" +\r
515             "\n\"Clamp, M., Cuff, J., Searle, S. M. and Barton, G. J. (2004), The Jalview Java Alignment Editor\"" +\r
516             "\nBioinformatics,  2004 20;426-7.");\r
517 \r
518         JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
519 \r
520            message.toString(), "About Jalview",\r
521             JOptionPane.INFORMATION_MESSAGE);\r
522     }\r
523 \r
524     /**\r
525      * DOCUMENT ME!\r
526      *\r
527      * @param e DOCUMENT ME!\r
528      */\r
529     public void documentationMenuItem_actionPerformed(ActionEvent e)\r
530     {\r
531         try\r
532         {\r
533             ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();\r
534             java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");\r
535             javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);\r
536 \r
537             javax.help.HelpBroker hb = hs.createHelpBroker();\r
538             hb.setCurrentID("home");\r
539             hb.setDisplayed(true);\r
540         }\r
541         catch (Exception ex)\r
542         {\r
543             ex.printStackTrace();\r
544         }\r
545     }\r
546 \r
547     /**\r
548      * DOCUMENT ME!\r
549      *\r
550      * @param e DOCUMENT ME!\r
551      */\r
552     protected void preferences_actionPerformed(ActionEvent e)\r
553     {\r
554         new Preferences();\r
555     }\r
556 \r
557     /**\r
558      * DOCUMENT ME!\r
559      *\r
560      * @param e DOCUMENT ME!\r
561      */\r
562     public void saveState_actionPerformed(ActionEvent e)\r
563     {\r
564         JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(\r
565                     "LAST_DIRECTORY"), new String[] { "jar" },\r
566                 new String[] { "Jalview Project" }, "Jalview Project");\r
567 \r
568         chooser.setFileView(new JalviewFileView());\r
569         chooser.setDialogTitle("Save State");\r
570 \r
571         int value = chooser.showSaveDialog(this);\r
572 \r
573         if (value == JalviewFileChooser.APPROVE_OPTION)\r
574         {\r
575             java.io.File choice = chooser.getSelectedFile();\r
576             jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());\r
577             new Jalview2XML().SaveState(choice);\r
578         }\r
579     }\r
580 \r
581     /**\r
582      * DOCUMENT ME!\r
583      *\r
584      * @param e DOCUMENT ME!\r
585      */\r
586     public void loadState_actionPerformed(ActionEvent e)\r
587     {\r
588         JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.getProperty(\r
589                     "LAST_DIRECTORY"), new String[] { "jar" },\r
590                 new String[] { "Jalview Project" }, "Jalview Project");\r
591         chooser.setFileView(new JalviewFileView());\r
592         chooser.setDialogTitle("Restore state");\r
593 \r
594         int value = chooser.showOpenDialog(this);\r
595 \r
596         if (value == JalviewFileChooser.APPROVE_OPTION)\r
597         {\r
598             String choice = chooser.getSelectedFile().getAbsolutePath();\r
599             jalview.bin.Cache.setProperty("LAST_DIRECTORY",\r
600                 chooser.getSelectedFile().getParent());\r
601             new Jalview2XML().LoadJalviewAlign(choice);\r
602         }\r
603     }\r
604 \r
605   /*  public void vamsasLoad_actionPerformed(ActionEvent e)\r
606     {\r
607       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.\r
608           getProperty("LAST_DIRECTORY"));\r
609 \r
610       chooser.setFileView(new JalviewFileView());\r
611       chooser.setDialogTitle("Load Vamsas file");\r
612       chooser.setToolTipText("Import");\r
613 \r
614       int value = chooser.showOpenDialog(this);\r
615 \r
616       if (value == JalviewFileChooser.APPROVE_OPTION)\r
617       {\r
618         jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(null);\r
619         vs.load(\r
620             chooser.getSelectedFile().getAbsolutePath()\r
621             );\r
622       }\r
623 \r
624     }*/\r
625 \r
626 \r
627     public void inputSequence_actionPerformed(ActionEvent e)\r
628     {\r
629       new SequenceFetcher(null);\r
630     }\r
631 \r
632     JPanel progressPanel;\r
633 \r
634     public void startLoading(final String fileName)\r
635     {\r
636       if (fileLoadingCount == 0)\r
637       {\r
638         progressPanel = new JPanel(new BorderLayout());\r
639         JProgressBar progressBar = new JProgressBar();\r
640         progressBar.setIndeterminate(true);\r
641 \r
642         progressPanel.add(new JLabel("Loading File: " + fileName + "   "),\r
643                           BorderLayout.WEST);\r
644 \r
645         progressPanel.add(progressBar, BorderLayout.CENTER);\r
646 \r
647         instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);\r
648       }\r
649       fileLoadingCount++;\r
650       validate();\r
651     }\r
652 \r
653     public void stopLoading()\r
654     {\r
655       fileLoadingCount--;\r
656       if (fileLoadingCount < 1)\r
657       {\r
658         if(progressPanel!=null)\r
659         {\r
660           this.getContentPane().remove(progressPanel);\r
661           progressPanel = null;\r
662         }\r
663         fileLoadingCount = 0;\r
664       }\r
665       validate();\r
666     }\r
667 \r
668 }\r
669 \r