1    /*
2    Copyright 2000 by Ralph Hartley
3    This software is licenced under the terms of the
4    Gnu Public Licence
5    */
6    import javax.swing.*;
7    import java.awt.event.*;
8    import java.awt.*;
9    import java.awt.print.*;
10   import java.util.*;
11   import java.util.zip.*;
12   import java.io.*;
13   import java.lang.ref.*;
14   
15   /**The top level interface.
16    *This is the "Carto window" that the user sees when he opens Carto.
17    */
18   public class CartoFrame extends JFrame implements ActionListener,Styleable{
19   
20     /**The contents of the file that is being edited. */
21     Carto carto;
22   
23     /**The desktop that contains all sub-windows. */
24     JDesktopPane desk;
25   
26     public static String SEGWORD = "Segment";
27     public static String COMPWORD = "Plan";
28     public static String SECTION = "XSection";
29   
30     public static String DRAWING = "Drawings";
31   
32       //For file menu
33     public static String NEWPROJECT = "New Project";
34     public static String NEW = "New empty file";
35     public static String OPEN = "Open...";
36     public static String SAVE = "Save";
37     public static String QUIT = "Exit";
38     public static String PRINT = "Print Enabled";
39     public static String PRINTSEL = "Print Selected";
40     public static String IPRINT = "Print Images";
41     public static String PREVIEW = "Print preview";
42     public static String PRINTSETUP = "Print Setup...";
43     public static String STATS = "Stats";
44   //  public static String MERGE = "Merge with...";
45   //  public static String SPLIT = "Split into...";
46     public static String SAVEAS = "Save as...";
47     public static String PREFS = "Global Prefs...";
48     public static String FPREFS = "File Prefs...";
49   
50       //For elements menu
51     public static String EDITSEGMENT = "Edit "+SEGWORD+"...";
52     public static String EDITCOMPOSITE = "Edit "+COMPWORD+"...";
53     public static String EDITSECTION = "Edit "+SECTION+"...";
54     public static String ADDSEGMENT = "New "+SEGWORD+"...";
55   //  public static String ADDSEGMENTV = "New Vertical Segment...";
56     public static String ADDCOMPOSITES = "New "+COMPWORD+" ...";
57   //  public static String ADDCOMPOSITEP = "New Composite (plain)...";
58     public static String ADDSECTION = "New "+SECTION+" ...";
59     public static String ADDPAGE = "New Page...";
60     public static String ADDSURVEY = "Set/Update";
61     public static String PURGESURVEY = "Purge";
62     public static String DELETE = "Delete ...";
63     public static String RECOOK = "Remorph all";
64     public static String HARDRECOOK = "Soft Remorph";
65     public static String IMAGELOAD = "Reload Images";
66     public static String STOPCOOK = "Interupt Morphing";
67     public static String STOPPRINT = "Interupt Printing";
68     public static String DELETESEG = "Del "+SEGWORD+" ...";
69     public static String IMPORTSEG = "Import "+SEGWORD+" ...";
70     public static String IMPORTCOMP = "Import "+COMPWORD+" ...";
71     public static String IMPORTSECTION = "Import "+SECTION+" ...";
72     public static String DELETECOMP = "Del "+COMPWORD+" ...";
73     public static String DELETESECTION = "Del "+SECTION+" ...";
74     public static String OLDER = "Previous Version";
75     public static String HISTORY = "Versions ...";
76     public static String NEWER = "Next Version";
77     public static String DELSTA = "Delete stations ...";
78     public static String DELSHOT = "Delete shots ...";
79   
80   //For debug menu
81     public static String DEBUG = "Debug";
82     public static String GC = "GC";
83     public static String ERROR = "Test Error";
84     public static String CLEAN = "Clean";
85     public static String CLOSEDITS = "Close editors";
86     public static String JAR = "Write debug file";
87     public static String ERRORFILE = "View error file";
88   
89   //For help menu
90     public static String HELP = "Help";
91     public static String ABOUT = "About";
92     public static String LICENCE = "Licence";
93   
94     /**Recook all segments when opening files? */
95     static public boolean cookonopen;
96   
97     /**Open last edited file when Carto opens? */
98     static public boolean lastopen;
99   
100    /**Open Carto maximized?.
101     *@deprecated Dosn't work because Java has no maximizing API. */
102    static public boolean openmaximized;
103  
104    /**Open new editors maximized? */
105    static public boolean openeditormaximized;
106  
107    /**Restore editors on open? */
108    static public boolean restoreplace;
109  
110    /**Save state of editors on save? */
111    static public boolean saveplace;
112  
113    /**Initial horizontal window position. */
114    static public int initialx;
115  
116    /**Initial vertical window position. */
117    static public int initialy;
118  
119    /**Initial window width. */
120    static public int initialwidth;
121  
122    /**Initial window height. */
123    static public int initialheight;
124  
125    /**The title to use when file is "New". */
126    public static String NEWTITLE = "Carto - New";
127  
128    /**The last CartoFrame created.
129     * Usually there is onlt one in the program anyway. */
130    public static CartoFrame topframe = null;
131  
132    /**The last editor in which a selection took place.
133     *used to direct events that apply to editors but have no
134     *independant way to know which one. (A bit of a hack). */
135    public Editor lastselected = null;
136  
137    /**The name of the file being edited.
138     *null if new. */
139    File file = null;
140  
141    /**The progress message. 
142     *Displayed at the bottom left of the window. Used
143     *by background job to tell the user about progress */
144    public static ThreadMessage message = new ThreadMessage();
145    /**Displays name of current survey.
146     *If there is one. Lower right corner of window. */
147    JLabel surveylabel = new JLabel("No Survey");
148    /**Status bar at bottom of window. */
149    JPanel statusbar = new JPanel();
150  
151  //  Vector linetypes = LineType.getDefaultTypes();
152    /**Has this file been changed?.
153     *If so, make sure to give user a chance to save before
154     *closeing. */
155    public boolean dirty = false;
156  
157   /**Build a top level window with all its menus.
158     *Once it is shown it will be ready to edit. */
159    public CartoFrame(File startfile) {
160      addWindowListener(new WindowAdapter() {
161        public void windowClosing(WindowEvent e) {
162          closefile();
163          shutDown();
164        }});
165  
166      topframe = this;
167  
168      setBounds(initialx,initialy,initialwidth,initialheight);
169  
170      desk = new JDesktopPane();
171      getContentPane().add(desk,BorderLayout.CENTER);
172  
173      JMenuBar bar = new JMenuBar();
174      getContentPane().add(bar,BorderLayout.NORTH);
175  
176      statusbar.setLayout(new BorderLayout());
177      statusbar.add(message,BorderLayout.WEST);
178      statusbar.add(surveylabel,BorderLayout.EAST);
179      getContentPane().add(statusbar,BorderLayout.SOUTH);
180  
181      JMenu menu = new JMenu("File");
182      menu.setMnemonic('f');
183      bar.add(menu);
184      addMenuItem(menu,NEW,'n');
185      addMenuItem(menu,NEWPROJECT,'j');
186      addMenuItem(menu,OPEN,'o');
187      addMenuItem(menu,SAVE,'s');
188      addMenuItem(menu,SAVEAS,'a');
189  
190  //    JMenu pmenu = new JMenu("Print");
191  //    pmenu.setMnemonic('p');
192  //    menu.add(pmenu);
193  //    addMenuItem(menu,IPRINT,'i');
194  //    addMenuItem(pmenu,PRINTSETUP);
195      addMenuItem(menu,PRINT,'p');
196      addMenuItem(menu,PRINTSEL,'l');
197      addMenuItem(menu,STOPPRINT,'i');
198  //    addMenuItem(menu,PREVIEW);
199  
200      addMenuItem(menu,STATS,'t');
201  //    addMenuItem(menu,MERGE);
202  //    addMenuItem(menu,SPLIT);
203      addMenuItem(menu,PREFS,'r');
204      addMenuItem(menu,FPREFS,'e');
205      addMenuItem(menu,QUIT,'x');
206  
207      menu = new JMenu("Survey");
208      menu.setMnemonic('u');
209      bar.add(menu);
210  //    addMenuItem(menu,ADDPAGE);
211      addMenuItem(menu,ADDSURVEY,'a');
212      addMenuItem(menu,PURGESURVEY,'g');
213      addMenuItem(menu,OLDER,'o');
214      addMenuItem(menu,NEWER,'n');
215  //    addMenuItem(menu,HISTORY);
216  //    addMenuItem(menu,DELSTA);
217  //    addMenuItem(menu,DELSHOT);
218  
219      menu = new JMenu(SEGWORD);
220      menu.setMnemonic('s');
221      bar.add(menu);
222      addMenuItem(menu,ADDSEGMENT,'a');
223  //    addMenuItem(menu,ADDSEGMENTV,'v');
224      addMenuItem(menu,EDITSEGMENT,'e');
225      addMenuItem(menu,DELETESEG,'d');
226      addMenuItem(menu,RECOOK,'r');
227      addMenuItem(menu,IMAGELOAD,'d');
228      addMenuItem(menu,STOPCOOK,'i');
229      addMenuItem(menu,IMPORTSEG,'m');
230      
231      menu = new JMenu(COMPWORD);
232      menu.setMnemonic('p');
233      bar.add(menu);
234      addMenuItem(menu,ADDCOMPOSITES,'n');
235      addMenuItem(menu,EDITCOMPOSITE,'e');
236      addMenuItem(menu,DELETECOMP,'d');
237  
238      menu = new JMenu(SECTION);
239      menu.setMnemonic('x');
240      bar.add(menu);
241      addMenuItem(menu,ADDSECTION,'n');
242      addMenuItem(menu,EDITSECTION,'e');
243      addMenuItem(menu,DELETESECTION,'d');
244  
245      menu = new JMenu(HELP);
246      menu.setMnemonic('h');
247      bar.add(menu);
248      addMenuItem(menu,HELP,'e');
249      addMenuItem(menu,ABOUT,'a');
250      addMenuItem(menu,LICENCE,'l');
251  
252      menu = new JMenu(DEBUG);
253      menu.setMnemonic('d');
254      bar.add(menu);
255      addMenuItem(menu,ERRORFILE,'f');
256      addMenuItem(menu,GC,'g');
257      addMenuItem(menu,ERROR,'e');
258      addMenuItem(menu,JAR,'b');
259      addMenuItem(menu,CLEAN,'c');
260      addMenuItem(menu,CLOSEDITS,'t');
261      addMenuItem(menu,HARDRECOOK,'r');
262  
263      if (startfile!=null) open(startfile);
264      else if (lastopen && Persist.current.last!=null) open(Persist.current.last);
265      else {
266        setTitle(NEWTITLE);
267        carto = new Carto();
268      }
269    }
270  
271    /** Change the line type of the last selection.
272     */
273    public void setLineType(LineType type,Size thick,Color color,Set subsel) {
274  
275      if (lastselected==null || !(lastselected instanceof CompEditor)) return;
276  
277      ((CompEditor)lastselected).pane.setLineType(type,thick,color);
278    }
279  
280    /**Add a menu item with a shortcut key. */
281    public void addMenuItem(JMenu menu,String comand,char mne) {
282      JMenuItem item = new JMenuItem(comand);
283      item.setMnemonic(mne);
284      item.addActionListener(this);
285      menu.add(item);
286    }
287  
288    /**Add a menu item without a shortcut key.
289     *The absence of a shortcut indecates that the function is not
290     *yet implemented. */
291    public void addMenuItem(JMenu menu,String comand) {
292      JMenuItem item = new JMenuItem(comand);
293      item.addActionListener(this);
294      menu.add(item);
295    }
296  
297  /*  public LineType getLineType(String name) {
298      for (Iterator it=LineType.predefined.iterator();it.hasNext();) {
299        LineType item = (LineType)it.next();
300        if (item.name.compareTo(name)==0) return(item);
301      }
302      System.out.println("Couldn't find linetype "+name);
303      return(null);
304    }
305  */
306  
307  /**
308   * Import elements of type given by word<br>
309   * Prompts for a file to read from, then pops up
310   * dialog to select which to import.
311   */
312    public void importElement(String word) {
313      dirty = true;
314      
315      if (Carto.background!=null) {
316        ErrorLog.log("Importing not permitted while printing or morphing");
317        return;
318      }
319  
320      File f = Carto.choose(null);
321      if (f==null) return;
322  
323      Carto source = Carto.read(f);
324      if (source==null) return;
325  
326      Object[] res = CartoFrame.selectFromList(carto.getList(word),
327          				     "Choose a " + word +
328          				     " to Import from "+f.getName(),
329          				     "Import " + word);
330      if (res==null) return;
331      for (int i=0;i<res.length;i++) {
332        res[i] = ((Symbol)res[i]).importSym(carto,source);
333        addEditor((Editable)res[i]);
334      }
335    }
336  
337    /**
338     * Pops up a dialog to choose an item from a list<br>
339     * Used when it only make sense to select at most one.
340     */
341    public static Element chooseFromList(java.util.List list,String message,String title) {
342      int i;
343      if (list==null || list.size()==0) return(null);
344      if (list.size()==1) return((Element)list.get(0));
345  
346      Object[] names = list.toArray();
347  //    for (i=0;i<list.size();i++)
348  //      names[i] = (Element)list.elementAt(i);
349      return((Element)JOptionPane.showInputDialog(null,
350          					message,
351          					title,
352          					JOptionPane.QUESTION_MESSAGE,
353          					null,
354          					names,
355          					names[0]));
356  
357    }
358  
359    /**
360     * Pops up a dialog to choose items from a list<br>
361     * User may select more than one.
362     */
363    public static Object[] selectFromList(java.util.List list,String message,String title) {
364      if (list==null || list.size()==0) return(null);
365      if (list.size()==1) return(list.toArray());
366  
367      Object[] names = list.toArray();
368      JList choices = new JList(names);
369      
370      if (JOptionPane.showOptionDialog(null,
371          			 new JScrollPane(choices),
372          			 title,
373          			 JOptionPane.OK_CANCEL_OPTION,
374          			 JOptionPane.QUESTION_MESSAGE,
375          			 null,
376          			 null,
377          			 null)
378          !=JOptionPane.OK_OPTION) return(null);
379  
380      return(choices.getSelectedValues());
381  //    Object[] ores = choices.getSelectedValues();
382  //    Element[] res = new Element[ores.length];
383  //    for (int i=0;i<ores.length;i++)
384  //      res[i] = (Element)ores[i];
385  
386  //    return(res);
387    }
388  
389    /**
390     * Edit one or more elements of named type */
391    public void editElement(String typestring) {
392        Object[] elems = selectFromList(carto.getList(typestring),
393          				       "Choose a "+typestring+" to edit.",
394          				       "Edit "+typestring);
395        if (elems!=null)
396          for (int i=0;i<elems.length;i++)
397            addEditor((Editable)elems[i]);
398    }
399  
400    /**
401     *Start a new Editor<br>
402     *Edits the given item.
403     */
404    public Editor addEditor(Editable elem) {
405      Editor editor = elem.getEditor(this);
406      if (editor != null) {
407        editor.editframe.setOpaque(true);
408        desk.add(editor.editframe,desk.getComponentCount());//,new Integer(editlist.size()+1));
409        editor.editframe.setVisible(true);
410        try {
411          editor.editframe.setMaximum(openeditormaximized);
412        } catch (java.beans.PropertyVetoException ex) {
413          ErrorLog.exception(ex,"Couldn't maximize editor");
414        }
415        if (elem instanceof Segment && Segment.openscaled)
416          ((SegEditor)editor).fitScale();
417  //      editlist.add(editor);
418  //      editor.requestFocus();
419      }
420      return(editor);
421    }
422  
423    /**
424     * Print selected pages.
425     */
426    public void printSelected() {
427      dirty=true;
428      Print.printer.printSelected(this,message);
429    }
430  
431    /**
432     * Print all pages.
433     */
434    public void print() {
435      dirty=true;
436      Print.printer.printAll(carto,message);
437    }
438  
439    /**
440     * Read a survey file<br>
441     * Creates or updates the survey.
442     */
443    public void addSurvey() {
444      dirty = true;
445      
446      if (carto.survey!=null) carto.prepareForSurveyUpdate();
447  
448      Survey newsurvey = Survey.open(carto.survey);
449      if (newsurvey!=null) {
450        carto.survey = newsurvey;
451        surveylabel.setText(carto.survey.name);
452        forceReCook();
453        carto.useSurveyUpdate();
454      }
455    }
456  
457    /**
458     * Create a new, empty, .cto file
459     */
460    public boolean newFile() {
461      if (!closefile()) return(false);
462  
463      carto = new Carto();
464      Persist.current.last = file;
465      Persist.save();
466      file=null;
467      surveylabel.setText("No Survey");
468      setTitle(NEWTITLE);
469  
470      return(true);
471    }
472  
473    /**
474     * Called when a menu item is selected.<br>
475     * Decode the menu item and execute the command.
476     */
477    public void actionPerformed(ActionEvent e) {
478      if (e.getSource() instanceof AbstractButton) {
479        String command = ((AbstractButton)e.getSource()).getActionCommand();
480  //      System.out.println("Command = "+command);
481        // File menu items
482        if (command == NEW)
483          newFile();
484  
485        else if (command == NEWPROJECT) {
486          if (newFile()) {
487            addSurvey();
488            Comp comp = new Comp("plan",carto.survey,false);
489  
490            carto.complist.add(comp);
491            addEditor(comp);
492            dirty = false;
493          }
494        }
495        else if (command == OPEN) open();
496        else if (command == SAVE) {dirty = true;save(file);}
497        else if (command == SAVEAS) save(null);
498        else if (command == PRINT) print();
499        else if (command == PRINTSEL) printSelected();
500  //      if (command == IPRINT) new Print(carto).iPrint(message);
501  //      if (command == PREVIEW) new Print(carto).preview();
502        else if (command == PREFS) (new Prefs(this)).show();
503        else if (command == FPREFS) (new Prefs(this,carto)).show();
504        else if (command == PRINTSETUP) Print.printSetup();
505        else if (command == QUIT && closefile()) shutDown();
506        else if (command == CLOSEDITS) closeEditors();
507        else if (command == GC) System.gc();
508        else if (command == HARDRECOOK) {
509          JInternalFrame[] eds = desk.getAllFrames();
510          for (int i=0;i<eds.length;i++)
511            ((EditFrame)eds[i]).edit.saveData();
512          carto.reCookAll(message,this);
513          dirty=true;
514        }
515        else if (command == RECOOK) {
516          JInternalFrame[] eds = desk.getAllFrames();
517          for (int i=0;i<eds.length;i++)
518            ((EditFrame)eds[i]).edit.saveData();
519          carto.hardReCookAll(message,this);
520          dirty=true;
521        }
522        else if (command == IMAGELOAD) {
523          ImageFile.flushImages();
524          JInternalFrame[] eds = desk.getAllFrames();
525          for (int i=0;i<eds.length;i++) {
526            Editor ed = ((EditFrame)eds[i]).edit;
527            if (ed instanceof SegEditor) {
528              ((SegEditor)ed).pane.image = ((SegEditor)ed).seg.getImage();
529              ed.repaint();
530            }
531            ed.saveData();
532          }
533          carto.reCookAll(message,this);
534          dirty=true;
535        }
536        else if (command == STOPPRINT) stopPrint();
537        else if (command == STOPCOOK) stopReCook();
538        else if (command == CLEAN) carto.clean();
539        else if (command == ERROR) throw new RuntimeException("this is a test");
540        else if (command == DELETECOMP) deleteElement(COMPWORD);
541        else if (command == DELETESECTION) deleteElement(SECTION);
542        else if (command == DELETESEG) deleteElement(SEGWORD);
543        else if (command == OLDER) {
544          carto.survey.undo();
545          surveylabel.setText(carto.survey.name);
546          forceReCook();
547        }
548        else if (command == NEWER) {
549          carto.survey.redo();
550          surveylabel.setText(carto.survey.name);
551          forceReCook();
552        }
553        else if (command==STATS) {
554  //        System.out.println("Stats");
555          try {
556            PrintStream out = new PrintStream(new FileOutputStream("stats.txt"));
557            out.println("Carto file "+file.getName());
558            carto.printStats(out);
559  //          carto.printStats(System.out);
560            out.flush();
561            out.close();
562          } catch (IOException ex) {
563            ErrorLog.exception(ex,"Could not write stats.txt");
564          }
565          Help.showFile(this,"stats.txt","stats.txt");
566        }
567        else if (command==LICENCE) Help.showFile(this,"gpl.txt","Licence");
568        else if (command==ERRORFILE) Help.showFile(this,"error.txt","Error File");
569        else if (command==JAR) new DebugFile(this);
570        else if (command==ABOUT) Help.about(this);
571        else if (command==HELP) (new Help()).show();
572        
573        //Component menu items
574        else if (command == ADDSURVEY)
575          addSurvey();
576  
577        else if (command == PURGESURVEY && carto.survey!=null)
578          carto.survey.purge(carto);
579  
580        else if (command == ADDSEGMENT) {
581  // || command == ADDSEGMENTV) {
582          Segment newseg = new Segment(carto.survey,false);
583          if (newseg!=null && newseg.check()) {
584            dirty = true;
585            carto.segmentlist.add(newseg);
586            addEditor(newseg);
587          }
588        }
589  
590        else if (command == IMPORTCOMP) importElement(COMPWORD);
591        else if (command == IMPORTSEG) importElement(SEGWORD);
592  
593        else if (command == EDITSEGMENT) editElement(SEGWORD);
594        else if (command == EDITCOMPOSITE) editElement(COMPWORD);
595        else if (command == EDITSECTION) editElement(SECTION);
596        else if (command == ADDCOMPOSITES) {
597          String name = (Symbol.makenames?null:JOptionPane.showInputDialog("Name of new "+COMPWORD));
598          Comp comp = new Comp(name,carto.survey,false);
599          dirty = true;
600          carto.complist.add(comp);
601          addEditor(comp);
602        }
603        else if (command==ADDSECTION) {
604          String name = (Symbol.makenames?null:JOptionPane.showInputDialog("Name of new "+SECTION));
605          SectionComp comp = new SectionComp(name,null);
606          System.out.println("ADDSECTION comp = "+comp);
607  
608  //          Segment newseg = (Segment)chooseFromList(carto.segmentlist,
609  //        					   "Segment",
610  //        					   "Segment");
611  //          if (newseg!=null)
612  //            comp.add(newseg,Comp.BOTTOM);
613  //        }
614  //        else comp = new Comp(name,null,false);
615  
616          dirty = true;
617          carto.sectionlist.add(comp);
618          addEditor(comp);
619        }
620  /*      else if (command==ADDCOMPOSITEP) {
621          String name = (Symbol.makenames?null:JOptionPane.showInputDialog("Name of new composite"));
622          Comp comp = new Comp(name,null,false);
623          dirty = true;
624          carto.complist.add(comp);
625          addEditor(comp);
626        }
627  */
628      }
629    }
630  
631    /**
632     * Delete one or more elements of the given type<br>
633     * Pops up a dialog to choose which, then another to confirm.
634     * Closes any editors that are editing deleted items
635     * and uses recursiveDelete to fix dependencies.
636     */
637    public void deleteElement(String typestring) {
638      Vector elist = carto.getList(typestring);
639      Object[] elem = selectFromList(elist,
640          			   "Choose a "+typestring+" to delete.",
641          			   "Delete "+typestring);
642      if (elem==null) return;
643      StringBuffer buf = new StringBuffer("Really Delete"+typestring+(elem.length>1?"s\n":"\n"));
644      for (int i=0;i<elem.length;i++) {
645        buf.append(((Symbol)elem[i]).getName());
646        if (i<elem.length-1) buf.append("\n");
647      }
648      buf.append("?");
649      if (JOptionPane.YES_OPTION ==
650          JOptionPane.showConfirmDialog(this,buf.toString(),
651          			      "delete",
652          			      JOptionPane.YES_NO_OPTION)) {
653        dirty = true;
654  
655        for (int i=0;i<elem.length;i++) {
656          elist.remove(elem[i]);
657          Component[] contents = desk.getComponents();
658          for (int j=0;j<contents.length;j++) {
659            if ((contents[j] instanceof EditFrame) &&
660                ((EditFrame)contents[j]).edit.target==elem[i]) {
661              desk.remove((EditFrame)contents[j]);
662              repaint();
663            }
664          }
665  
666          for (Iterator it = carto.complist.iterator();it.hasNext();) {
667            Comp composite = (Comp) it.next();
668            composite.recursiveDelete((Symbol)elem[i]);
669          }
670        }
671      }
672    }
673  
674    /**
675     * Close all editors.
676     */
677    public void closeEditors() {
678      Component[] contents = desk.getComponents();
679      for (int j=0;j<contents.length;j++)
680        if (contents[j] instanceof EditFrame)
681          desk.remove((EditFrame)contents[j]);
682      repaint();
683    }
684  
685    /**
686     * Close Carto
687     */
688    public void shutDown() {
689      Persist.save();
690      System.out.println("Exit RunID = "+Persist.current.getRunID());
691      System.exit(0);
692    }
693  
694    /**
695     *Open a new .cto file<br>
696     *Closes the old one if needed.
697     */
698    public void open() {
699      int type;
700      String title;
701      Editor ed;
702  
703      if (!closefile()) return;
704  
705      File f = Carto.choose(this);
706      if (f!=null) open(f);
707      else setTitle(NEWTITLE);
708    }
709  
710    public void open(File filename) {
711      if (!closefile()) return;
712  
713      Carto result = Carto.read(filename);
714      if (result != null) {
715        carto = (Carto)result;
716        dirty = false;
717        file = filename;
718        showname(file);
719        carto.clean();
720        // the cooked images are not saved - tell segments to re-cook.
721        // Use another thread because it may take a while.
722        if (cookonopen) forceReCook();
723        if (restoreplace && carto.place!=null)
724          EditState.restoreAll(carto.place,this);
725      }
726      else carto = new Carto();
727      repaint();
728    }
729  
730    /** Update the displayed name of the current file */
731    void showname(File file) {
732      Persist.current.last = file;
733      Persist.save();
734      setTitle("Carto - "+file.getName());
735      if (carto.survey!=null)
736        surveylabel.setText(carto.survey.name);
737  //      surveylabel.setText("Survey");
738    }
739  
740    /**
741     * Close the current .cto file<br>
742     * If it is dirty, give the user a chance to cancel or save.
743     */
744    public boolean closefile() {
745  
746      if (carto==null)return(true);
747      for (boolean saveit=dirty;saveit;) {
748        int res  = JOptionPane.showConfirmDialog(this,"file changed - save?","Close",
749          			      JOptionPane.YES_NO_CANCEL_OPTION);
750        if (res==JOptionPane.CANCEL_OPTION) return(false);
751        if (res==JOptionPane.NO_OPTION) saveit = false;
752        if (res==JOptionPane.YES_OPTION) saveit = !save(file);
753      }
754      stopReCook();
755      lastselected = null;
756      JInternalFrame[] editors = desk.getAllFrames();
757  //    desk.removeAll();
758      desk.setSelectedFrame(null);
759      for (int i=0;i<editors.length;i++) {
760        try {
761          editors[i].setClosed(true);
762        } 
763        catch (java.beans.PropertyVetoException ex) {
764          editors[i].dispose();
765        }
766        desk.remove(editors[i]);
767      }
768      editors=null;
769      file=null;
770      dirty = false;
771      repaint();
772  //    Editor.findRefs(desk,SegEditor.class,2);
773      return(true);
774    }
775  
776    /**
777     * Save the current file<br>
778     * If savefile is null, then do save-as,
779     * otherwise save to savefile if dirty.
780     * If savefile is not null and the file is not
781     * dirty, the file is already saved, so do nothing.
782     */
783    public boolean save(File savefile) {
784  
785      File backfile = null;
786      if (savefile==null) {
787        JFileChooser choose = Persist.current.getFileChooser();
788        choose.setFileFilter(new FileTypes(FileTypes.CARTO));
789        choose.rescanCurrentDirectory();
790        if (choose.showSaveDialog(this)!=choose.APPROVE_OPTION) return(false);
791        savefile = choose.getSelectedFile();
792        if (!savefile.getName().endsWith(".cto") &&
793            !savefile.getName().endsWith(".zip") &&
794            !savefile.getName().endsWith(".jar"))
795          savefile = new File(savefile.toString().concat(".cto"));
796        dirty = true;
797        showname(savefile);
798      }
799  
800      if (dirty) {
801        if (saveplace) carto.place = EditState.saveEditors(desk);
802        carto.saved_version = Carto.runversion.getVersion();
803        try {
804          backfile = null;
805          if (savefile.exists()) {
806            backfile = new File(savefile.getParent(),"backup_of_"+savefile.getName());
807            savefile.renameTo(backfile);
808          }
809          OutputStream ostream = new FileOutputStream(savefile);
810          if (savefile.getName().endsWith(".cto")) {
811            ImageFile.fileSave(carto);
812            ObjectOutputStream out = new ObjectOutputStream(ostream);
813            carto.zipfile = null;
814            carto.zipent = null;
815            carto.base = savefile.getCanonicalFile().getParentFile();
816            out.writeObject(carto);
817            out.flush();
818          }
819          else {
820            if (!savefile.getName().endsWith(".zip")) 
821              ErrorLog.log(savefile.getName()+"is not .cto or .zip");
822            ZipOutputStream zout = new ZipOutputStream(ostream);
823            zout.setLevel(8);
824            String name = savefile.getName();
825            name = name.substring(0,name.lastIndexOf(".")) + ".cto";
826            ZipEntry zipent = new ZipEntry(name);
827            zout.putNextEntry(zipent);
828            ObjectOutputStream out = new ObjectOutputStream(zout);
829            out.writeObject(carto);
830            out.flush();
831            zout.closeEntry();
832            ImageFile.zipSave(zout,carto,backfile);
833            zout.finish();
834          }
835          ostream.close();
836          dirty =  false;
837        } catch (IOException e) {
838            ErrorLog.log("I/O error writing: "+savefile);
839            ErrorLog.exception(e);
840            return(false);
841        }
842        file = savefile;
843      }
844      return(true);
845    }
846  
847    /**
848     * Stop background Cooking, if any.
849     */
850    public void stopReCook() {
851      Carto.stopBackground(Carto.COOKKILL);
852      ImageThread.stopLoading();
853    }
854  
855    /**
856     * Stop background printing, if any.
857     */
858    public void stopPrint() {
859      Carto.stopBackground(Carto.PRINTKILL);
860    }
861  
862    /**
863     * Recook all segments.
864     * Cancel any cooking that was already running.
865     */
866    void forceReCook() {
867      stopReCook();
868      carto.reCookAll(message,this);
869    }
870  }
871