[Japi-cvs] SF.net SVN: japi: [184] libs/swing-bookmarks/trunk
Status: Beta
Brought to you by:
christianhujer
From: <chr...@us...> - 2006-10-14 12:43:44
|
Revision: 184 http://svn.sourceforge.net/japi/?rev=184&view=rev Author: christianhujer Date: 2006-10-14 05:43:19 -0700 (Sat, 14 Oct 2006) Log Message: ----------- Copied bookmarks classes from historic to swing-bookmarks subproject. Added Paths: ----------- libs/swing-bookmarks/trunk/src/ libs/swing-bookmarks/trunk/src/net/ libs/swing-bookmarks/trunk/src/net/sf/ libs/swing-bookmarks/trunk/src/net/sf/japi/ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkDropTargetAdapter.java libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkManager.java libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferHandler.java libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferable.java libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTreeCellRenderer.java libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/Bookmarkable.java libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action.properties libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action_de.properties libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/package.html Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkDropTargetAdapter.java (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/BookmarkDropTargetAdapter.java) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkDropTargetAdapter.java (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkDropTargetAdapter.java 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,84 @@ +/* JAPI - (Yet another (hopefully) useful) Java API + * + * Copyright (C) 2004-2006 Christian Hujer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + + +// $Header: /cvsroot/japi/japi/src/app/net/sf/japi/swing/bookmarks/BookmarkDropTargetAdapter.java,v 1.1 2006/03/26 01:26:25 christianhujer Exp $ + +package net.sf.japi.swing.bookmarks; + +import java.awt.Point; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.io.IOException; +import javax.swing.JTree; +import javax.swing.tree.TreePath; +import org.jetbrains.annotations.Nullable; +import static net.sf.japi.swing.bookmarks.BookmarkTransferable.getBookmarkDataFlavor; + +/** Class for dropping a bookmark over a JTree managing bookmarks. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + */ +public class BookmarkDropTargetAdapter extends DropTargetAdapter { + + /** {@inheritDoc} */ + public void drop(final DropTargetDropEvent dtde) { + System.err.println("drop"); + final JTree tree = (JTree) dtde.getDropTargetContext().getComponent(); + BookmarkManager.Bookmark dest = getDestBookmark(dtde, tree); + final BookmarkManager.Bookmark src = getSourceBookmark(dtde); + int pos = -1; + if (!(dest instanceof BookmarkManager.BookmarkFolder)) { + pos = dest.getFolder().getIndex(dest); + dest = dest.getFolder(); + } + assert dest instanceof BookmarkManager.BookmarkFolder; + ((BookmarkManager.BookmarkFolder)dest).insert(src, pos); + tree.treeDidChange(); + } + + /** Find the destination bookmark for a drop event. + * @param dtde drop event + * @param tree JTree to find bookmark in + * @return destination bookmark + */ + private static BookmarkManager.Bookmark getDestBookmark(final DropTargetDropEvent dtde, final JTree tree) { + final Point p = dtde.getLocation(); + final TreePath tp = tree.getClosestPathForLocation(p.x, p.y); + return (BookmarkManager.Bookmark) tp.getLastPathComponent(); + } + + /** Find source bookmark for a drop event. + * @param dtde drop event + * @return source bookmark + */ + @Nullable private static BookmarkManager.Bookmark getSourceBookmark(final DropTargetDropEvent dtde) { + final Transferable source = dtde.getTransferable(); + try { + return (BookmarkManager.Bookmark) source.getTransferData(getBookmarkDataFlavor()); + } catch (final UnsupportedFlavorException e) { + return null; + } catch (final IOException e) { + return null; + } + } + +} // class BookmarkDropTargetAdapter Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkManager.java (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/BookmarkManager.java) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkManager.java (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkManager.java 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,796 @@ +/* JAPI - (Yet another (hopefully) useful) Java API + * + * Copyright (C) 2004-2006 Christian Hujer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +package net.sf.japi.swing.bookmarks; + +import java.awt.BorderLayout; +import java.awt.dnd.DropTarget; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.showInputDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.JTree; +import javax.swing.tree.MutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.ls.DOMImplementationLS; +import org.xml.sax.SAXException; +import net.sf.japi.swing.ActionFactory; +import net.sf.japi.swing.IconManager; +import net.sf.japi.util.EmptyEnumeration; + +/** Class for managing and displaying Bookmarks. + * Usage of this class works the following way: + * <ul> + * <li>implement the interface {@link Bookmarkable} and its methods</li> + * <li>instanciate this class once for each gruop / kind of bookmarks you want to manage. Normally, one instance is enough per application</li> + * <li>invoke {@link #createBookmarkMenu} to create your bookmarks menu</li> + * </ul> + * @author $Author: christianhujer $ + * @todo documentation + * @todo fix bookmark drag and drop indices + * @todo think about serialization of Actions + */ +public class BookmarkManager { + + /** The Bookmarks. */ + private BookmarkFolder bookmarks = new BookmarkFolder(); + + /** Action Factory. */ + private static final ActionFactory ACTION_FACTORY = ActionFactory.getFactory("net.sf.japi.swing.bookmarks"); + + /** The ProgramFrame this BookmarkManager manages bookmarks for. */ + private Bookmarkable bookmarkable; + + /** Create a new BookmarkManager. */ + public BookmarkManager(final Bookmarkable bookmarkable) { + this.bookmarkable = bookmarkable; + try { + load(); + save(); + } catch (final Exception e) { + e.printStackTrace(); + // TODO: improve dialog + showMessageDialog(bookmarkable.getBookmarkBlocker(), ACTION_FACTORY.getString("bookmarksCreated.message")); + } + } + + /** Create a Bookmark ToolBar. + * @return toolBar for Bookmarks + */ + public JToolBar createBookmarkToolBar() { + // Variant 1: JToolBar with JMenuBar with one entry "Lesezeichen" + final JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name")); + final JMenuBar mb = new JMenuBar(); + mb.add(bookmarks.createMenu()); + toolBar.add(mb); + return toolBar; + + //// Variant 2: JToolBar with JMenus and JMenuItems (broken) + //JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name")); + //for (Bookmark bookmark : bookmarks) { + // toolBar.add(bookmark.createMenu()); + //} + //return toolBar; + + //// Variant 3: JToolBar with JMenuBar with JMenus and JMenuItems (JMenuItems don't hover) + //JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name")); + //JMenuBar mb = new JMenuBar(); + //for (Bookmark bookmark : bookmarks) { + // mb.add(bookmark.createMenu()); + //} + //toolBar.add(mb); + //return toolBar; + + //// Variant 4: JToolBar with a button activating a PopupMenu. + //// Doesn't work either, the popup menu is not correctly usable. + //JToolBar toolBar = new JToolBar(ACTION_FACTORY.getString("bookmarkToolBar.name")); + //final JPopupMenu menu = new JPopupMenu(ACTION_FACTORY.getString("bookmark.text")); + //menu.add(bookmarks.createMenu()); + //toolBar.add( + // new javax.swing.JButton( + // new AbstractAction() { + // public void actionPerformed(final ActionEvent e) { + // menu.setVisible(true); + // } + // } + // ) + //); + //return toolBar; + } + + /** Create a Bookmark Menu. + * @return JMenu for Bookmarks + */ + public JMenu createBookmarkMenu() { + final JMenu menu = bookmarks.createMenu(); + menu.add(new JMenuItem(manageBookmarks)); + return menu; + } + + /** Create a Bookmark ControlPanel. + * @return ControlPanel for Bookmarks + */ + public JComponent createBookmarkControlPanel() { + return new ControlPanel(); + } + + /** Recursive attach method. + * @param menu JMenu to attach bookmarks to + * @param bookmarks Bookmarks to attach + */ + private static void attach(final JMenu menu, final BookmarkFolder bookmarks) { + for (final Bookmark bookmark : bookmarks) { + if (bookmark instanceof BookmarkFolder) { + final JMenu newMenu = new JMenu(bookmark); + attach(newMenu, (BookmarkFolder) bookmark); + } else { + assert bookmark instanceof BookmarkItem; + menu.add(new JMenuItem(bookmark)); + } + } + } + + /** Action for managing the bookmarks. + * @serial include + */ + private Action manageBookmarks = ACTION_FACTORY.createAction(true, "manageBookmarks", this); + + /** Action for managing the bookmarks. */ + public void manageBookmarks() { + final JFrame f = new JFrame(ACTION_FACTORY.getString("manageBookmarks_shortdescription")); + f.getContentPane().add(createBookmarkControlPanel()); + //f.getContentPane().add(new JScrollPane(new JTree(bookmarks))); + f.pack(); + f.setVisible(true); + } + + /** Set the AddBookmark enabled state. + * @param enabled enabled state for AddBookmark action + */ + public void setAddBookmarkEnabled(final boolean enabled) { + bookmarks.setAddBookmarkEnabled(enabled); + } + + /** Load bookmarks from default file. + * @throws IOException in case of I/O problems + * @throws ParserConfigurationException in case of XML configuration problems + * @throws SAXException in case of XML document problems + */ + public void load() throws IOException, ParserConfigurationException, SAXException { + load(new File(System.getProperty("user.home"), ".jeduca.bookmarks.xml").toURL().toString()); + } + + /** Save bookmarks to default file. + * @throws IOException in case of I/O problems + * @throws ParserConfigurationException in case of XML configuration problems + */ + public void save() throws IOException, ParserConfigurationException { + save(new File(System.getProperty("user.home"), ".jeduca.bookmarks.xml").toURL().toString()); + } + + /** Save bookmarks to a file. + * @param uri URI of file to save + * @throws IOException in case of I/O problems + * @throws ParserConfigurationException in case of XML configuration problems + */ + public void save(final String uri) throws IOException, ParserConfigurationException { + final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + final DocumentBuilder db = dbf.newDocumentBuilder(); + final Document doc = db.newDocument(); + bookmarks.store(doc); + final DOMImplementation impl = doc.getImplementation(); + if (impl instanceof DOMImplementationLS) { + final DOMImplementationLS ls = (DOMImplementationLS) impl; + ls.createLSSerializer().writeToURI(doc, uri); + } + } + + /** Load bookmarks from a file. + * @param uri URI of file to load + * @throws IOException in case of I/O problems + * @throws ParserConfigurationException in case of XML configuration problems + * @throws SAXException in case of XML document problems + */ + public void load(final String uri) throws IOException, ParserConfigurationException, SAXException { + final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + final DocumentBuilder db = dbf.newDocumentBuilder(); + final Document doc = db.parse(uri); + bookmarks = new BookmarkFolder(doc); + } + + + /** Class for a ControlPanel to manage the bookmarks. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $ + */ + private class ControlPanel extends JComponent { + + /** Serial Version. */ + private static final long serialVersionUID = 1L; + + /** Create a new ControlPanel. */ + ControlPanel() { + setLayout(new BorderLayout()); + final JTree tree = new JTree(bookmarks, true); + tree.setRootVisible(false); + tree.setCellRenderer(new BookmarkTreeCellRenderer()); + //tree.setEditable(true); + tree.setDragEnabled(true); + final DropTarget dt = new DropTarget(tree, new BookmarkDropTargetAdapter()); + tree.setTransferHandler(new BookmarkTransferHandler()); + add(new JScrollPane(tree)); + } + + } // class ControlPanel + + + /** Base class for bookmarks. + * There are two kinds of bookmarks: + * <ul> + * <li>{@link BookmarkItem}s for normal Bookmarks with title and url</li> + * <li>{@link BookmarkFolder}s for Folders within Bookmarks with a title and (possibly) contents</li> + * </ul> + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $ + */ + public static abstract class Bookmark extends AbstractAction implements MutableTreeNode { + + /** Title for Bookmark. + * @serial include + */ + private String title; + + /** The folder (parent) of this bookmark. + * @serial include + */ + protected BookmarkFolder folder; + + ///** Create a Bookmark without title. + // * Should only be used by {@link BookmarkFolder#BookmarkFolder()}. + // */ + //protected Bookmark() { + //} + + /** Create a Bookmark. + * You must not forget to invoke {@link #setFolder(BookmarkFolder)} in order to make {@link #getParent()} for JTrees working. + * @param title title for Bookmark + */ + protected Bookmark(final String title) { + setTitle(title); + putValue(SMALL_ICON, IconManager.getDefaultIconManager().getIcon(ACTION_FACTORY.getString("bookmark.icon"))); + } + + /** {@inheritDoc} + * @return title of this Bookmark + */ + public String toString() { + return title; + } + + /** Get the folder (parent) of this bookmark. + * @return folder (parent) of this bookmark + */ + public BookmarkFolder getFolder() { + return folder; + } + + /** Set the folder (parent) of this bookmark. + * @param folder parent folder of this bookmark + */ + public void setFolder(final BookmarkFolder folder) { + if (this.folder != null) { + this.folder.remove(this); + } + this.folder = folder; + } + + /** Set this Bookmark's title. + * @param title new title for this Bookmark + */ + public void setTitle(final String title) { + this.title = title; + putValue(NAME, title); + } + + /** Create a MenuItem for this Bookmark + * @return MenuItem for this Bookmark + */ + public abstract JMenuItem createMenu(); + + /** Get this Bookmark's title. + * @return Bookmark title + */ + public String getTitle() { + return title; + } + + /** Store bookmarks in an XML Document. + * @param n Node (Element or Document) to attach to + */ + public abstract void store(final Node n); + + /** {@inheritDoc} */ + public Enumeration<Bookmark> children() { + return new EmptyEnumeration<Bookmark>(); + } + + /** {@inheritDoc} */ + public boolean getAllowsChildren() { + return false; + } + + /** {@inheritDoc} */ + public Bookmark getChildAt(final int childIndex) { + throw new IndexOutOfBoundsException(Integer.toString(childIndex)); + } + + /** {@inheritDoc} */ + public int getChildCount() { + return 0; + } + + /** {@inheritDoc} */ + public int getIndex(final TreeNode node) { + return 0; + } + + /** {@inheritDoc} */ + public BookmarkFolder getParent() { + return getFolder(); + } + + /** {@inheritDoc} */ + public boolean isLeaf() { + return true; + } + + /** {@inheritDoc} */ + public void insert(final MutableTreeNode child, final int index) { + throw new IllegalStateException("A bookmark cannot have children"); + } + + /** {@inheritDoc} */ + public void remove(final int index) { + throw new IllegalStateException("A bookmark has no children"); + } + + /** {@inheritDoc} */ + public void remove(final MutableTreeNode child) { + throw new IllegalStateException("A bookmark has no children"); + } + + /** {@inheritDoc} */ + public void removeFromParent() { + folder.remove(this); + } + + /** {@inheritDoc} */ + public void setParent(final MutableTreeNode newParent) { + if (newParent instanceof BookmarkFolder) { + setFolder((BookmarkFolder)newParent); + } else { + throw new IllegalArgumentException("Parent must be a BookmarkFolder, but was : " + newParent.getClass().getName()); + } + } + + /** {@inheritDoc} */ + public void setUserObject(final Object object) { + System.err.println("setUserObject was called but does not know what to do. Supplied object: " + object); + } + + } // class Bookmark + + + /** Class for Bookmark Separator. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $ + */ + public static class BookmarkSeparator extends Bookmark { + + /** Serial Version. */ + private static final long serialVersionUID = 1L; + + /** Create a Bookmark Separator. */ + BookmarkSeparator() { + super("--------------------------------"); + } + + /** {@inheritDoc} + * @return always <code>null</code> + */ + @Override public JMenuItem createMenu() { + return null; + } + + /** {@inheritDoc} */ + @Override public void store(final Node n) { + final Element e = n.getOwnerDocument().createElement("separator"); + n.appendChild(e); + } + + /** {@inheritDoc} */ + public void actionPerformed(final ActionEvent e) { + /* Do nothing */ + } + + } // class BookmarkSeparator + + + /** Class for Bookmark Items. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $ + */ + public class BookmarkItem extends Bookmark { + + /** Serial Version. */ + private static final long serialVersionUID = 1L; + + /** URL of this Bookmark. + * @serial include + */ + private String url; + + /** Create a BookmarkItem. + * @param title title for this BookmarkItem + * @param url URL for this BookmarkItem + */ + public BookmarkItem(final String title, final String url) { + super(title); + setURL(url); + } + + /** Create a BookmarkItem from XML. + * @param el Element to create from + */ + BookmarkItem(final Element el) { + this(el.getAttribute("title"), el.getAttribute("href")); + } + + /** Set this BookmarkItem's url. + * @param url new url for this BookmarkItem + */ + public void setURL(final String url) { + this.url = url; + putValue(SHORT_DESCRIPTION, url); + } + + /** Get this BookmarkItem's url. + * @return BookmarkItem url + */ + public String getURL() { + return url; + } + + /** {@inheritDoc} */ + @Override public JMenuItem createMenu() { + return new JMenuItem(this); + } + + /** {@inheritDoc} */ + public void actionPerformed(final ActionEvent e) { + bookmarkable.load(url); + } + + /** {@inheritDoc} */ + public void store(final Node n) { + final Element e = n.getOwnerDocument().createElement("bookmark"); + e.setAttribute("title", getTitle()); + e.setAttribute("href", getURL()); + n.appendChild(e); + } + + } // class BookmarkItem + + + /** Class for Bookmark folders. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @version $Id: BookmarkManager.java,v 1.2 2006/03/26 15:22:16 christianhujer Exp $ + */ + public class BookmarkFolder extends Bookmark implements Iterable<Bookmark> { + + /** Serial Version. */ + private static final long serialVersionUID = 1L; + + /** The bookmarks in this folder. + * @serial include + */ + private List<Bookmark> bookmarks = new ArrayList<Bookmark>(); + + /** The Menus created for this folder. + * @serial include + */ + private List<JMenu> menus = new ArrayList<JMenu>(); + + /** Create a BookmarkFolder without a title. + * This should only be used for the basic BookmarkFolder containing all other BookmarkItems and BookmarkFolders. + */ + public BookmarkFolder() { + super(ACTION_FACTORY.getString("bookmark_text")); + } + + /** Create a BookmarkFolder. + * @param title title for BookmarkFolder + */ + public BookmarkFolder(final String title) { + super(title); + addBookmark.putValue(ACCELERATOR_KEY, null); + newBookmarkFolder.putValue(ACCELERATOR_KEY, null); + } + + /** Create a BookmarkFolder from XML. + * @param doc Document to create from + */ + BookmarkFolder(final Document doc) { + this(); + readNodes(doc.getDocumentElement()); + } + + /** Create a BookmarkFolder from XML. + * @param el Element to create from + */ + BookmarkFolder(final Element el) { + this(el.getAttribute("title")); + readNodes(el); + } + + /** Read nodes from XML. + * @param el XML Element to read nodes from + */ + private void readNodes(final Element el) { + final NodeList children = el.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + final Node n = children.item(i); + if (n instanceof Element) { + final Element e = (Element) n; + if ("bookmark".equals(e.getNodeName())) { + add(new BookmarkItem(e)); + } else if ("bookmarks".equals(e.getNodeName())) { + add(new BookmarkFolder(e)); + } else if ("separator".equals(e.getNodeName())) { + add(new BookmarkSeparator()); + } + } + } + } + + /** {@inheritDoc} */ + public Iterator<Bookmark> iterator() { + return bookmarks.iterator(); + } + + /** Add a Bookmark to this BookmarkFolder. + * @param bookmark Bookmark to add + */ + public void add(final Bookmark bookmark) { + final int pos = bookmarks.size(); + bookmarks.add(bookmark); + bookmark.setFolder(this); + for (JMenu menu : menus) { + menu.insert(bookmark.createMenu(), pos); + } + try { + save(); + } catch (final Exception e) { + showMessageDialog(bookmarkable.getBookmarkBlocker(), e, "Bookmarks-Fehler", ERROR_MESSAGE); + System.err.println(e); + e.printStackTrace(); + } + } + + /** Insert a Bookmark into this BookmarkFolder. + * @param bookmark Bookmark to add + * @param index desired index + */ + public void insert(final Bookmark bookmark, final int index) { + if (index < 0 || index > bookmarks.size()) { + throw new IllegalArgumentException("Invalid index: " + index + " (size: " + bookmarks.size() + ")"); + } + bookmark.setFolder(this); + bookmarks.add(index, bookmark); + for (JMenu menu : menus) { + menu.insert(bookmark.createMenu(), index); + } + try { + save(); + } catch (final Exception e) { + showMessageDialog(bookmarkable.getBookmarkBlocker(), e, "Bookmarks-Fehler", ERROR_MESSAGE); + System.err.println(e); + e.printStackTrace(); + } + } + + /** Remove a Bookmark from this BookmarkFolder. + * @param bookmark Bookmark to remove + */ + public void remove(final Bookmark bookmark) { + if (bookmarks.contains(bookmark)) { + remove(bookmarks.indexOf(bookmark)); + } else { + for (final Bookmark folder : bookmarks) { + if (folder instanceof BookmarkFolder) { + ((BookmarkFolder)folder).remove(bookmark); + } + } + } + } + + /** Remove a Bookmark from this BookmarkFolder. + * @param index Index of Bookmark to remove + */ + @Override public void remove(final int index) { + final Bookmark bookmark = bookmarks.remove(index); + bookmark.folder = null; + for (JMenu menu : menus) { + menu.remove(index); + } + } + + /** {@inheritDoc} */ + @Override public JMenu createMenu() { + final JMenu menu = new JMenu(getTitle()); + menu.setName(getTitle()); + for (Bookmark bookmark : bookmarks) { + if (bookmark instanceof BookmarkSeparator) { + menu.addSeparator(); + } else { + menu.add(bookmark.createMenu()); + } + } + menu.addSeparator(); + menu.add(new JMenuItem(addBookmark)); + menu.add(new JMenuItem(newBookmarkFolder)); + menus.add(menu); + return menu; + } + + /** {@inheritDoc} */ + public void actionPerformed(final ActionEvent e) { + /* Do nothing */ + } + + /** Action for adding a bookmark. + * @serial include + */ + private Action addBookmark = ACTION_FACTORY.createAction(true, "addBookmark", this); + + /** Action for creating a new bookmark folder. + * @serial include + */ + private Action newBookmarkFolder = ACTION_FACTORY.createAction(true, "newBookmarkFolder", this); + + /** Add a bookmark for the currently selected Question from the currently selected QuestionCollection . */ + public void addBookmark() { + if (bookmarkable.isBookmarkPossible()) { + add(new BookmarkItem(bookmarkable.getBookmarkTitle(), bookmarkable.getBookmarkURL())); + } + } + + /** Create a new BookmarkFolder. */ + public void newBookmarkFolder() { + final String folderName = showInputDialog(bookmarkable.getBookmarkBlocker(), "Name für Lesezeichen", "Neuer Lesezeichen-Ordner", QUESTION_MESSAGE); + if (folderName == null) { + return; + } + add(new BookmarkFolder(folderName)); + } + + /** Set the AddBookmark enabled state. + * @param enabled enabled state for AddBookmark action + */ + public void setAddBookmarkEnabled(final boolean enabled) { + addBookmark.setEnabled(enabled); + for (Bookmark folder : bookmarks) { + if (folder instanceof BookmarkFolder) { + ((BookmarkFolder)folder).setAddBookmarkEnabled(enabled); + } + } + } + + /** {@inheritDoc} */ + @Override public void store(final Node n) { + final Element e = (n instanceof Document ? (Document) n : n.getOwnerDocument()).createElement("bookmarks"); + e.setAttribute("title", getTitle()); + n.appendChild(e); + for (Bookmark bookmark : bookmarks) { + bookmark.store(e); + } + } + + /** {@inheritDoc} */ + public Enumeration<Bookmark> children() { + return Collections.enumeration(bookmarks); + } + + /** {@inheritDoc} */ + @Override public boolean getAllowsChildren() { + return true; + } + + /** {@inheritDoc} */ + @Override public Bookmark getChildAt(final int childIndex) { + return bookmarks.get(childIndex); + } + + /** {@inheritDoc} */ + @Override public int getChildCount() { + return bookmarks.size(); + } + + /** {@inheritDoc} */ + @Override public int getIndex(final TreeNode node) { + return bookmarks.indexOf(node); + } + + /** {@inheritDoc} */ + @Override public boolean isLeaf() { + return false; + } + + /** {@inheritDoc} */ + @Override public void insert(final MutableTreeNode child, final int index) { + if (child instanceof Bookmark) { + insert((Bookmark)child, index); + } else { + throw new IllegalArgumentException("Children of BookmarkFolders must be instance of Bookmark but was " + child.getClass().getName()); + } + } + + ///** {@inheritDoc} */ + //public void remove(final int index) { + // remove(index); + //} + + /** {@inheritDoc} */ + @Override public void remove(final MutableTreeNode child) { + if (child instanceof Bookmark) { + remove((Bookmark)child); + } else { + throw new IllegalArgumentException("Node " + child + " not child of this BookmarkFolder."); + } + } + + } // class BookmarkFolder + +} // class BookmarkManager Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferHandler.java (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/BookmarkTransferHandler.java) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferHandler.java (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferHandler.java 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,59 @@ +/* JAPI - (Yet another (hopefully) useful) Java API + * + * Copyright (C) 2004-2006 Christian Hujer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +package net.sf.japi.swing.bookmarks; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.TransferHandler; + +/** Class for DnD in Bookmarks displaying JTrees. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @todo improve implementation + */ +public class BookmarkTransferHandler extends TransferHandler { + + /** Serial Version. */ + private static final long serialVersionUID = 1L; + + /** {@inheritDoc} */ + @Override + public Transferable createTransferable(final JComponent c) { + return new BookmarkTransferable((BookmarkManager.Bookmark)((JTree)c).getSelectionPath().getLastPathComponent()); + } + + /** {@inheritDoc} + * @todo correctly implement this + */ + @Override + public boolean canImport(final JComponent comp, final DataFlavor[] transferFlavors) { + // TODO + return true; + } + + /** {@inheritDoc} */ + @Override + public int getSourceActions(final JComponent c) { + return COPY_OR_MOVE; + } + +} // class BookmarkTransferHandler Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferable.java (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/BookmarkTransferable.java) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferable.java (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTransferable.java 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,80 @@ +/* JAPI - (Yet another (hopefully) useful) Java API + * + * Copyright (C) 2004-2006 Christian Hujer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +package net.sf.japi.swing.bookmarks; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; + +/** Class for transfering a bookmark through a clipboard or drag and drop. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + */ +public class BookmarkTransferable implements Transferable { + + /** Data Flavor for Bookmarks. */ + private static final DataFlavor bookmarkDataFlavor = initBookmarkFlavor(); + + /** Initialize DataFlavor for Bookmarks. + * @return DataFlavor for Bookmarks + */ + private static DataFlavor initBookmarkFlavor() { + try { + return new DataFlavor("application/x-jtest-bookmark"); + } catch (ClassNotFoundException e) { + throw new Error(e); + } + } + + /** Supported DataFlavors. */ + private static DataFlavor[] flavors = { bookmarkDataFlavor }; + + /** Bookmark to be transferred. */ + private final BookmarkManager.Bookmark bookmark; + + /** Create a BookmarkTransferable. + * @param bookmark Bookmark to transfer + */ + public BookmarkTransferable(final BookmarkManager.Bookmark bookmark) { + this.bookmark = bookmark; + } + + /** {@inheritDoc} */ + public Object getTransferData(final DataFlavor flavor) { + return bookmark; + } + + /** {@inheritDoc} */ + public DataFlavor[] getTransferDataFlavors() { + return flavors; + } + + /** {@inheritDoc} */ + public boolean isDataFlavorSupported(final DataFlavor flavor) { + return flavor.equals(bookmarkDataFlavor); + } + + /** Get the DataFlavor for Bookmarks. + * @return DataFlavor for Bookmarks + */ + public static DataFlavor getBookmarkDataFlavor() { + return bookmarkDataFlavor; + } + +} // class BookmarkTransferable Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTreeCellRenderer.java (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/BookmarkTreeCellRenderer.java) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTreeCellRenderer.java (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/BookmarkTreeCellRenderer.java 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,60 @@ +/* JAPI - (Yet another (hopefully) useful) Java API + * + * Copyright (C) 2004-2006 Christian Hujer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +package net.sf.japi.swing.bookmarks; + +import java.awt.Component; +import javax.swing.JSeparator; +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +/** Class for rendering TreeCells in JTrees which manage Bookmarks. + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + * @todo improve separator + */ +public class BookmarkTreeCellRenderer extends DefaultTreeCellRenderer { + + /** Serial Version. */ + private static final long serialVersionUID = 1L; + + /** JSeparator. + * @serial include + */ + private JSeparator sep = new JSeparator(); + + /** {@inheritDoc} */ + @Override public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { + if (!(value instanceof BookmarkManager.Bookmark)) { + throw new IllegalArgumentException("Supplied value must be a bookmark but was " + value.getClass().getName()); + //return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + } + if (value instanceof BookmarkManager.BookmarkSeparator) { + return super.getTreeCellRendererComponent(tree, "----------------", sel, expanded, leaf, row, hasFocus); + //sep.setMinimumSize(new Dimension(tree.getWidth() / 2, 2)); + //sep.setPreferredSize(new Dimension(tree.getWidth(), 2)); + //sep.setSize(5, tree.getWidth()); + ////sep.setBorder(new ); + //return sep; + } else { + return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + } + } + +} // class BookmarkTreeCellRenderer Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/Bookmarkable.java (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/Bookmarkable.java) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/Bookmarkable.java (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/Bookmarkable.java 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,63 @@ +/* JAPI - (Yet another (hopefully) useful) Java API + * + * Copyright (C) 2004-2006 Christian Hujer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +package net.sf.japi.swing.bookmarks; + +import java.awt.Component; +import net.sf.japi.swing.io.CanLoad; + +/** Interface for classes that want to interact with the BookmarkManager. + * Implement this interface if your class provides information for creating bookmarks. + * See the class {@link BookmarkManager} for more information on Bookmarks. + * @see BookmarkManager + * @author <a href="mailto:ch...@ri...">Christian Hujer</a> + */ +public interface Bookmarkable extends CanLoad { + + /** Get wether it currently is possible to create a bookmark. + * @return <code>true</code> if it is possible to create a bookmark, e.g. {@link #getBookmarkTitle()} and {@link #getBookmarkURL()} will return + * meaningful values, otherwise <code>false</code> + */ + boolean isBookmarkPossible(); + + /** Get the title for the Bookmark. + * @return title for Bookmark + */ + String getBookmarkTitle(); + + /** Get the URL for the Bookmark. + * @return url for Bookmark + */ + String getBookmarkURL(); + + /** {@inheritDoc} + * Invoked when the user requests to load a Bookmark. + * The implementor of this method is itself responsible of handling errors and displaying eventual error messages to the user + * @param url URL from bookmark + */ + boolean load(final String url); + + /** Get the component which to block for modal dialogs. + * It is safe to return <code>null</code>. + * @return component + */ + Component getBookmarkBlocker(); + +} // interface Bookmarkable Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action.properties (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/action.properties) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action.properties (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action.properties 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,54 @@ +# +# JAPI - (Yet another (hopefully) useful) Java API +# +# Copyright (C) 2006 Christian Hujer +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +# $Header: /cvsroot/japi/japi/src/app/net/sf/japi/swing/bookmarks/action.properties,v 1.1 2006/03/26 01:26:25 christianhujer Exp $ +# Properties for Bookmarks + +########### +# Bookmarks + +bookmarkToolBar.name=Bookmarks toolbar + +bookmark.text=Bookmarks +bookmark.mnemonic=B +bookmark.icon=general/Bookmarks + +addBookmark.text=Add +addBookmark.shortdescription=Add bookmark for this question +addBookmark.longdescription=Adds a bookmark for this question +addBookmark.mnemonic=A +addBookmark.accel=ctrl pressed B +addBookmark.icon=general/Bookmarks + +newBookmarkFolder.text=New Folder... +newBookmarkFolder.shortdescription=Create a new Bookmark Folder +newBookmarkFolder.longdescription=Creates a new Bookmark Folder at the end of these Boookmarks +newBookmarkFolder.mnemonic=N +newBookmarkFolder.accel=ctrl pressed F +newBookmarkFolder.icon=general/Bookmarks + +manageBookmarks.text=Manage... +manageBookamrks.shortdescription=Manage bookmarks +manageBookmarks.longdescription=Show a dialog to manage your bookmarks +manageBookmarks.mnemonic=M +manageBookmarks.icon=general/Bookmarks + +bookmarksCreated.message=Bookmarks created Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action_de.properties (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/action_de.properties) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action_de.properties (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/action_de.properties 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,51 @@ +# +# JAPI - (Yet another (hopefully) useful) Java API +# +# Copyright (C) 2006 Christian Hujer +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +########### +# Bookmarks + +bookmarkToolBar.name=Lesezeichenleiste + +bookmark.text=Lesezeichen +bookmark.mnemonic=L +#bookmark.icon + +addBookmark.text=Hinzuf\xFCgen +addBookmark.shortdescription=Lesezeichen f\xFCr diese Frage hinzuf\xFCgen +addBookmark.longdescription=F\xFCgt ein Lesezeichen f\xFCr diese Frage in die Liste mit Lesezeichen ein +addBookmark.mnemonic=H +addBookmark.accel=ctrl pressed B +#addBookmark.icon + +newBookmarkFolder.text=Neuer Ordner... +newBookmarkFolder.shortdescription=Neuen Lesezeichen-Ordner erstellen +newBookmarkFolder.longdescription=Erstellt einen neuen Lesezeichen-Ordner am Ende dieser Lesezeichen +newBookmarkFolder.mnemonic=N +newBookmarkFolder.accel=ctrl pressed F +newBookmarkFolder.icon=general/Bookmarks + +manageBookmarks.text=Verwalten... +manageBookamrks.shortdescription=Lesezeichen verwalten +manageBookmarks.longdescription=Zeigt einen Dialog zum Verwalten der Lesezeichen +manageBookmarks.mnemonic=V +#manageBookmarks.icon=general/Bookmarks + +bookmarksCreated.message=Bookmarks erzeugt Copied: libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/package.html (from rev 181, historic/trunk/src/app/net/sf/japi/swing/bookmarks/package.html) =================================================================== --- libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/package.html (rev 0) +++ libs/swing-bookmarks/trunk/src/net/sf/japi/swing/bookmarks/package.html 2006-10-14 12:43:19 UTC (rev 184) @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- JAPI - (Yet another (hopefully) useful) Java API + - + - Copyright (C) 2006 Christian Hujer + - + - This program is free software; you can redistribute it and/or + - modify it under the terms of the GNU General Public License as + - published by the Free Software Foundation; either version 2 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, but + - WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + - General Public License for more details. + - + - You should have received a copy of the GNU General Public License + - along with this program; if not, write to the Free Software + - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + - 02111-1307, USA. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de"> + <head> + <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" /> + <meta name="Date" content="$Date: 2006-04-03 23:00:14 +0200 (Mon, 03 Apr 2006) $" /> + <title>net.sf.japi.swing.bookmarks</title> + </head> + <body> + <p> + This package contains classes and interfaces related to bookmarks. + Using these classes you can: + </p> + <ul> + <li>Add a bookmark for the current document to the list of bookmarks</li> + <li>Create a Menu to display the bookmarks</li> + <li>Create a Toolbar to display the bookmarks</li> + <li>Activate a bookmark from the Menu or Toolbar and set the current document to the bookmark</li> + <li>Load bookmarks</li> + <li>Save bookmarks</li> + <li>Manage bookmarks (in progress)</li> + <li>Import bookmarks (planned)</li> + <li>Export bookmarks (planned)</li> + <li>Join bookmarks (planned)</li> + </ul> + </body> +</html> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |