# Bookmark.py
# $Id: Bookmark.py,v 1.14 2001/07/11 22:43:09 s2mdalle Exp $
# Written by David Allen <mda@idatar.com>
# This is a subclass of GopherResource and is used in the Bookmark
# management system of the FORG.
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
############################################################################
from string import *
from Tkinter import *
import GopherResource
import ListNode
import List
import utils
import xmllib
BookmarkFactoryException = "Error"
BookmarkException = "Error"
BookmarkMenuException = "Error"
class Bookmark(GopherResource.GopherResource):
def __init__(self, res=None):
GopherResource.GopherResource.__init__(self)
if res != None:
# Copy data from the resource to this bookmark.
self.setName(res.getName())
self.setHost(res.getHost())
self.setPort(res.getPort())
self.setLocator(res.getLocator())
self.setType(res.getTypeCode())
return None
def toXML(self, fp, indentlevel):
"""Returns an XML representation of the object. Callers should use
an indentlevel of 0 since this is called recursively. fp is the file
pointer the data should be stored in."""
if self.verbose:
print "Bookmark.toXML()"
fp.write(utils.indent(indentlevel) + "<bookmark href=\"" +
self.getURL() + "\">\n")
fp.write(utils.indent(indentlevel+1) + "<title>" +
self.getName() + "</title>\n")
fp.write(utils.indent(indentlevel) + "</bookmark>\n")
return 1
def getURL(self):
return self.toURL()
def toData(self):
return "%s !! gopher://%s:%s/%s" % (self.getName(),
self.getHost(),
self.getPort(),
self.getLocator())
def __str__(self):
return self.toString()
def __repr__(self):
return self.toString()
def toString(self):
if self.getName() != '':
return "%s: %s" % (self.getHost(), self.getName())
elif self.getLocator() == '/' or self.getLocator() == '':
return "%s Root" % self.getHost()
else:
return "%s:%s %s" % (self.getHost(), self.getPort(),
self.getLocator())
# Not specific to the bookmark class methods...
def parseBookmark(data):
data = strip(data)
items = split(data, "!! ", 2)
if len(items) < 2:
print "***Bookmark parse error: Items is: %s" % join(items,"::")
return None
bmark = Bookmark()
try:
items[1] = strip(items[1])
bmark.setURL(items[1])
except Exception, estr:
print "Returning NONE because I couldn't parse the URL: %s" % estr
return None
bmark.setName(items[0])
return bmark
# Subclass ListNode just for naming purposes so BookmarkMenuNodes can be
# dealt with inside of BookmarkMenus
class BookmarkMenuNode(ListNode.ListNode):
pass
# BookmarkMenu is to organize heirarchical menus of bookmarks which will be
# editable by the user.
class BookmarkMenu(List.List):
verbose = None
def __init__(self, menuName=" "):
List.List.__init__(self)
self.menuName = menuName
return None
def getName(self):
if strip(self.menuName) > 0:
return self.menuName
else:
self.setName("Bookmarks")
return self.menuName
return "Error fetching name"
def setName(self, newname):
self.menuName = newname
return self.menuName
def toString(self):
return "BookmarkMenu: \"%s\"" % self.getName()
def __str__(self):
return self.toString()
def __repr__(self):
return self.toString()
def insert(self, item, truncate=0):
"""Overrides the insert method to always pass a truncate argument of
0 so the list is never truncated on insertions."""
return List.List.insert(self, item, 0)
def addSubmenu(self, menu):
"""Adds menu as a submenu of this menu"""
if menu.__class__ != BookmarkMenu and menu.__class__ != Bookmark:
raise Exception, "Cannot add a non-Bookmark/Menu as submenu"
return self.insert(BookmarkMenuNode(menu))
def toXML(self, fp, indentlevel):
"""Returns an XML representation of this object. This is called
recursively, so if you are calling this for the first time, use an
indentlevel of 0. fp holds the file pointer to write the data into"""
if self.verbose:
print "BookmarkMenu.toXML()"
il = indentlevel
i = utils.indent
fp.write(i(il) + "<folder>\n")
fp.write(i(il+1) + "<title>" + self.getName() + "</title>\n")
def fn(item, fp=fp, il=(il+1)):
data = item.getData()
# It doesn't matter which kind it is, whether it's a
# Bookmark or a BookmarkMenu since they both have toXML() methods
# and they'll take it from here. If it's a BookmarkMenu, this
# will happen recursively.
data.toXML(fp, il)
return data.getName()
# Apply the above function to each item in the menu
self.traverse(fn)
fp.write(i(il) + "</folder> <!-- End %s -->\n" % self.getName())
return None
def getTkMenu(self, parent_widget, callback):
"""Return a Tk Menu object which is suitable for being added to other
submenus. parent_widget is the menu you will add it to, and callback
is a function that takes exactly one argument. That argument is the
Bookmark object that the user-clicked item represents."""
# This is the menu we will return.
m = Menu(parent_widget)
# Create a subfunction to deal with each item in the list.
# This way since this object already extends List and we already have
# a traverse function, we can go through the entire list in order
def fn(listitem, cb=callback, addTo=m):
# Each item in the list is a ListNode, not a Bookmark, so get the
# data out of the ListNode.
data = listitem.getData()
def real_callback(item=data, oldCallback=cb):
"""This is a way of converting a callback that takes no
arguments into one that takes two for our purposes. The
user passes in a callback function that takes only one
parameter (the resource you want to go to) and this is
the actual function that is bound to the action, which calls
the user defined function with the appropriate argument"""
return oldCallback(item)
try:
# If it's a menu, then add the submenu recursively.
if data.__class__ == BookmarkMenu:
addTo.add_cascade(label=data.getName(),
menu=data.getTkMenu(addTo, cb))
else:
# Otherwise add a regular command into the menu.
addTo.add_command(label=data.getName(),
command=real_callback)
except:
pass
return data.getName()
# Apply the above to each item in the list, adding menu items and
# submenus where needed.
self.traverse(fn) # Don't care about the return results.
return m
class BookmarkFactory(xmllib.XMLParser):
verbose = None
def __init__(self):
xmllib.XMLParser.__init__(self)
self.menu = None
self.lastTag = ''
self.accumulator = ''
self.currentMenu = None
self.currentBmrk = None
self.folders = []
return None
def writeXML(self, fp, menu):
"""Writes an XML representation of menu to fp"""
# Header-type stuff.
fp.write("<?xml version=\"1.0\"?>\n")
fp.write("<!DOCTYPE xbel SYSTEM \"xbel-1.0.dtd\">\n\n")
fp.write("<!-- This file contains a listing of bookmarks for\n")
fp.write(" the FORG. Please do not edit this unless you are\n")
fp.write(" familiar with the structure of the file. -->\n\n");
fp.write("<xbel>\n")
menu.toXML(fp, 1)
fp.write("</xbel>\n")
fp.flush()
return 1
def getMenu(self):
"""Menu object accessor"""
return self.menu
def setMenu(self, menu):
"""Menu object mutator"""
self.menu = menu
return self.menu
def handle_data(self, data):
"""Necessary overriden method for XML parsing - when a chunk of
data comes in, this is what is done with it."""
self.accumulator = self.accumulator + data
def syntax_error(self, message):
"""Necessary overridden method for XML parsing - handles syntax
errors when they occur"""
print "****Error parsing XML: ", message
return None
def unknown_starttag(self, tag, attrs):
"""Necessary overriden method for XML parsing - handles unknown
start tags"""
print "****Error parsing XML: Unknown tag \"%s\"" % tag
return None
def unknown_endtag(self, tag):
"""Necessary overridden method for XML parsing - handles unknown
end tags"""
print "****Error parsing XML: Unknown ending tag \"%s\"" % tag
return None
def unknown_charref(self, ref):
"""Necessary overridden method for XML parsing - handles unknown
charrefs"""
print "****Error parsing XML: uknown charref \"%s\"" % ref
return None
def unknown_entityref(self, ref):
"""Necessary overridden method for XML parsing - handles unknown
entity references"""
print "****Error parsing XML: unknown entityref \"%s\"" % ref
return None
def start_xbel(self, attrs):
self.lastTag = "xbel"
if self.verbose:
print "<XBEL>"
return None
def start_folder(self, attrs):
self.currentMenu = BookmarkMenu()
self.folders.append(self.currentMenu)
self.lastTag = "folder"
if self.verbose:
print "Creating new folder"
return None
def start_bookmark(self, attrs):
self.currentBmrk = Bookmark()
self.lastTag = "bookmark"
if self.verbose:
print "Setting URL to be ", attrs['href']
try:
self.currentBmrk.setURL(attrs['href'])
except KeyError:
print "**** Error parsing XML: bookmark is missing 'href'"
except Exception, errstr:
print "**** Parse error: Couldn't parse %s: %s" % (attrs['href'],
errstr)
self.currentBmrk = None
return None
if self.verbose:
print "Creating new bookmark"
return None
def start_title(self, attrs):
if self.lastTag == 'xbel':
self.currentMenu.setName("Bookmarks")
else:
self.accumulator = ''
return None
def end_title(self):
self.accumulator = strip(self.accumulator)
if self.lastTag == 'xbel' or self.lastTag == 'folder':
if self.verbose:
print 'Setting menu name: ', self.accumulator
self.currentMenu.setName(self.accumulator)
elif self.lastTag == 'bookmark':
if self.verbose:
print "Setting bmark name: ", self.accumulator
self.currentBmrk.setName(self.accumulator)
else:
if self.verbose:
print("****Error parsing XML: Unknown lastTag %s" %
self.lastTag)
self.accumulator = ''
return None
def end_bookmark(self):
if self.verbose:
print "Inserting new bmark"
if self.currentBmrk:
self.currentMenu.insert(BookmarkMenuNode(self.currentBmrk))
else:
print "**** Error parsing XML: could not insert invalid bookmark."
self.lastTag = "/bookmark"
return None
def end_folder(self):
try:
finished_folder = self.folders.pop()
except IndexError:
print "****Error parsing XML: </folder> without <folder>"
return None
if self.verbose:
print "Finishing up folder: %s" % finished_folder.getName()
if len(self.folders) > 0:
self.currentMenu = self.folders[len(self.folders)-1]
if self.verbose:
print("Adding submenu \"%s\" to \"%s\"" %
(finished_folder.getName(),
self.currentMenu.getName()))
self.currentMenu.addSubmenu(finished_folder)
else:
# The stack is empty - assign the main menu to be this item
# here.
if self.verbose:
print "Finished toplevel folder."
self.menu = finished_folder
return None
def end_xbel(self):
if self.verbose:
print "/XBEL"
if len(self.folders) > 0:
print "**** Error parsing XML: premature </xbel> element."
def parseResource(self, fp):
for line in fp.read():
self.feed(line)
self.close()
return self.menu
syntax highlighted by Code2HTML, v. 0.9.1