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