001    /* BEE - Book Editing Environment for PAVLOV
002     * Copyright (C) 2004 T.J. Willis
003     * 
004     * This program is free software; you can redistribute it and/or
005     * modify it under the terms of the GNU General Public License
006     * as published by the Free Software Foundation; either version 2
007     * of the License, or (at your option) any later version.
008     * 
009     * This program is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     * GNU General Public License for more details.
013     * 
014     * You should have received a copy of the GNU General Public License
015     * along with this program; if not, write to the Free Software
016     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
017     *
018     * $Header: /cvsroot/pavlov/net/sourceforge/bee/Bee.java,v 1.20 2004/06/24 07:56:15 tj_willis Exp $
019     */
020    package net.sourceforge.bee;
021    
022    import java.util.Hashtable;
023    import java.awt.*;
024    import java.awt.event.*;
025    import javax.help.*;
026    import javax.swing.*;
027    import net.sourceforge.pavlov.startup.*;
028    import net.sourceforge.pavlov.library.*;
029    import net.sourceforge.steelme.*;
030    import net.sourceforge.pavlov.swing.HelpSystem;
031    import net.sourceforge.pavlov.randommedia.ImageIconUtilities;
032    import net.sourceforge.pavlov.zipUtilities.XMLFileFilter;
033    import java.io.*;
034    import java.net.URL;
035    import org.xml.sax.*;
036    import org.xml.sax.helpers.*;
037    
038    /**
039     * Main class for BEE Editor for Pavlov.
040     *
041     * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
042     * @version $Revision: 1.20 $
043     */
044    public class Bee extends JFrame
045                            implements ActionListener
046    {
047    // FIXME: Bee really should have a single-document interface
048    // FIXME: investigate http://txt2xml.sf.net for importing delimited text files
049    // FIXME: test save on exit when manually closing frame
050    // FIXME: does java automatically convert special chars to XML entities, i.e. &-> &amp;
051            /**
052             * Bee Editor's JDesktopPane.
053             *
054             */
055            protected JDesktopPane desktop;
056            /**
057             * Describe variable <code>iOpenBook</code> here.
058             *
059             */
060            protected JMenuItem iOpenBook, iNewBook, iExit, iUnicode;
061            /**
062             * Keeps track of book editors that are active.
063             *
064             */
065            protected Hashtable<String,BookEditor> editors;
066            private UnicodeCharInputPanel.UnicodeCharJDialog unicode;
067            
068            /**
069             * Creates a new <code>Bee</code> instance.
070             *
071             */
072            public Bee(){
073    
074                    super("Bee Editor For PAVLOV");
075                    //Frame ref = JOptionPane.getRootFrame(); //new Frame(); // this might be why the window's not closing
076                    //lstartup = new StartupWindow("file:resources/aboutBee.jpg",ref);
077    
078                    //lstartup.setMainApp(this);
079    
080    
081    
082                    javax.swing.ToolTipManager.sharedInstance().setEnabled(true);
083    
084                    ImageIcon logo = ImageIconUtilities.getNamedImageIcon("file:resources/B.png",50);
085                    if(logo!=null)
086                    {
087                            java.awt.Image imm = logo.getImage();
088                            setIconImage(imm);
089                    }
090    
091    
092    
093                    desktop = new JDesktopPane();
094                    //desktop.setLayout(new BorderLayout());
095                    setContentPane(desktop);
096    
097                    editors = new Hashtable<String,BookEditor>();
098                    setSize( new Dimension(705,685));
099    
100                    JMenuBar menubar = new JMenuBar();
101    
102                    JMenu file = new JMenu("File");
103                    iOpenBook = new JMenuItem("Open Book");
104                    iOpenBook.addActionListener(this);
105                    file.add(iOpenBook);
106    
107                    iNewBook = new JMenuItem("New Book");
108                    iNewBook.addActionListener(this);
109    
110                    iExit = new JMenuItem("Exit");
111                    iExit.addActionListener(this);
112    
113                    file.add(iOpenBook);
114                    file.add(iNewBook);
115                    file.add(iExit);
116    
117                    menubar.add(file);
118                    
119                    JMenu tools = new JMenu("Tools");
120                    iUnicode = new JMenuItem("Unicode Characters...");
121                    iUnicode.addActionListener(this);
122                    tools.add(iUnicode);
123                    menubar.add(tools);
124    
125                    //     JToolBar toolbar = new JToolBar("Bee Tools");
126                    //     toolbar.add( new AbstractAction() {
127                    //          public void actionPerformed(ActionEvent e) {
128                    //              System.out.println("clicked the btn");
129                    //          }
130                    //      }
131                    //               );
132                    //     desktop.add(toolbar,BorderLayout.PAGE_START);
133                    //PavlovMetalTheme t = new PavlovMetalTheme();//f);
134                    //t.setTarget(this);
135    
136                    //menubar.add(t.getThemeMenu(g));
137    
138                    //      java.io.File g = new java.io.File("resources/themes");
139                    //      ThemeMenu theme = new ThemeMenu(this, g);
140                    //      menubar.add(theme);
141    
142    
143                    HelpSystem h = new HelpSystem(desktop);
144                    try {
145                            HelpBroker hb;
146                            HelpSet hs;
147                            JMenuItem menu_help;
148                            //FIXME: get helpset from Resources
149                            URL hsURL = new URL("file:resources/beehs/main.hs");
150                            hs = new HelpSet(null, hsURL);
151                            hb = hs.createHelpBroker();
152                            menu_help = new JMenuItem("Help Topics...");
153                            menu_help.addActionListener(new CSH.DisplayHelpFromSource( hb ));
154                            h.add(menu_help);
155                    } catch (Exception ee) {
156                            // Say what the exception really is
157                            System.out.println( "Error Creating HelpSet " + ee);
158                    }
159    
160                    boolean ret;// = h.addItem("file:resources/help/beeHelp.html","Help Topics");
161                    ret = h.addItem("file:resources/help/aboutBee.html","About Bee");
162                    h.addSeparator();
163                    ret = h.addItem("http://pavlov.sourceforge.net","Pavlov Home Page (online)");
164                    ret = h.addItem("http://pavlov.sourceforge.net/bdem/tour1.html","Bee Tutorial (online)");
165                    menubar.add(h);
166                    setJMenuBar(menubar);
167                    setVisible(true);
168    
169    
170                    
171    
172                    //Quit this app when the big window closes.
173                    addWindowListener(new WindowAdapter() {
174                                              public void windowClosing(WindowEvent e) {
175                                                      niceQuit(false);
176                                              }
177                                      }
178                                     );
179    
180                    WelcomeDialog wd = new WelcomeDialog(this);
181                    addInternalFrame(wd);  
182                    
183                    //FIXME: attach unicode to a button/menu item
184                    //unicode = new UnicodeCharInputPanel.UnicodeCharJFrame();
185                    //UnicodeCharInputPanel ucip = unicode.getUnicodeCharInputPanel();
186                    
187                    //unicode = new UnicodeCharInputPanel.UnicodeCharJFrame();
188                    UnicodeCharInputPanel ucip = new UnicodeCharInputPanel();
189                    unicode = ucip.getJDialog(this);
190                    ucip.setPostGlobally(true);
191                    //addInternalFrame(unicode);
192    
193            }
194    
195    
196            /**
197             * User wants to open a book, but hasn't provided the filename.
198             *
199             */
200            public void openBook()
201            {
202                    JFileChooser chooser = new JFileChooser();
203                    XMLFileFilter filter = new XMLFileFilter();
204                    chooser.setFileFilter(filter);
205                    chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES  );
206                    int returnVal = chooser.showOpenDialog(this);
207                    if(returnVal != JFileChooser.APPROVE_OPTION) return;
208                    File bookFile = chooser.getSelectedFile();
209                    openBook(bookFile);
210            }
211    
212            /**
213             * Opens the given file for editing.
214             *
215             * @param bookFile a <code>File</code> value
216             */
217            public void openBook(File bookFile)
218            {
219                    if(bookFile==null)
220                    {
221                            showError("Cannot open file",null);
222                            return;
223                    }
224                    String fname = bookFile.getName();
225                    if(!bookFile.exists())
226                    {
227                            showError("File " + fname + " doesn't exist",null);
228                            return;
229                    }
230                    if(!bookFile.isFile())
231                    {
232                            showError("File " + fname + " is not a book file",null);
233                            return;
234                    }
235                    if(!bookFile.canRead())
236                    {
237                            showError("File " + fname + " is read-protected",null);
238                            return;
239                    }
240    
241                    /*
242                    BookXMLHandler br = new BookXMLHandler();
243                    XMLReader xr = null;
244                    Book b = null;
245                    try {
246                            xr = XMLReaderFactory.createXMLReader();
247                    } catch (Exception e) {
248                            showError("Cannot Create XML Reader",e);
249                            return;
250                    }
251    
252                    xr.setContentHandler(br);
253    
254                    try {
255                            //HERE
256                            FileReader fr = new FileReader(bookFile);
257                            InputSource is = new InputSource(fr);
258                            is.setEncoding("UTF8");
259                            xr.parse(is);
260                    } catch (Exception e) {
261                            showError("Cannot Parse File " + fname,e);
262                    }
263    
264                    //System.out.println("file name = " + fname.toString());
265                    */
266                    Book b = Book.loadFrom(bookFile);
267    
268                    if(b==null) System.out.println("Book is null");
269                    //else System.out.println(b.getTitle());
270    
271                    addBookEditor(b,bookFile);
272            }
273    
274    
275            /**
276             * Creates a new book, saves it to user selected file, opens it for editing.
277             *
278             */
279            public void newBook()
280            {
281                    JFileChooser chooser = new JFileChooser();
282                    XMLFileFilter filter = new XMLFileFilter();
283                    chooser.setFileFilter(filter);
284                    chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES  );
285                    int returnVal = chooser.showSaveDialog(this);
286                    if(returnVal != JFileChooser.APPROVE_OPTION) return;
287                    File bookFile = chooser.getSelectedFile();
288                    newBook(bookFile,null);
289            }
290    
291            /**
292             * User wants to create a new book.  Makes a new book and saves it to the
293             * given file.
294             *
295             * @param bookFile a <code>File</code> value
296             * @param book a <code>Book</code> value
297             */
298            public void newBook(File bookFile, Book book)
299            {
300                    if(bookFile==null)
301                    {
302                            showError("Cannot open file",null);
303                            return;
304                    }
305                    String fname = bookFile.getName();
306                    if(bookFile.exists()){
307                            int ret =
308                                    JOptionPane.showInternalConfirmDialog(getContentPane(),
309                                                                          "Overwrite Existing File " +fname +"?",
310                                                                          "File Exists",
311                                                                          JOptionPane.YES_NO_OPTION);
312                            if(ret==JOptionPane.NO_OPTION) return;
313                    } else {
314                            try {
315                                    boolean tmp = bookFile.createNewFile();
316                            } catch (Exception e) {
317                                    showError("Cannot create file " + fname,e);
318                                    return;
319                            }
320                    }
321                    Book b = null;
322                    if(book!=null)
323                            b = book;
324                    else
325                            b = new Book();
326    
327                    try {
328                            b.saveAs(bookFile);
329                    } catch (Exception e) {
330                            showError("Cannot create file " + fname,e);
331                            return;
332                    }
333                    addBookEditor(b,bookFile);
334            }
335    
336    
337            /**
338             * Adds a BookEditor instance to this editing session.
339             *
340             * @param bk a <code>Book</code> value
341             * @param bf a <code>File</code> value
342             */
343            public void addBookEditor(Book bk,File bf)
344            {
345                    String key = bf.getAbsolutePath();
346                    if(editors.containsKey(key) )
347                    {
348                            BookEditor c = (BookEditor)editors.get(key);
349                            c.setVisible(true);
350                            try { c.setSelected(true); } catch (Exception e) {}
351                            return;
352                    }
353    
354                    BookEditor ed = new BookEditor(this,bk,bf);
355                    editors.put(key,ed);
356                    addInternalFrame(ed);
357            }
358    
359    
360    
361            /**
362             * Nice error/exception reporting to user.
363             *
364             * @param text a <code>String</code> value
365             * @param e an <code>Exception</code> value
366             */
367            public void showError(String text, Exception e)
368            {
369                    JOptionPane.showInternalMessageDialog(getContentPane(),
370                                                          text,
371                                                          "Error",
372                                                          JOptionPane.ERROR_MESSAGE);
373            }
374    
375            /**
376             * Returns true if an editor for the given book file is open.
377             *
378             * @param f a <code>File</code> value
379             * @return a <code>boolean</code> value
380             */
381            public boolean hasEditorKey(File f)
382            {
383                    String key = f.getAbsolutePath();
384                    return editors.containsKey(key);
385            }
386    
387            /**
388             * Removes the reference for an open editor for the given book file.
389             *
390             * @param f a <code>File</code> value
391             */
392            public void removeEditorKey(File f)
393            {
394                    String key = f.getAbsolutePath();
395                    editors.remove(key);
396            }
397    
398            /**
399             * User tries to rename book from oldFile to newFile.  Checks that no
400             * editor for newFile is currently open.  If one is open, returns quietly
401             * without renaming.  Otherwise, renames the book and updates the 
402             * hashtable reference.
403             *
404             * @param oldFile a <code>File</code> value
405             * @param newFile a <code>File</code> value
406             */
407            public void changeEditorKey(File oldFile, File newFile)
408            {
409                    String oldKey = oldFile.getAbsolutePath();
410                    String newKey = newFile.getAbsolutePath();
411    
412                    if(editors.containsKey(newKey) )
413                            return;
414    
415                    // this was ChapterEditor for some reason...
416                    BookEditor c = (BookEditor)editors.remove(oldKey);
417                    if(c==null) return;
418                    editors.put(newKey,c);
419            }
420    
421            /**
422             * Convenience method for adding a JInternalFrame to the editor nicely.
423             *
424             * @param jif a <code>JInternalFrame</code> value
425             */
426            public void addInternalFrame(JInternalFrame jif) {
427                    if(jif==null) return;
428                    jif.setVisible(true);
429                    jif.pack();
430                    desktop.add(jif);
431                    try {
432                            jif.setSelected(true);
433                    } catch (java.beans.PropertyVetoException e) {}
434                    jif.repaint(); // bug-workaround
435            }
436    
437            /**
438             * Handles menu events.
439             *
440             * @param e an <code>ActionEvent</code> value
441             */
442            public void actionPerformed(ActionEvent e)
443            {
444                    if(e.getSource().equals(iOpenBook))
445                            openBook();
446                    else if(e.getSource().equals(iExit))
447                            niceQuit(true);
448                    else if(e.getSource().equals(iNewBook))
449                            newBook();
450                    else if(e.getSource().equals(iUnicode))
451                            unicode.setVisible(!unicode.isVisible());
452            }
453    
454            /**
455             * Runs the BEE Editor for Pavlov.
456             *
457             * @param args[] a <code>String</code> value
458             */
459            public static void main(String args[])
460            {
461                    Bee bee = new Bee();
462                    bee.setVisible(true);
463            }
464    
465    
466            /**
467             * If confirm is true, asks user if he wants to quit.  Otherwise quits.
468             *
469             * @param confirm a <code>boolean</code> value
470             */
471            public void niceQuit(boolean confirm)
472            {
473                    if(confirm)
474                            if( !confirmQuit()) return;
475    
476                    //saveUsers();
477                    System.exit(0);
478            }
479    
480            /**
481             * Asks user if he really wants to exit the application.
482             *
483             * @return a <code>boolean</code> value
484             */
485            public boolean confirmQuit()
486            {
487                    int ret = JOptionPane.showInternalConfirmDialog(getContentPane(), "Really Quit Bee?");
488                    if (ret == JOptionPane.YES_OPTION) return true;
489                    return false;
490            }
491    
492    }
493