001    /* JAPI - (Yet another (hopefully) useful) Java API
002     *
003     * Copyright (C) 2004-2006 Christian Hujer
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License as
007     * published by the Free Software Foundation; either version 2 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful, but
011     * WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018     * 02111-1307, USA.
019     */
020    
021    package net.sf.japi.swing.bookmarks;
022    
023    import java.awt.BorderLayout;
024    import java.awt.dnd.DropTarget;
025    import java.awt.event.ActionEvent;
026    import java.io.File;
027    import java.io.IOException;
028    import java.util.ArrayList;
029    import java.util.Collections;
030    import java.util.Enumeration;
031    import java.util.Iterator;
032    import java.util.List;
033    import javax.swing.AbstractAction;
034    import javax.swing.Action;
035    import javax.swing.JComponent;
036    import javax.swing.JFrame;
037    import javax.swing.JMenu;
038    import javax.swing.JMenuBar;
039    import javax.swing.JMenuItem;
040    import static javax.swing.JOptionPane.ERROR_MESSAGE;
041    import static javax.swing.JOptionPane.QUESTION_MESSAGE;
042    import static javax.swing.JOptionPane.showInputDialog;
043    import static javax.swing.JOptionPane.showMessageDialog;
044    import javax.swing.JScrollPane;
045    import javax.swing.JToolBar;
046    import javax.swing.JTree;
047    import javax.swing.tree.MutableTreeNode;
048    import javax.swing.tree.TreeNode;
049    import javax.xml.parsers.DocumentBuilder;
050    import javax.xml.parsers.DocumentBuilderFactory;
051    import javax.xml.parsers.ParserConfigurationException;
052    import org.w3c.dom.DOMImplementation;
053    import org.w3c.dom.Document;
054    import org.w3c.dom.Element;
055    import org.w3c.dom.Node;
056    import org.w3c.dom.NodeList;
057    import org.w3c.dom.ls.DOMImplementationLS;
058    import org.xml.sax.SAXException;
059    import net.sf.japi.swing.ActionFactory;
060    import net.sf.japi.swing.IconManager;
061    import net.sf.japi.util.EmptyEnumeration;
062    
063    /** Class for managing and displaying Bookmarks.
064     * Usage of this class works the following way:
065     * <ul>
066     *  <li>implement the interface {@link Bookmarkable} and its methods</li>
067     *  <li>instanciate this class once for each gruop / kind of bookmarks you want to manage. Normally, one instance is enough per application</li>
068     *  <li>invoke {@link #createBookmarkMenu} to create your bookmarks menu</li>
069     * </ul>
070     * @author $Author: christianhujer $
071     * @todo documentation
072     * @todo fix bookmark drag and drop indices
073     * @todo think about serialization of Actions
074     */
075    public class BookmarkManager {
076    
077        /** The Bookmarks. */
078        private BookmarkFolder bookmarks = new BookmarkFolder();
079    
080        /** Action Factory. */
081        private static final ActionFactory ACTION_FACTORY = ActionFactory.getFactory("net.sf.japi.swing.bookmarks");
082    
083        /** The ProgramFrame this BookmarkManager manages bookmarks for. */
084        private Bookmarkable bookmarkable;
085    
086        /** Create a new BookmarkManager. */
087        public BookmarkManager(final Bookmarkable bookmarkable) {
088            this.bookmarkable = bookmarkable;
089            try {
090                load();
091                save();
092            } catch (final Exception e) {
093                e.printStackTrace();
094                // TODO: improve dialog
095                showMessageDialog(bookmarkable.getBookmarkBlocker(), ACTION_FACTORY.getString("bookmarksCreated.message"));
096            }
097        }
098    
099        /** Create a Bookmark ToolBar.
100         * @return toolBar for Bookmarks
101         */
102        public JToolBar createBookmarkToolBar() {
103            // Variant 1: JToolBar with JMenuBar with one entry "Lesezeichen"
104            final JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name"));
105            final JMenuBar mb = new JMenuBar();
106            mb.add(bookmarks.createMenu());
107            toolBar.add(mb);
108            return toolBar;
109    
110            //// Variant 2: JToolBar with JMenus and JMenuItems (broken)
111            //JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name"));
112            //for (Bookmark bookmark : bookmarks) {
113            //    toolBar.add(bookmark.createMenu());
114            //}
115            //return toolBar;
116    
117            //// Variant 3: JToolBar with JMenuBar with JMenus and JMenuItems (JMenuItems don't hover)
118            //JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name"));
119            //JMenuBar mb = new JMenuBar();
120            //for (Bookmark bookmark : bookmarks) {
121            //    mb.add(bookmark.createMenu());
122            //}
123            //toolBar.add(mb);
124            //return toolBar;
125    
126            //// Variant 4: JToolBar with a button activating a PopupMenu.
127            //// Doesn't work either, the popup menu is not correctly usable.
128            //JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name"));
129            //final JPopupMenu menu = new JPopupMenu(ACTION_FACTORY.getString("bookmark.text"));
130            //menu.add(bookmarks.createMenu());
131            //toolBar.add(
132            //    new javax.swing.JButton(
133            //        new AbstractAction() {
134            //            public void actionPerformed(final ActionEvent e) {
135            //                menu.setVisible(true);
136            //            }
137            //        }
138            //    )
139            //);
140            //return toolBar;
141        }
142    
143        /** Create a Bookmark Menu.
144         * @return JMenu for Bookmarks
145         */
146        public JMenu createBookmarkMenu() {
147            final JMenu menu = bookmarks.createMenu();
148            menu.add(new JMenuItem(manageBookmarks));
149            return menu;
150        }
151    
152        /** Create a Bookmark ControlPanel.
153         * @return ControlPanel for Bookmarks
154         */
155        public JComponent createBookmarkControlPanel() {
156            return new ControlPanel();
157        }
158    
159        /** Recursive attach method.
160         * @param menu JMenu to attach bookmarks to
161         * @param bookmarks Bookmarks to attach
162         */
163        private static void attach(final JMenu menu, final BookmarkFolder bookmarks) {
164            for (final Bookmark bookmark : bookmarks) {
165                if (bookmark instanceof BookmarkFolder) {
166                    final JMenu newMenu = new JMenu(bookmark);
167                    attach(newMenu, (BookmarkFolder) bookmark);
168                } else {
169                    assert bookmark instanceof BookmarkItem;
170                    menu.add(new JMenuItem(bookmark));
171                }
172            }
173        }
174    
175        /** Action for managing the bookmarks.
176         * @serial include
177         */
178        private Action manageBookmarks = ACTION_FACTORY.createAction(true, "manageBookmarks", this);
179    
180        /** Action for managing the bookmarks. */
181        public void manageBookmarks() {
182            final JFrame f = new JFrame(ACTION_FACTORY.getString("manageBookmarks_shortdescription"));
183            f.getContentPane().add(createBookmarkControlPanel());
184            //f.getContentPane().add(new JScrollPane(new JTree(bookmarks)));
185            f.pack();
186            f.setVisible(true);
187        }
188    
189        /** Set the AddBookmark enabled state.
190         * @param enabled enabled state for AddBookmark action
191         */
192        public void setAddBookmarkEnabled(final boolean enabled) {
193            bookmarks.setAddBookmarkEnabled(enabled);
194        }
195    
196        /** Load bookmarks from default file.
197         * @throws IOException in case of I/O problems
198         * @throws ParserConfigurationException in case of XML configuration problems
199         * @throws SAXException in case of XML document problems
200         */
201        public void load() throws IOException, ParserConfigurationException, SAXException {
202            load(new File(System.getProperty("user.home"), ".jeduca.bookmarks.xml").toURL().toString());
203        }
204    
205        /** Save bookmarks to default file.
206         * @throws IOException in case of I/O problems
207         * @throws ParserConfigurationException in case of XML configuration problems
208         */
209        public void save() throws IOException, ParserConfigurationException {
210            save(new File(System.getProperty("user.home"), ".jeduca.bookmarks.xml").toURL().toString());
211        }
212    
213        /** Save bookmarks to a file.
214         * @param uri URI of file to save
215         * @throws IOException in case of I/O problems
216         * @throws ParserConfigurationException in case of XML configuration problems
217         */
218        public void save(final String uri) throws IOException, ParserConfigurationException {
219            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
220            final DocumentBuilder db = dbf.newDocumentBuilder();
221            final Document doc = db.newDocument();
222            bookmarks.store(doc);
223            final DOMImplementation impl = doc.getImplementation();
224            if (impl instanceof DOMImplementationLS) {
225                final DOMImplementationLS ls = (DOMImplementationLS) impl;
226                ls.createLSSerializer().writeToURI(doc, uri);
227            }
228        }
229    
230        /** Load bookmarks from a file.
231         * @param uri URI of file to load
232         * @throws IOException in case of I/O problems
233         * @throws ParserConfigurationException in case of XML configuration problems
234         * @throws SAXException in case of XML document problems
235         */
236        public void load(final String uri) throws IOException, ParserConfigurationException, SAXException {
237            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
238            final DocumentBuilder db = dbf.newDocumentBuilder();
239            final Document doc = db.parse(uri);
240            bookmarks = new BookmarkFolder(doc);
241        }
242    
243    
244        /** Class for a ControlPanel to manage the bookmarks.
245         * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
246         * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $
247         */
248        private class ControlPanel extends JComponent {
249    
250            /** Serial Version. */
251            private static final long serialVersionUID = 1L;
252    
253            /** Create a new ControlPanel. */
254            ControlPanel() {
255                setLayout(new BorderLayout());
256                final JTree tree = new JTree(bookmarks, true);
257                tree.setRootVisible(false);
258                tree.setCellRenderer(new BookmarkTreeCellRenderer());
259                //tree.setEditable(true);
260                tree.setDragEnabled(true);
261                final DropTarget dt = new DropTarget(tree, new BookmarkDropTargetAdapter());
262                tree.setTransferHandler(new BookmarkTransferHandler());
263                add(new JScrollPane(tree));
264            }
265    
266        } // class ControlPanel
267    
268    
269        /** Base class for bookmarks.
270         * There are two kinds of bookmarks:
271         * <ul>
272         *  <li>{@link BookmarkItem}s for normal Bookmarks with title and url</li>
273         *  <li>{@link BookmarkFolder}s for Folders within Bookmarks with a title and (possibly) contents</li>
274         * </ul>
275         * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
276         * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $
277         */
278        public static abstract class Bookmark extends AbstractAction implements MutableTreeNode {
279    
280            /** Title for Bookmark.
281             * @serial include
282             */
283            private String title;
284    
285            /** The folder (parent) of this bookmark.
286             * @serial include
287             */
288            protected BookmarkFolder folder;
289    
290            ///** Create a Bookmark without title.
291            // * Should only be used by {@link BookmarkFolder#BookmarkFolder()}.
292            // */
293            //protected Bookmark() {
294            //}
295    
296            /** Create a Bookmark.
297             * You must not forget to invoke {@link #setFolder(BookmarkFolder)} in order to make {@link #getParent()} for JTrees working.
298             * @param title title for Bookmark
299             */
300            protected Bookmark(final String title) {
301                setTitle(title);
302                putValue(SMALL_ICON, IconManager.getDefaultIconManager().getIcon(ACTION_FACTORY.getString("bookmark.icon")));
303            }
304    
305            /** {@inheritDoc}
306             * @return title of this Bookmark
307             */
308            public String toString() {
309                return title;
310            }
311    
312            /** Get the folder (parent) of this bookmark.
313             * @return folder (parent) of this bookmark
314             */
315            public BookmarkFolder getFolder() {
316                return folder;
317            }
318    
319            /** Set the folder (parent) of this bookmark.
320             * @param folder parent folder of this bookmark
321             */
322            public void setFolder(final BookmarkFolder folder) {
323                if (this.folder != null) {
324                    this.folder.remove(this);
325                }
326                this.folder = folder;
327            }
328    
329            /** Set this Bookmark's title.
330             * @param title new title for this Bookmark
331             */
332            public void setTitle(final String title) {
333                this.title = title;
334                putValue(NAME, title);
335            }
336    
337            /** Create a MenuItem for this Bookmark
338             * @return MenuItem for this Bookmark
339             */
340            public abstract JMenuItem createMenu();
341    
342            /** Get this Bookmark's title.
343             * @return Bookmark title
344             */
345            public String getTitle() {
346                return title;
347            }
348    
349            /** Store bookmarks in an XML Document.
350             * @param n Node (Element or Document) to attach to
351             */
352            public abstract void store(final Node n);
353    
354            /** {@inheritDoc} */
355            public Enumeration<Bookmark> children() {
356                return new EmptyEnumeration<Bookmark>();
357            }
358    
359            /** {@inheritDoc} */
360            public boolean getAllowsChildren() {
361                return false;
362            }
363    
364            /** {@inheritDoc} */
365            public Bookmark getChildAt(final int childIndex) {
366                throw new IndexOutOfBoundsException(Integer.toString(childIndex));
367            }
368    
369            /** {@inheritDoc} */
370            public int getChildCount() {
371                return 0;
372            }
373    
374            /** {@inheritDoc} */
375            public int getIndex(final TreeNode node) {
376                return 0;
377            }
378    
379            /** {@inheritDoc} */
380            public BookmarkFolder getParent() {
381                return getFolder();
382            }
383    
384            /** {@inheritDoc} */
385            public boolean isLeaf() {
386                return true;
387            }
388    
389            /** {@inheritDoc} */
390            public void insert(final MutableTreeNode child, final int index) {
391                throw new IllegalStateException("A bookmark cannot have children");
392            }
393    
394            /** {@inheritDoc} */
395            public void remove(final int index) {
396                throw new IllegalStateException("A bookmark has no children");
397            }
398    
399            /** {@inheritDoc} */
400            public void remove(final MutableTreeNode child) {
401                throw new IllegalStateException("A bookmark has no children");
402            }
403    
404            /** {@inheritDoc} */
405            public void removeFromParent() {
406                folder.remove(this);
407            }
408    
409            /** {@inheritDoc} */
410            public void setParent(final MutableTreeNode newParent) {
411                if (newParent instanceof BookmarkFolder) {
412                    setFolder((BookmarkFolder)newParent);
413                } else {
414                    throw new IllegalArgumentException("Parent must be a BookmarkFolder, but was : " + newParent.getClass().getName());
415                }
416            }
417    
418            /** {@inheritDoc} */
419            public void setUserObject(final Object object) {
420                System.err.println("setUserObject was called but does not know what to do. Supplied object: " + object);
421            }
422    
423        } // class Bookmark
424    
425    
426        /** Class for Bookmark Separator.
427         * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
428         * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $
429         */
430        public static class BookmarkSeparator extends Bookmark {
431    
432            /** Serial Version. */
433            private static final long serialVersionUID = 1L;
434    
435            /** Create a Bookmark Separator. */
436            BookmarkSeparator() {
437                super("--------------------------------");
438            }
439    
440            /** {@inheritDoc}
441             * @return always <code>null</code>
442             */
443            @Override public JMenuItem createMenu() {
444                return null;
445            }
446    
447            /** {@inheritDoc} */
448            @Override public void store(final Node n) {
449                final Element e = n.getOwnerDocument().createElement("separator");
450                n.appendChild(e);
451            }
452    
453            /** {@inheritDoc} */
454            public void actionPerformed(final ActionEvent e) {
455                /* Do nothing */
456            }
457    
458        } // class BookmarkSeparator
459    
460    
461        /** Class for Bookmark Items.
462         * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
463         * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $
464         */
465        public class BookmarkItem extends Bookmark {
466    
467            /** Serial Version. */
468            private static final long serialVersionUID = 1L;
469    
470            /** URL of this Bookmark.
471             * @serial include
472             */
473            private String url;
474    
475            /** Create a BookmarkItem.
476             * @param title title for this BookmarkItem
477             * @param url URL for this BookmarkItem
478             */
479            public BookmarkItem(final String title, final String url) {
480                super(title);
481                setURL(url);
482            }
483    
484            /** Create a BookmarkItem from XML.
485             * @param el Element to create from
486             */
487            BookmarkItem(final Element el) {
488                this(el.getAttribute("title"), el.getAttribute("href"));
489            }
490    
491            /** Set this BookmarkItem's url.
492             * @param url new url for this BookmarkItem
493             */
494            public void setURL(final String url) {
495                this.url = url;
496                putValue(SHORT_DESCRIPTION, url);
497            }
498    
499            /** Get this BookmarkItem's url.
500             * @return BookmarkItem url
501             */
502            public String getURL() {
503                return url;
504            }
505    
506            /** {@inheritDoc} */
507            @Override public JMenuItem createMenu() {
508                return new JMenuItem(this);
509            }
510    
511            /** {@inheritDoc} */
512            public void actionPerformed(final ActionEvent e) {
513                bookmarkable.load(url);
514            }
515    
516            /** {@inheritDoc} */
517            public void store(final Node n) {
518                final Element e = n.getOwnerDocument().createElement("bookmark");
519                e.setAttribute("title", getTitle());
520                e.setAttribute("href", getURL());
521                n.appendChild(e);
522            }
523    
524        } // class BookmarkItem
525    
526    
527        /** Class for Bookmark folders.
528         * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
529         * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $
530         */
531        public class BookmarkFolder extends Bookmark implements Iterable<Bookmark> {
532    
533            /** Serial Version. */
534            private static final long serialVersionUID = 1L;
535    
536            /** The bookmarks in this folder.
537             * @serial include
538             */
539            private List<Bookmark> bookmarks = new ArrayList<Bookmark>();
540    
541            /** The Menus created for this folder.
542             * @serial include
543             */
544            private List<JMenu> menus = new ArrayList<JMenu>();
545    
546            /** Create a BookmarkFolder without a title.
547             * This should only be used for the basic BookmarkFolder containing all other BookmarkItems and BookmarkFolders.
548             */
549            public BookmarkFolder() {
550                super(ACTION_FACTORY.getString("bookmark_text"));
551            }
552    
553            /** Create a BookmarkFolder.
554             * @param title title for BookmarkFolder
555             */
556            public BookmarkFolder(final String title) {
557                super(title);
558                addBookmark.putValue(ACCELERATOR_KEY, null);
559                newBookmarkFolder.putValue(ACCELERATOR_KEY, null);
560            }
561    
562            /** Create a BookmarkFolder from XML.
563             * @param doc Document to create from
564             */
565            BookmarkFolder(final Document doc) {
566                this();
567                readNodes(doc.getDocumentElement());
568            }
569    
570            /** Create a BookmarkFolder from XML.
571             * @param el Element to create from
572             */
573            BookmarkFolder(final Element el) {
574                this(el.getAttribute("title"));
575                readNodes(el);
576            }
577    
578            /** Read nodes from XML.
579             * @param el XML Element to read nodes from
580             */
581            private void readNodes(final Element el) {
582                final NodeList children = el.getChildNodes();
583                for (int i = 0; i < children.getLength(); i++) {
584                    final Node n = children.item(i);
585                    if (n instanceof Element) {
586                        final Element e = (Element) n;
587                        if ("bookmark".equals(e.getNodeName())) {
588                            add(new BookmarkItem(e));
589                        } else if ("bookmarks".equals(e.getNodeName())) {
590                            add(new BookmarkFolder(e));
591                        } else if ("separator".equals(e.getNodeName())) {
592                            add(new BookmarkSeparator());
593                        }
594                    }
595                }
596            }
597    
598            /** {@inheritDoc} */
599            public Iterator<Bookmark> iterator() {
600                return bookmarks.iterator();
601            }
602    
603            /** Add a Bookmark to this BookmarkFolder.
604             * @param bookmark Bookmark to add
605             */
606            public void add(final Bookmark bookmark) {
607                final int pos = bookmarks.size();
608                bookmarks.add(bookmark);
609                bookmark.setFolder(this);
610                for (JMenu menu : menus) {
611                    menu.insert(bookmark.createMenu(), pos);
612                }
613                try {
614                    save();
615                } catch (final Exception e) {
616                    showMessageDialog(bookmarkable.getBookmarkBlocker(), e, "Bookmarks-Fehler", ERROR_MESSAGE);
617                    System.err.println(e);
618                    e.printStackTrace();
619                }
620            }
621    
622            /** Insert a Bookmark into this BookmarkFolder.
623             * @param bookmark Bookmark to add
624             * @param index desired index
625             */
626            public void insert(final Bookmark bookmark, final int index) {
627                if (index < 0 || index > bookmarks.size()) {
628                    throw new IllegalArgumentException("Invalid index: " + index + " (size: " + bookmarks.size() + ")");
629                }
630                bookmark.setFolder(this);
631                bookmarks.add(index, bookmark);
632                for (JMenu menu : menus) {
633                    menu.insert(bookmark.createMenu(), index);
634                }
635                try {
636                    save();
637                } catch (final Exception e) {
638                    showMessageDialog(bookmarkable.getBookmarkBlocker(), e, "Bookmarks-Fehler", ERROR_MESSAGE);
639                    System.err.println(e);
640                    e.printStackTrace();
641                }
642            }
643    
644            /** Remove a Bookmark from this BookmarkFolder.
645             * @param bookmark Bookmark to remove
646             */
647            public void remove(final Bookmark bookmark) {
648                if (bookmarks.contains(bookmark)) {
649                    remove(bookmarks.indexOf(bookmark));
650                } else {
651                    for (final Bookmark folder : bookmarks) {
652                        if (folder instanceof BookmarkFolder) {
653                            ((BookmarkFolder)folder).remove(bookmark);
654                        }
655                    }
656                }
657            }
658    
659            /** Remove a Bookmark from this BookmarkFolder.
660             * @param index Index of Bookmark to remove
661             */
662            @Override public void remove(final int index) {
663                final Bookmark bookmark = bookmarks.remove(index);
664                bookmark.folder = null;
665                for (JMenu menu : menus) {
666                    menu.remove(index);
667                }
668            }
669    
670            /** {@inheritDoc} */
671            @Override public JMenu createMenu() {
672                final JMenu menu = new JMenu(getTitle());
673                menu.setName(getTitle());
674                for (Bookmark bookmark : bookmarks) {
675                    if (bookmark instanceof BookmarkSeparator) {
676                        menu.addSeparator();
677                    } else {
678                        menu.add(bookmark.createMenu());
679                    }
680                }
681                menu.addSeparator();
682                menu.add(new JMenuItem(addBookmark));
683                menu.add(new JMenuItem(newBookmarkFolder));
684                menus.add(menu);
685                return menu;
686            }
687    
688            /** {@inheritDoc} */
689            public void actionPerformed(final ActionEvent e) {
690                /* Do nothing */
691            }
692    
693            /** Action for adding a bookmark.
694             * @serial include
695             */
696            private Action addBookmark = ACTION_FACTORY.createAction(true, "addBookmark", this);
697    
698            /** Action for creating a new bookmark folder.
699             * @serial include
700             */
701            private Action newBookmarkFolder = ACTION_FACTORY.createAction(true, "newBookmarkFolder", this);
702    
703            /** Add a bookmark for the currently selected Question from the currently selected QuestionCollection . */
704            public void addBookmark() {
705                if (bookmarkable.isBookmarkPossible()) {
706                    add(new BookmarkItem(bookmarkable.getBookmarkTitle(), bookmarkable.getBookmarkURL()));
707                }
708            }
709    
710            /** Create a new BookmarkFolder. */
711            public void newBookmarkFolder() {
712                final String folderName = showInputDialog(bookmarkable.getBookmarkBlocker(), "Name für Lesezeichen", "Neuer Lesezeichen-Ordner", QUESTION_MESSAGE);
713                if (folderName == null) {
714                    return;
715                }
716                add(new BookmarkFolder(folderName));
717            }
718    
719            /** Set the AddBookmark enabled state.
720             * @param enabled enabled state for AddBookmark action
721             */
722            public void setAddBookmarkEnabled(final boolean enabled) {
723                addBookmark.setEnabled(enabled);
724                for (Bookmark folder : bookmarks) {
725                    if (folder instanceof BookmarkFolder) {
726                        ((BookmarkFolder)folder).setAddBookmarkEnabled(enabled);
727                    }
728                }
729            }
730    
731            /** {@inheritDoc} */
732            @Override public void store(final Node n) {
733                final Element e = (n instanceof Document ? (Document) n : n.getOwnerDocument()).createElement("bookmarks");
734                e.setAttribute("title", getTitle());
735                n.appendChild(e);
736                for (Bookmark bookmark : bookmarks) {
737                    bookmark.store(e);
738                }
739            }
740    
741            /** {@inheritDoc} */
742            public Enumeration<Bookmark> children() {
743                return Collections.enumeration(bookmarks);
744            }
745    
746            /** {@inheritDoc} */
747            @Override public boolean getAllowsChildren() {
748                return true;
749            }
750    
751            /** {@inheritDoc} */
752            @Override public Bookmark getChildAt(final int childIndex) {
753                return bookmarks.get(childIndex);
754            }
755    
756            /** {@inheritDoc} */
757            @Override public int getChildCount() {
758                return bookmarks.size();
759            }
760    
761            /** {@inheritDoc} */
762            @Override public int getIndex(final TreeNode node) {
763                return bookmarks.indexOf(node);
764            }
765    
766            /** {@inheritDoc} */
767            @Override public boolean isLeaf() {
768                return false;
769            }
770    
771            /** {@inheritDoc} */
772            @Override public void insert(final MutableTreeNode child, final int index) {
773                if (child instanceof Bookmark) {
774                    insert((Bookmark)child, index);
775                } else {
776                    throw new IllegalArgumentException("Children of BookmarkFolders must be instance of Bookmark but was " + child.getClass().getName());
777                }
778            }
779    
780            ///** {@inheritDoc} */
781            //public void remove(final int index) {
782            //    remove(index);
783            //}
784    
785            /** {@inheritDoc} */
786            @Override public void remove(final MutableTreeNode child) {
787                if (child instanceof Bookmark) {
788                    remove((Bookmark)child);
789                } else {
790                    throw new IllegalArgumentException("Node " + child + " not child of this BookmarkFolder.");
791                }
792            }
793    
794        } // class BookmarkFolder
795    
796    } // class BookmarkManager