#!/usr/bin/python # Buzz Lite # 06/04/02 by Charles Nofsinger # This code is governed by the Gnu Public License # and is provided without any warranty of any kind # by using this code you accept full responsibility for any damage # of any kind import string,sys,os import GDK from gtk import * from xml.sax import make_parser,handler OUTPUT_ENCODING = 'ISO-8859-1' global xml def delete_event( window, event): global xml if xml.modified: xml.message("File was Modifed!") return 1 mainquit() return 0 class ErrorHandler: global BigFatal BigFatal="SGML Syntax Error" global isopml def error(self, exception): 'Handle a recoverable error.' print ('Error: %s\n' % exception) def fatalError(self, exception): 'Handle a non-recoverable error.' print ('Fatal error: %s\n' % exception) raise BigFatal def warning(self, exception): 'Handle a warning.' print ('Warning: %s\n' % exception) class ElementWrapper: def __init__(self, EltType): self.EltType = EltType def __elt__(self): return self.EltType def startsave(self,outfile,newvalue): global bracket_open global lastend if self.EltType == '__Root': return lastend=self.EltType if self.EltType != '__data': if self.EltType == '__attr': attrname=self.xml_escape(newvalue[:newvalue.find('=')]) attrval=self.xml_escape(newvalue[len(attrname)+1:]) outfile.write(' %s=\'%s\' '%(attrname,attrval)) elif self.EltType != '__outline': if bracket_open: outfile.write('>') outfile.write('<%s'%newvalue) bracket_open = 1 else: if bracket_open: outfile.write('>') outfile.write('') bracket_open=0 newvalue = string.replace(newvalue, '&nl;','\n') outfile.write('%s' % self.xml_escape(newvalue,1)) def endsave(self,outfile,newvalue): global lastend global bracket_open if self.EltType == '__Root' or self.EltType == '__attr': return if self.EltType != '__data': if bracket_open: outfile.write('/>') bracket_open=0 lastend='__data' return if lastend != '__data': outfile.write('\n') if self.EltType != '__outline': outfile.write(''%(newvalue)) else: outfile.write('') lastend=self.EltType else: lastend='__data' def xml_escape(self,str,isdata=0): if str: if 1: str = string.replace(str, '&', '&') str = string.replace(str, '<', '<') str = string.replace(str, '>', '>') str = string.replace(str, '"', '"') if not isdata: str = string.replace(str, "'", ''') #str = string.replace(str, '\n', '&nl;') try: u=unicode(str,'ISO-8859-1') str=u.encode('ISO-8859-1','replace') except: news='' for i in range(len(str)): try: news=news+str[i].encode('ISO-8859-1','replace') except: news=news+'?' str=news return str else: return '' class GetElements(handler.ContentHandler): def setTree(self, tree,nodeStack): self.tree = tree self.nodeStack=nodeStack self.lasttype="root" def setDocumentLocator(self, locator): 'Receive an mbject for locating the origin of SAX document events.' self.locator = locator def startElement(self, name, attrs): self.lasttype=name if (name == 'outline' or name == 'Outline') and attrs.has_key('text'): title=attrs['text'] title=title.encode('ISO-8859-1','replace') newnode=title name='__outline' else: name = name.encode('ISO-8859-1','replace') newnode=name id = self.tree.insert_node(self.nodeStack[-1], None,[newnode],is_leaf=0) if not self.nodeStack[-1]: self.tree.root=id self.tree.node_set_row_data(id,ElementWrapper(name)) self.nodeStack.append(id) for attr in attrs.keys(): if attr != 'text': name= attr+"="+attrs[attr] name=name.encode('ISO-8859-1','replace') attid = self.tree.insert_node(id, None,[name],is_leaf=1) self.tree.node_set_row_data(attid,ElementWrapper('__attr')) def endElement(self, name ): self.nodeStack = self.nodeStack[:-1] self.lasttype="__end" def characters(self, data ): istext=0 if not isopml and (self.lasttype=="__data" or self.lasttype=="xsl:text" or self.lasttype=="fo:block"): data=string.replace(data,'\n','&nl;') istext=1 self.lasttype="__data" if istext or string.strip(data): data = data.encode('ISO-8859-1','replace') id=self.tree.insert_node(self.nodeStack[-1],None, [data],is_leaf=1) self.tree.node_set_row_data(id,ElementWrapper('__data')) class TestEntityResolver: def resolveEntity(self, publicId, systemId): from xml.sax.xmlreader import InputSource inpsrc = InputSource("/usr/share/buzz/entity.xml") return inpsrc class XMLTreeCtrl: def __init__(self,filename): global attrstyle global datastyle global nodestyle global leafstyle self.tree = GtkCTree(1,0,[""]) self.tree.column_titles_hide() self.tree.set_selection_mode(1) self.casesensitive=0 self.lastdelete="" self.lastfile="" self.modified=0 self.lastdeletewrapper="" self.lastfoundroot="" self.currentfindstr="" self.currentfind=0 self.findcount=1 self.filename=filename (self.cwd,foo)=os.path.split(filename) self.tree.set_expander_style(CTREE_EXPANDER_CIRCULAR) self.tree.set_line_style(CTREE_LINES_DOTTED) self.tree.set_indent(4) self.tree.set_column_auto_resize(0,1) self.tree.root=None self.scrolledright=0 self.tree.connect('key-press-event',self.key_pressed) self.tree.connect('button-press-event',self.button_pressed) self.nodeStack = [self.tree.root] attrstyle=self.tree.get_style().copy() datastyle=self.tree.get_style().copy() nodestyle=self.tree.get_style().copy() leafstyle=self.tree.get_style().copy() f = load_font('-*-lucida-bold-i-normal-*-12-*-*-*-p-*-*-*') datastyle.font=f f = load_font('-*-lucida-bold-r-normal-*-12-*-*-*-p-*-*-*') nodestyle.font=f f = load_font('-*-lucida-medium-i-normal-*-12-*-*-*-p-*-*-*') attrstyle.font=f f = load_font('-*-lucida-medium-r-normal-*-12-*-*-*-p-*-*-*') leafstyle.font=f def LoadTree(self, filename): global isopml from xml.sax import make_parser if not os.path.exists(filename): print("File %s doesn't exist"%filename) return "" filesplit=filename.split('.') topwindow.set_title(filename) filetype=filesplit[len(filesplit)-1] isopml=0 if (filetype.lower() == "opml"): isopml=1 Parser = make_parser() DocHandler=GetElements() DocHandler.setTree(self.tree,self.nodeStack) Parser.setErrorHandler(ErrorHandler()) Parser.setContentHandler(DocHandler) Parser.setEntityResolver(TestEntityResolver()) ParserStatus = Parser.parse(filename) del Parser return filetype def button_pressed(self,tree,event): if self.tree.selection: selection=self.tree.selection[0] else: selection=None if event.type==GDK._2BUTTON_PRESS: if self.modified: self.message("File was Modified") return (url,a,b,c,d,e,f,g)=self.tree.get_node_info(selection) if url[0:4] != "url=": for kid in selection.children: (url,a,b,c,d,e,f,g)=self.tree.get_node_info(kid) if url[0:4] == "url=": selection=kid break elif url[0:4] == "xlin": selection=kid break urloffset=url.find('=')+1 if url[0:4] == "xlin": return self.FollowXlink(url[urloffset:]) elif urloffset: url=url[urloffset:] (foo,extension)=os.path.splitext(url) (self.cwd,foo)=os.path.split(self.filename) if (extension == ".opml" or extension == ".OPML"): filename=os.path.join(self.cwd,url) self.file_open(filename) return else: if (url.find(":") == -1) and not os.path.exists(url): url="FILE:"+os.path.join(self.cwd,url) return os.system("dillo "+url+' &') elif selection: self.tree.toggle_expansion(selection) self.tree.emit_stop_by_name('button-press-event') def key_pressed(self,tree,event): global homefile global attrstyle global datastyle global window global bracket_open if self.tree.selection: selection=self.tree.selection[0] else: selection=None if event.keyval==GDK.t or event.keyval==GDK.T: if selection: self.tree.toggle_expansion_recursive(selection) self.tree.emit_stop_by_name('key-press-event') if event.keyval == 269025049: if selection: if not self.scrolledright: self.scrolledright=0.1 elif self.scrolledright < 1.0: self.scrolledright=self.scrolledright+0.1 self.tree.node_moveto(selection,0,0.5,self.scrolledright) self.tree.emit_stop_by_name('key-press-event') if event.keyval==GDK.v or event.keyval==GDK.V or event.keyval == 2809: if selection: self.scrolledright=0 self.tree.node_moveto(selection,0,0.5,0.0) self.tree.emit_stop_by_name('key-press-event') if event.keyval==GDK.z or event.keyval==GDK.Z: if selection: self.tree.expand(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.e or event.keyval==GDK.E: if selection: self.edit_item(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.o or event.keyval==GDK.O or event.keyval==GDK.Return: if selection: if selection.sibling: sib=selection.sibling else: sib=None selection = self.tree.insert_node(selection.parent,sib,[''],is_leaf=0) self.tree.node_set_row_data(selection,ElementWrapper("__outline")) self.tree.select(selection) self.edit_item(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.c or event.keyval==GDK.C: if selection: sib=None selection = self.tree.insert_node(selection,sib,[''],is_leaf=0) self.tree.node_set_row_data(selection,ElementWrapper("__outline")) self.tree.select(selection) self.edit_item(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.period: if selection: sib=None selection = self.tree.insert_node(selection,sib,[''],is_leaf=0) self.tree.node_set_row_style(selection,datastyle) self.tree.node_set_row_data(selection,ElementWrapper("__data")) self.tree.select(selection) self.edit_item(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.at: if selection: sib=None selection = self.tree.insert_node(selection,sib,['attribute=value'],is_leaf=1) self.tree.node_set_row_style(selection,attrstyle) self.tree.node_set_row_data(selection,ElementWrapper("__data")) self.tree.select(selection) self.edit_item(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.p or event.keyval==GDK.P: if selection and self.lastdelete: if selection.sibling: sib=selection.sibling else: sib=None selection = self.tree.insert_node(selection.parent,sib,[self.lastdelete],is_leaf=0) self.tree.node_set_row_data(selection,self.lastdeletewrapper) self.tree.select(selection) elif event.keyval==GDK.u or event.keyval==GDK.U: if selection: self.modified=1 prev=self.prev(selection.parent,selection) if prev: selection = self.tree.move(selection,selection.parent,prev) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.d or event.keyval==GDK.D: if selection: nextsib= selection.sibling.sibling selection = self.tree.move(selection,selection.parent,nextsib) self.modified=1 self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.l or event.keyval==GDK.L: if selection.parent.parent: nextparent=selection.parent.sibling selection = self.tree.move(selection,selection.parent.parent,nextparent) self.modified=1 self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.i or event.keyval==GDK.I or event.keyval==GDK.N or event.keyval==GDK.n: self.repeat_find() self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.r or event.keyval==GDK.R: if selection: childprev=self.prev(selection.parent,selection) if childprev: selection = self.tree.move(selection,childprev,None) self.modified=1 self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.x or event.keyval==GDK.X: if selection: (self.lastdelete,a,b,c,d,e,f,g)=self.tree.get_node_info(selection) self.lastdeletewrapper=self.tree.node_get_row_data(selection) self.tree.remove_node(selection) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.S: self.file_save(self.filename) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK._4: if selection: self.tree.expand_to_depth(selection,4) elif event.keyval==GDK.A: self.file_selector=GtkFileSelection("Choose a file name") self.file_selector.set_usize(200,200) self.file_selector.ok_button.connect("clicked",self.save_as) self.file_selector.show_all() self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.f or event.keyval==GDK.F: self.tree.finding=self.tree.root self.tree.findwindow=GtkWindow(WINDOW_TOPLEVEL,'Find') self.tree.finder=GtkEntry() self.tree.findwindow.add(self.tree.finder) self.tree.finder.show_all() self.tree.finder.set_text(self.currentfindstr) self.tree.finder.grab_focus() self.tree.findwindow.set_modal(1) self.tree.findwindow.set_usize(180,20) self.tree.findwindow.show_all() self.connect_id = self.tree.finder.connect('activate',self.find_item) self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.m or event.keyval==GDK.M: self.modified=0 self.message("Modified status Cleared!") self.tree.emit_stop_by_name('key-press-event') elif event.keyval==GDK.H: self.Highlight(self.tree.root,0) elif event.keyval==GDK.h: if self.modified: self.message("File was Modified!") return self.file_open(homefile) elif event.keyval==GDK.b or event.keyval==GDK.B: if self.modified: self.message("File was Modified!") return if self.lastfile and os.path.isfile(self.lastfile): self.file_open(self.lastfile) elif event.keyval == GDK.space or event.keyval == GDK.slash or event.keyval == 65421: if self.modified: self.message("File was Modified") return (url,a,b,c,d,e,f,g)=self.tree.get_node_info(selection) if url[0:4] != "url=": for kid in selection.children: (url,a,b,c,d,e,f,g)=self.tree.get_node_info(kid) if url[0:4] == "url=": selection=kid break elif url[0:4] == "xlin": selection=kid break urloffset=url.find('=')+1 if url[0:4] == "xlin": return self.FollowXlink(url[urloffset:]) elif urloffset: url=url[urloffset:] (foo,extension)=os.path.splitext(url) (self.cwd,foo)=os.path.split(self.filename) if (extension == ".opml" or extension == ".OPML"): filename=os.path.join(self.cwd,url) self.file_open(filename) return else: if (url.find(":") == -1) and not os.path.exists(url): url="FILE:"+os.path.join(self.cwd,url) return os.system("dillo "+url+' &') elif event.keyval==GDK.space: if selection: self.tree.toggle_expansion(selection) self.tree.emit_stop_by_name('key-press-event') else: self.file_selector=GtkFileSelection("Choose a file name") self.file_selector.set_usize(200,200) self.file_selector.ok_button.connect("clicked",self.open_file) self.file_selector.cancel_button.connect("clicked",self.cancel_file) self.file_selector.show_all() self.tree.emit_stop_by_name('key-press-event') def message(self,message): self.notewindow=GtkWindow(WINDOW_TOPLEVEL,message) button=GtkButton(message) self.notewindow.add(button) button.grab_focus() button.connect("clicked",self.message_done) self.notewindow.set_modal(1) self.notewindow.show_all() def file_open(self,filename): if os.path.isfile(filename): self.lastfile=self.filename self.filename=filename if self.tree.root: self.tree.remove_node(self.tree.root) self.tree.show_now() mainiteration(FALSE) self.tree.root=None self.nodeStack = [self.tree.root] self.tree.freeze() self.LoadTree(self.filename) #xml.tree.set_column_title(0,self.filename) self.expand_body() self.Highlight(self.tree.root,0) self.tree.thaw() def file_save(self,filename): global bracket_open bracket_open=0 try: outfile=open(filename,'w') outfile.write("") self.SaveModel(self.tree.root,outfile,0) self.modified=0 self.message("Saved!") except: self.message("NOT Saved") def open_file(self,event): filename=self.file_selector.get_filename() self.file_selector.destroy() if os.path.isfile(filename): self.file_open(filename) else: self.message("Not a file!") def cancel_file(self,event): self.file_selector.destroy() def edit_item(self,selection): self.expand_to_root(selection) self.tree.node_moveto(selection,0,0.3,0.0) (text,a,b,c,d,e,f,g)=self.tree.get_node_info(selection) self.tree.editwindow=GtkWindow(WINDOW_TOPLEVEL,'Enter Text') self.tree.editor=GtkEntry() self.tree.editwindow.add(self.tree.editor) self.tree.editor.show_all() self.tree.editor.set_text(text) self.tree.editor.grab_focus() self.tree.editwindow.set_modal(1) self.editting=selection self.tree.editwindow.set_usize(180,20) self.tree.editwindow.show_all() self.connect_id = self.tree.editor.connect('activate',self.item_editted) def item_editted(self,event): text = self.tree.editor.get_chars(0,-1) self.tree.node_set_text(self.editting,0,text) self.modified=1 self.tree.editwindow.destroy() def message_done(self,event): self.notewindow.destroy() def save_as(self,event): filename=self.file_selector.get_filename() self.file_selector.destroy() if filename: self.file_save(filename) def prev(self,parent,child): if not parent: return None prevchild=None for kid in parent.children: if kid==child: return prevchild prevchild=kid return prevchild def repeat_find(self): if self.currentfindstr and self.lastfoundroot: findroot=self.lastfoundroot self.findcount=self.findcount+1 self.currentfind=0 founditem=self.FindInModel(findroot,self.currentfindstr,0,self.casesensitive) if founditem: self.tree.select(founditem) self.expand_to_root(founditem) self.tree.node_moveto(founditem,0,0.0,0.0) self.lastfoundroot=findroot else: (rootlabel,a,b,c,d,e,f,g)=self.tree.get_node_info(findroot) self.notewindow=GtkWindow(WINDOW_TOPLEVEL,'Not Found') button=GtkButton("Not Found") self.notewindow.add(button) button.grab_focus() button.connect("clicked",self.message_done) self.notewindow.set_modal(1) self.notewindow.show_all() self.lastfoundroot=None self.findcount=1 def find_item(self,event): text = self.tree.finder.get_chars(0,-1) if text: findstr=text if not self.casesensitive: findstr=findstr.lower() if self.currentfindstr==findstr and self.lastfoundroot: findroot=self.lastfoundroot self.findcount=self.findcount+1 else: findroot=self.tree.finding self.findcount=1 self.currentfindstr=findstr self.currentfind=0 founditem=self.FindInModel(findroot,self.currentfindstr,0,self.casesensitive) if founditem: self.tree.select(founditem) self.expand_to_root(founditem) self.tree.node_moveto(founditem,0,0.0,0.0) self.lastfoundroot=findroot else: (rootlabel,a,b,c,d,e,f,g)=self.tree.get_node_info(findroot) self.notewindow=GtkWindow(WINDOW_TOPLEVEL,'Not Found') button=GtkButton("Not Found") self.notewindow.add(button) button.grab_focus() button.connect("clicked",self.message_done) self.notewindow.set_modal(1) self.notewindow.show_all() self.lastfoundroot=None self.findcount=1 self.tree.findwindow.destroy() def SaveModel(self,item,outfile,depth): global bracket_open data_node=self.tree.node_get_row_data(item) (newvalue,a,b,c,d,e,f,g)=self.tree.get_node_info(item) data_node.startsave(outfile,newvalue) cookie=depth for kid in item.children: self.SaveModel(kid,outfile,depth+1) data_node.endsave(outfile,newvalue) def Highlight(self,item,depth): global nodestyle global leafstyle global datastyle global attrstyle cookie=depth if item.children: self.tree.node_set_row_style(item,nodestyle) else: ew=self.tree.node_get_row_data(item) if ew.EltType == "__data": self.tree.node_set_row_style(item,datastyle) elif ew.EltType == "__attr": self.tree.node_set_row_style(item,attrstyle) else: self.tree.node_set_row_style(item,leafstyle) return for kid in item.children: self.Highlight(kid,depth+1) def expand_to_root(self,item): self.tree.expand(item) if item.parent: self.expand_to_root(item.parent) def expand_body(self): self.tree.expand(self.tree.root) if self.tree.root.children: self.tree.expand(self.tree.root.children[-1]) def FindInModel(self,item,needle,depth,casesensitive=0): (newvalue,a,b,c,d,e,f,g)=self.tree.get_node_info(item) cookie=depth if not casesensitive: found=newvalue.lower().find(needle) else: found=newvalue.find(needle) if found != - 1: self.currentfind=self.currentfind+1 if self.currentfind==self.findcount: return(item) for kid in item.children: found=self.FindInModel(kid,needle,cookie,casesensitive) if found: return(found) return(None) def FollowXlink(self,xlink): xlinkarray=xlink.split('#') url=xlinkarray[0] xpath=xlinkarray[1] filename=url if self.modified: self.message("File was Modified!") return if filename.find("http:") !=-1: filename,msg=urllib.urlretrieve(url) if filename.find("cloud:") !=-1: #when cloud fully implemented will mean that cloud from head will define url # for now only case of same directory supported cloudarray=filename.split(':') filename=os.path.join(self.cwd,cloudarray[1]) if not os.path.exists(filename): filename=os.path.join(self.cwd,filename) if filename and os.path.exists(filename): if filename!=self.filename: self.file_open(filename) curritem=self.Xpath(self.tree.root,xpath) if curritem: self.tree.select(curritem) self.expand_to_root(curritem) self.tree.node_moveto(curritem,0,0.0,0.0) return filename else: self.message("Target doesn't exist") return None def Xpath(self,root,xpath): if len(xpath) and xpath[:12]=='//*[@buzzid=': self.lastfoundroot=None self.findcount=1 oldfindstr=self.currentfindstr self.currentfindstr='buzzid='+xpath[13:-2] self.currentfind=0 targetattr=self.FindInModel(self.tree.root,'buzzid='+xpath[13:-2],0) self.currentfindstr=oldfindstr if targetattr: return(targetattr.parent) else: return None topwindow=GtkWindow(WINDOW_TOPLEVEL,'Buzz Lite') topwindow.set_default_size(235,235) topwindow.connect('delete_event', delete_event) xml=XMLTreeCtrl("") sw = GtkScrolledWindow() sw.add(xml.tree) sw.set_policy(POLICY_AUTOMATIC,POLICY_AUTOMATIC) if len(sys.argv) > 1 and os.path.exists(sys.argv[1]): xml.filename=sys.argv[1] homefile=sys.argv[1] topwindow.add(sw) xml.file_open(sys.argv[1]) topwindow.show_all() mainloop() else: print "Could not open"