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/BookEditor.java,v 1.6 2004/05/24 02:02:17 tj_willis Exp $
019     */
020    package net.sourceforge.bee;
021    
022    import javax.swing.*;
023    import javax.swing.event.*;
024    import java.awt.*;
025    import java.awt.event.*;
026    import net.sourceforge.pavlov.library.*;
027    import net.sourceforge.pavlov.zipUtilities.XMLFileFilter;
028    import java.util.*;
029    import java.io.*;
030    import java.net.URL;
031    
032    /**
033     * User interface for editing a book file.
034     *
035     * @author <a href="mailto:"></a>
036     * @version 1.0
037     */
038    public class BookEditor 
039        extends JInternalFrame
040        implements ActionListener, ListSelectionListener
041    {
042        private JLabel lTitle, lAuthor, lDescription;
043        private JTextField fTitle, fAuthor;
044        private JTextArea aDescription;
045        private JList lChapters;
046        private JButton bEdit, bUpdate;
047        private JMenuItem save, saveAs, close, edit, iNew, iCut, iCopy,iInsert,iDelete;
048        private Object clipboard;
049        private Book book, savedBook;
050        private Bee bee;
051        private Hashtable<String,ChapterEditor> editors;
052        private File loadFile;
053        private Container content;
054        private AbstractAction editChapterAction,deleteChapterAction,insertChapterAction;
055        private AbstractAction copyChapterAction,newChapterAction,closeAction,saveAsAction;
056        private AbstractAction saveAction;
057    
058    
059        /**
060         * Creates a new <code>BookEditor</code> instance.
061         *
062         * @param parent a <code>Bee</code> value
063         * @param aBook a <code>Book</code> value
064         * @param file a <code>File</code> value
065         */
066        public BookEditor(Bee parent,Book aBook, File file)
067        {
068            super();
069            content = getContentPane();
070            content.setLayout(new BorderLayout());
071    
072            bee = parent;
073            book = aBook;
074            savedBook = book.deepCopy();
075            editors = new Hashtable<String,ChapterEditor>();
076            loadFile = file;
077    
078            super.setTitle("Book Editor"); //Edit: Book: " + book.getName());
079            setResizable(true);
080    
081            Box box = new Box(BoxLayout.Y_AXIS);
082          
083            Box b0 = new Box(BoxLayout.Y_AXIS);
084    
085            JPanel pan1 = new JPanel();
086            pan1.setLayout(new java.awt.GridLayout(2,1));
087    
088            Box b1 = new Box(BoxLayout.X_AXIS);
089            b1.setBorder( BorderFactory.createTitledBorder( "Title" ) );
090            fTitle = new JTextField();
091            fTitle.addActionListener(this);
092            b1.add(fTitle);
093    
094            Box b2 = new Box(BoxLayout.X_AXIS);
095            b2.setBorder( BorderFactory.createTitledBorder( "Author" ) );
096            fAuthor = new JTextField();
097            fAuthor.addActionListener(this);
098            b2.add(fAuthor);
099    
100            pan1.add(b1);
101            pan1.add(b2);
102    
103            b0.add(pan1);
104    
105            Box b3 = new Box(BoxLayout.X_AXIS);
106            b3.setBorder( BorderFactory.createTitledBorder( "Description" ) );
107            aDescription = new JTextArea();         
108            aDescription.setLineWrap(true);
109            JScrollPane sp1 = new JScrollPane(aDescription);
110            sp1.setMinimumSize(new Dimension(360,80));
111            bUpdate = new JButton("Update");
112            bUpdate.addActionListener(this);
113    
114            b3.add(sp1);
115            b3.add(bUpdate);
116            b0.add(b3);
117            box.add(b0);
118    
119            Box b4 = new Box(BoxLayout.X_AXIS);
120            b4.setBorder( BorderFactory.createTitledBorder( "Chapters" ) );
121            Vector<AbstractChapter> v = new Vector<AbstractChapter>(book.getValues());
122            lChapters = new JList(v);
123            lChapters.addListSelectionListener(this);
124            JScrollPane sp = new JScrollPane(lChapters);
125            sp.setMinimumSize( new Dimension(360,360));
126    
127            b4.add(sp);
128            box.add(b4);
129    
130    
131            // DEFINE ACTIONS
132            saveAction = ( new AbstractAction() {
133                    public void actionPerformed(ActionEvent e) {
134                        save(); 
135                    }
136                }
137                                          );
138            saveAction.putValue(Action.SHORT_DESCRIPTION,"Save This Book");
139            saveAsAction = ( new AbstractAction() {
140                    public void actionPerformed(ActionEvent e) {
141                        saveAs();
142                    }
143                }
144                                            );
145            saveAsAction.putValue(Action.SHORT_DESCRIPTION,"Save This Book To New File");
146    
147            closeAction = ( new AbstractAction() {
148                    public void actionPerformed(ActionEvent e) {
149                        close(); 
150                    }
151                }
152                                           );
153            closeAction.putValue(Action.SHORT_DESCRIPTION,"Close This Book");
154                                   
155            newChapterAction = ( new AbstractAction() {
156                    public void actionPerformed(ActionEvent e) {
157                        newChapter(); 
158                    }
159                }
160                                                );
161            newChapterAction.putValue(Action.SHORT_DESCRIPTION,"Create New Chapter");                                   
162            copyChapterAction = ( new AbstractAction() {
163                    public void actionPerformed(ActionEvent e) {
164                        copy(); 
165                    }
166                }
167                                                 );
168            copyChapterAction.putValue(Action.SHORT_DESCRIPTION,"Copy Selected Chapter");                                
169            insertChapterAction = ( new AbstractAction() {
170                    public void actionPerformed(ActionEvent e) {
171                        insert(); 
172                    }
173                }
174                                                   );
175            insertChapterAction.putValue(Action.SHORT_DESCRIPTION,"Insert A Chapter");
176            deleteChapterAction = ( new AbstractAction() {
177                    public void actionPerformed(ActionEvent e) {
178                        delete(); 
179                    }
180                }
181                                                   );
182            deleteChapterAction.putValue(Action.SHORT_DESCRIPTION,"Delete Selected Chapter");
183    
184            editChapterAction = ( new AbstractAction() {
185                    public void actionPerformed(ActionEvent e) {
186                        editChapter(); 
187                    }
188                }
189                                                   );
190             editChapterAction.putValue(Action.SHORT_DESCRIPTION,"Edit Selected Chapter");
191    
192            // DEFINE BUTTONS
193    
194    
195    
196            //Box b5 = new Box(BoxLayout.X_AXIS);
197            //bEdit = makeJButton(editChapterAction,"Edit", "resources/icons/tb_edit.jpg");
198            //b5.add(bEdit);
199            //b5.add(bClose);
200            //box.add(b5);
201            //bEdit.addActionListener(this);
202    
203    
204            content.add(box,BorderLayout.CENTER);
205          
206    
207            // DEFINE MENU ITEMS
208            JMenuBar menu = new JMenuBar();
209            JMenu mFile = new JMenu("File");        
210            save = makeJMenuItem(saveAction,"Save","resources/icons/tb_save.jpg");     
211            saveAs = makeJMenuItem(saveAsAction,"Save As...","resources/icons/tb_save_as.jpg");     
212            close = makeJMenuItem(closeAction,"Close","resources/icons/tb_close.jpg");      
213            //save.addActionListener(this);
214            //saveAs.addActionListener(this);
215            //close.addActionListener(this);
216            mFile.add(save);
217            mFile.add(saveAs);
218            mFile.add(close);
219            menu.add(mFile);
220    
221            JMenu mChapter = new JMenu("Chapter");
222            edit = makeJMenuItem(editChapterAction,"Edit","resources/icons/tb_edit.jpg");
223            iNew = makeJMenuItem(newChapterAction,"New","resources/icons/tb_new.jpg");
224            //iCut = new JMenuItem("Cut");
225            iCopy =makeJMenuItem(copyChapterAction,"Copy","resources/icons/tb_copy.jpg");
226            //iPaste = new JMenuItem("Paste");
227            iInsert = makeJMenuItem(insertChapterAction,"Insert","resources/icons/tb_insert.jpg");
228            iDelete = makeJMenuItem(deleteChapterAction,"Delete","resources/icons/tb_delete.jpg");
229    
230    
231            editChapterAction.setEnabled(false);
232            //iCut.setEnabled(false);
233            copyChapterAction.setEnabled(false);
234            deleteChapterAction.setEnabled(false);
235            insertChapterAction.setEnabled(false);
236            editChapterAction.setEnabled(false);
237    
238    
239            mChapter.add(iNew);
240            //mChapter.add(iCut);
241            mChapter.add(iCopy);
242            //mChapter.add(iPaste);
243            mChapter.add(iInsert);
244            mChapter.add(iDelete);
245    
246    
247            menu.add(mChapter);
248            setJMenuBar(menu);
249    
250            JToolBar toolbar = new JToolBar("BookEditor Tools");
251            toolbar.setRollover(true);
252    
253            JButton s1 = makeJButton(saveAction,"Save","resources/icons/tb_save.jpg");         
254            JButton s2 = makeJButton(saveAsAction,"Save As...","resources/icons/tb_save_as.jpg");      
255            JButton s3 = makeJButton(closeAction,"Close","resources/icons/tb_close.jpg");      
256            
257            JButton s4 = makeJButton(editChapterAction,"Edit","resources/icons/tb_edit.jpg");       
258            JButton s5 = makeJButton(newChapterAction,"New","resources/icons/tb_new.jpg");    
259            JButton s6 = makeJButton(copyChapterAction,"Copy","resources/icons/tb_copy.jpg");   
260            JButton s7 = makeJButton(insertChapterAction,"Insert","resources/icons/tb_insert.jpg");  
261            JButton s8 = makeJButton(deleteChapterAction,"Delete","resources/icons/tb_delete.jpg");         
262    
263            //saveAs = makeJMenuItem(saveAsAction,"Save As...","resources/icons/tb_save_as.jpg");   
264            //close = makeJMenuItem(closeAction,"Close","resources/icons/tb_close.jpg"); 
265    
266            toolbar.add(s1);
267            toolbar.add(s2);
268            toolbar.add(s3);
269            toolbar.addSeparator();
270            toolbar.add(s4);
271            toolbar.add(s5);
272            toolbar.add(s6);
273            toolbar.add(s7);
274            toolbar.add(s8);
275            content.add(toolbar,BorderLayout.PAGE_START);
276    
277            setTitle(book.getName());
278            setAuthor(book.getAuthor());
279            setDescription(book.getDescription());
280    
281            pack();
282        }
283    
284    
285        private void editChapter()
286        {
287            Chapter chapter = (Chapter) lChapters.getSelectedValue();
288            if(chapter==null) return;
289    
290            String cName = chapter.getName();
291            if(editors.containsKey(cName) )
292                {
293                    ChapterEditor c = (ChapterEditor)editors.get(cName);
294                    c.setVisible(true);
295                    try { c.setSelected(true); } catch (Exception e) {}
296                    return;
297                }
298            //Chapter c = book.getChapter(cName );
299    
300            ChapterEditor pan = new ChapterEditor(this,chapter);
301            editors.put(cName,pan);
302            bee.addInternalFrame(pan);
303        }
304    
305    
306        // conflicts with Frame.setTitle
307        /**
308         * Describe <code>setTitle</code> method here.
309         *
310         * @param t a <code>String</code> value
311         */
312        public void setTitle(String t)
313        {
314            fTitle.setText(t);
315        }
316        private void setAuthor(String t)
317        {
318            fAuthor.setText(t);
319        }
320        private void setDescription(String t)
321        {
322            aDescription.setText(t);
323        }
324    
325        private void newChapter()
326        {
327            Chapter c = Chapter.makeBlankChapter();
328            String t = "New Chapter";
329    
330            Chapter x = null;
331            long i=0;
332            for(i=1;true;i++)
333                {
334                    x = book.getChapter(t);
335                    if(x==null) break;
336                    t = "New Chapter " + i;
337                } 
338            c.setName(t);
339    
340            book.addChapter(c);
341            refreshChapterNames();
342            lChapters.setSelectedValue(c,true);
343        }
344    
345        private void copy()
346        {
347            Chapter chapter = (Chapter) lChapters.getSelectedValue();
348            if(chapter==null) return;
349            Chapter c = chapter.deepCopy();
350            c.setName("Copy of " + chapter.getName());
351            clipboard = c;
352            insertChapterAction.setEnabled(true);
353        }
354    
355        private void delete()
356        {
357            Chapter chapter = (Chapter) lChapters.getSelectedValue();
358            if(chapter==null) return;
359            String msg = "Really delete chapter " + chapter.getName() + "?";
360            int ret = 
361                JOptionPane.showInternalConfirmDialog(getDesktopPane(), 
362                                                      msg,
363                                                      "Delete Chapter",
364                                                      JOptionPane.YES_NO_OPTION);
365            if( ret == JOptionPane.NO_OPTION) 
366                return;
367    
368            ChapterEditor cp = (ChapterEditor)editors.get(chapter.getName());
369            if(cp!=null) cp.dispose();
370            book.deleteChapter(chapter.getName());
371            refreshChapterNames();
372            insertChapterAction.setEnabled(false);
373        }
374    
375    
376        private void insert()
377        {
378            if(clipboard ==null) return;
379            Chapter c = (Chapter)clipboard;
380            book.addChapter(c);
381            refreshChapterNames();
382            insertChapterAction.setEnabled(false);
383        }
384    
385        private void save()
386        {
387            try { 
388                book.saveAs(loadFile);
389            } catch (IOException e) {
390                bee.showError("Error saving book", e);
391            }
392            savedBook = book.deepCopy();
393        }
394    
395        private void saveAs()
396        {
397            JFileChooser chooser = new JFileChooser();
398            // Note: source for ExampleFileFilter can be found in FileChooserDemo,
399            // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
400            XMLFileFilter filter = new XMLFileFilter();
401            chooser.setFileFilter(filter);
402            chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES  );
403            int returnVal = chooser.showSaveDialog(this);
404            if(returnVal != JFileChooser.APPROVE_OPTION) return;
405            File bookFile = chooser.getSelectedFile();
406            if(bookFile==null) 
407                {
408                    bee.showError("Cannot open file",null);
409                    return;
410                }
411            String fname = bookFile.getName();
412            if(!bookFile.exists())
413                {
414                    try {
415                        boolean tmp = bookFile.createNewFile();
416                    } catch (Exception e) {
417                        bee.showError("Cannot create file " + fname,e);
418                        return;
419                    }
420                }
421    
422            if(bee.hasEditorKey(bookFile))
423                {
424                    bee.showError("That file is already open for editing.",null);
425                    return;
426                }
427            try{
428                book.saveAs(bookFile);
429            } catch (IOException ex) {
430                bee.showError("Cannot save book file",ex);
431                return;
432            }
433            savedBook = book.deepCopy();
434            if(loadFile!=bookFile) bee.changeEditorKey(loadFile,bookFile);
435            loadFile = bookFile;
436        }
437    
438        private void close()
439        {
440            if(book != savedBook)
441                {
442                    String msg = "Do you want to save book " + book.getName() + "?";
443                    int ret = JOptionPane.showInternalConfirmDialog(getDesktopPane(), msg);
444                    if( ret == JOptionPane.CANCEL_OPTION) return;
445                    if( ret == JOptionPane.YES_OPTION)
446                        {
447                            save();
448                            showInformationDialog("Saved book " + book.getName()); 
449                        }
450                }
451            Collection<ChapterEditor> col = editors.values();
452            for (ChapterEditor targ: col) {
453                targ.dispose();
454            }
455            bee.removeEditorKey(loadFile);
456            this.dispose();
457        }
458    
459        /**
460         * Describe <code>actionPerformed</code> method here.
461         *
462         * @param e an <code>ActionEvent</code> value
463         */
464        public void actionPerformed(ActionEvent e)
465        {
466            //if(e.getSource().equals(bEdit))
467            //    editChapter();
468            if(e.getSource().equals(close))
469                close();
470            else if(e.getSource().equals(save))
471                save();
472            else if(e.getSource().equals(saveAs))
473                saveAs();
474            else if(e.getSource().equals(iNew))
475                newChapter();
476            //else if(e.getSource().equals(iCut))
477            //  cut();
478            else if(e.getSource().equals(iCopy))
479                copy();
480            //else if(e.getSource().equals(iPaste))
481            //  paste();
482            else if(e.getSource().equals(iInsert))
483                insert();
484            else if(e.getSource().equals(iDelete))
485                delete();
486            else if(e.getSource().equals(fTitle))
487                editTitle();
488            else if(e.getSource().equals(fAuthor))
489                editAuthor();
490            else if(e.getSource().equals(aDescription))
491                editDescription();
492            else if(e.getSource().equals(bUpdate))
493                editDescription();
494            //else 
495            //super.actionPerformed(e);
496        }
497        private void showInformationDialog(String msg)
498        {
499            showInformationDialog(msg,msg);
500        }
501    
502        private void showInformationDialog(String title, String msg)
503        {
504            JOptionPane.showInternalMessageDialog(getDesktopPane(), 
505                                                  msg, 
506                                                  title, 
507                                                  JOptionPane.INFORMATION_MESSAGE);
508    
509        }
510    
511        private JMenuItem makeJMenuItem(Action ac, String txt, String url)
512        {
513            JMenuItem x = new JMenuItem(ac);
514            x.setText(txt);
515            try {
516                URL u = new URL("file:"+url);//getClass().getResource(url);
517                ImageIcon tmp = new ImageIcon(u);
518                x.setIcon(tmp  );
519            } catch (Exception e) {}
520            return x;
521        }
522    
523    
524        private JButton makeJButton(Action ac, String txt, String url)
525        {
526            JButton x = new JButton(ac);
527            x.setText(txt);
528            try {
529                URL u = new URL("file:"+url);//getClass().getResource(url);
530                ImageIcon tmp = new ImageIcon(u);
531                x.setIcon(tmp  );
532                Font g = x.getFont();
533                int style = g.getStyle();
534                String n = g.getFontName();
535                Font f = new Font(n,style,8);
536                x.setFont(f);
537                x.setVerticalTextPosition(SwingConstants.BOTTOM);
538                x.setHorizontalTextPosition(SwingConstants.CENTER);
539            } catch (Exception e) {}
540            return x;
541    
542        }
543    
544        private void editTitle()
545        {
546            // TOFIX: check that it's unique
547            // reinit chapterselector
548            String t = fTitle.getText();
549            if(!validCharacters(t))
550                {
551                    showInformationDialog("Invalid Characters in Title");
552                    return;
553                }
554            // TOFIX: have to do something like this, i think:
555            //bee.changeEditorKey(bName,t,cName);
556            //    cName = t; 
557            setTitle(t); 
558        }
559    
560    
561        // chapter editor changed chapter's name
562        void changeEditorKey(String newn, String old)
563        {
564            ChapterEditor c = (ChapterEditor)editors.remove(old);
565            if(c==null) return;
566            editors.put(newn,c);
567            refreshChapterNames();
568        }
569    
570        void removeEditorKey(String theKey)
571        {
572            ChapterEditor c = (ChapterEditor)editors.remove(theKey);
573            c=null;
574        }
575    
576    
577        private void refreshChapterNames()
578        {
579            Vector<AbstractChapter> v = new Vector<AbstractChapter>(book.getValues());
580            lChapters.setListData(v);
581            lChapters.repaint();
582        }
583    
584        private void editAuthor()
585        {
586            String t = fAuthor.getText();
587            if(!validCharacters(t))
588                {
589                    showInformationDialog("Invalid Characters in Author");
590                    return;
591                }
592            setAuthor(t); 
593        }
594    
595        private void editDescription()
596        {
597            String t = aDescription.getText();
598            if(!validCharacters(t))
599                {
600                    showInformationDialog("Invalid Characters in Description");
601                    return;
602                }
603            setDescription(t); 
604        }
605    
606        private boolean validCharacters(String s)
607        {
608            if(s.indexOf('<')>0) return false;
609            if(s.indexOf('&')>0) return false;
610            if(s.indexOf('\"')>0) return false;
611            return true;
612        }
613    
614        /**
615         * Describe <code>valueChanged</code> method here.
616         *
617         * @param e a <code>ListSelectionEvent</code> value
618         */
619        public void valueChanged(ListSelectionEvent e) 
620        {
621            Chapter chapter = (Chapter) lChapters.getSelectedValue();
622            if(chapter==null)
623                {
624                    //edit.setEnabled(false);
625                    //iCut.setEnabled(false);
626                    copyChapterAction.setEnabled(false);
627                    deleteChapterAction.setEnabled(false);
628                    editChapterAction.setEnabled(false);
629                } else {
630                //edit.setEnabled(true);
631                //iCut.setEnabled(true);
632                copyChapterAction.setEnabled(true);
633                deleteChapterAction.setEnabled(true);
634                editChapterAction.setEnabled(true);
635            }
636    
637            // handle menu enable/disables HERE
638    
639        }
640    
641    }
642    
643    
644    
645    
646    
647    
648