Re: PDesk - in progress: MRU, date stamping, shortcuts, CSV plugin, file info
"Jeff Mikels" <[email protected]> Tue, 15 Aug 2006 15:24:01 -0400
| Newsgroups | gmane.comp.handhelds.palm.progect |
|---|---|
| Message-ID | <[email protected]> |
I'd like to see a two way opml plugin. I'll send you what I've got so far. On 8/15/06, Tomas <[email protected]> wrote: > I plan to work a bit on PDesk: > > 1) MRU (most recently used files) functionality known from most > Windows programs that work with files > 2) date stamping > 3) slightly improving the keyboard operations (new shortcuts) > 4) fix the CSV plugin and Treepad plugin, they shall allow for both > import and export > > Considering: > 5) File information dialogue. Also standard feature of many Windows > apps. Not sure if useful and what it shall contain? > > No. 1-3 is 90% done and works on my PC. No 1 shall work on both > Windows and Linux (?), and store 10 last used files between sessions. > > If you have got requests re no. 2 or 3, or objections against any of > them, please let me know. > > I might also attempt to implement any other feature request you might > have, if easy to do, but I guess in 90% of such cases it will have to > wait for later time, or for someone else... > > Tomas > > > > > > > > Remember to visit Polls, Files and Bookmarks sections at http://groups.yahoo.com/group/progect. You are welcome to vote, upload your files and submit bookmarks. > Yahoo! Groups Links > > > > > > > > -- Jeff Mikels leading people one step closer to Jesus http://jeff.mikels.cc http://thesouthsidechurch.org ---------- #one !/usr/bin/env python __author__ = "Jeff Mikels <[email protected]>" __version__ = "$Revision: 0.1 $" __date__ = "2006-02-10" from PDeskPlugin import PDeskPlugin from Db import * from RecordFactory import * from string import * import re import sys from wxPython.wx import * class OPMLInOut(PDeskPlugin): """ Import a text file to a project. @author Jeff Mikels <[email protected]> @since 0.1 @version $Revision: 0.1 $ """ def __init__(self, db, tree): """ Constructor. @param Db db : Database to read @param TreePlusNode tree : root of the tree @since 1.0 """ PDeskPlugin.__init__(self, db, tree) self.__exportDone = 0 self.__exportDate = 0 self.__exportPriority = 0 self.__exportProgress = 0 self.__exportNote = 0 self.__importType = 'info' self.__importOverwrite = 0 self.oldTree = db self.logTree(db) def logTree(self,db): self.log('current tree looks like this\n----------------------------') for item in db.db: self.log("\t" * item.getLevel() + item.description) self.log('------------------------------') def log(self, a): return f = open('c:\\trash\\opml.log','a') f.write("%s\n" % a) f.close() def getPluginSystemVersion(): """ Don't change this method. It's used internally to determine the version of the plugin system. @return int : Minimum plugin system version to use this plugin. """ return 1 # corresponding to the beta 3 def getName(): """ This method returns the name of the plugin. @return string @since 0.1 """ return "OPML Import/Export" def getAuthor(): """ This method returns the author of the plugin. @return string @since 0.1 """ return "Jeff Mikels <[email protected]>" def getVersion(): """ This method returns the version of the plugin. @return string @since 0.1 """ return "0.1" def getInputFormat(): """ Return a specification tupple. @return tupple @since 0.1 """ # return None if this plugin can't read. # otherwise, return a tupple with # - name of the input format # - extension of the input format # - textual description of the plugin input format # example : return ("Text", "txt", "Tabbed text...") return ("OPML", "opml", "Outline Markup Format") def getOutputFormat(): """ Return a specification tupple. @return tupple @since 0.1 """ # return None if this plugin can't write. # otherwise, return a tupple with # - name of the output format # - extension of the output format # - textual description of the plugin output format # example : return ("Text", "txt", "Tabbed text...") return ("OPML", "opml", "Outline Markup Format") # make these methods static getName = staticmethod(getName) getAuthor = staticmethod(getAuthor) getVersion = staticmethod(getVersion) getInputFormat = staticmethod(getInputFormat) getOutputFormat = staticmethod(getOutputFormat) getPluginSystemVersion = staticmethod(getPluginSystemVersion) def readPrefs(self): """ Open a preferences dialog for import. @return boolean : true if ok, false if the user hit cancel @since 1.0 """ dlg = wxDialog(None, -1, _("OPML Import Preferences")) sizer = wxBoxSizer(wxVERTICAL) text = wxStaticText(dlg, -1, "Select tasks type:") sizer.Add(text) sampleList = ['info (default)', 'action', 'progress', 'numeric', 'link'] ch = wxChoice(dlg, 40, (80, 50), choices = sampleList) sizer.Add(ch) #text2 = wxStaticText(dlg, -1, "Select properties to import:") #sizer.Add(text2) chkOverwrite = wxCheckBox(dlg, -1, _("Overwrite Current Tree")) sizer.Add(chkOverwrite) def overwriteClick(event): self.__importOverwrite = event.IsChecked() def choiceClick(event): self.__importType = event.GetString() EVT_CHECKBOX(dlg, chkOverwrite.GetId() , overwriteClick) EVT_CHOICE(dlg, ch.GetId() , choiceClick) hs = wxBoxSizer(wxHORIZONTAL) hs.Add(wxButton(dlg, wxID_OK, _("OK"))) hs.Add(wxButton(dlg, wxID_CANCEL, _("Cancel"))) sizer.Add(hs) dlg.SetAutoLayout(true) dlg.SetSizer(sizer) sizer.Fit(dlg) sizer.SetSizeHints(dlg) ans = dlg.ShowModal() dlg.Destroy() return ans == wxID_OK def writePrefs(self): """ Open a preferences dialog for export. @return boolean : true if ok, false if the user hit cancel @since 1.6 """ dlg = wxDialog(None, -1, _("Text Export Preferences")) sizer = wxBoxSizer(wxVERTICAL) chkDone = wxCheckBox(dlg, -1, _("Export done tasks")) sizer.Add(chkDone) chkDate = wxCheckBox(dlg, -1, _("Export due dates")) sizer.Add(chkDate) chkPriority = wxCheckBox(dlg, -1, _("Export priorities")) sizer.Add(chkPriority) chkProgress = wxCheckBox(dlg, -1, _("Export progress")) sizer.Add(chkProgress) chkNotes = wxCheckBox(dlg, -1, _("Export notes")) sizer.Add(chkNotes) def doneClick(event): self.__exportDone = event.IsChecked() def dateClick(event): self.__exportDate = event.IsChecked() def prioClick(event): self.__exportPriority = event.IsChecked() def progClick(event): self.__exportProgress = event.IsChecked() def noteClick(event): self.__exportNote = event.IsChecked() EVT_CHECKBOX(dlg, chkDone.GetId() , doneClick) EVT_CHECKBOX(dlg, chkDate.GetId() , dateClick) EVT_CHECKBOX(dlg, chkPriority.GetId(), prioClick) EVT_CHECKBOX(dlg, chkProgress.GetId(), progClick) EVT_CHECKBOX(dlg, chkNotes.GetId() , noteClick) hs = wxBoxSizer(wxHORIZONTAL) hs.Add(wxButton(dlg, wxID_OK, _("OK"))) hs.Add(wxButton(dlg, wxID_CANCEL, _("Cancel"))) sizer.Add(hs) dlg.SetAutoLayout(true) dlg.SetSizer(sizer) sizer.Fit(dlg) sizer.SetSizeHints(dlg) ans = dlg.ShowModal() dlg.Destroy() return ans == wxID_OK def read(self, file, db): """ Read the db from filename (indented text file). @param File file : from where to read @param Db db : the database to fill @since 1.0 """ if self.__importOverwrite == 0: db.db[0] = self.oldTree.db[0] db.db.extend(self.oldTree.db[1:]) self.logTree(db) lastElement = len(db.db) - 1 self.log(lastElement) self.log('reading OPML file...') opml = OPML() opml.read(file) data = opml.outline self.log('got data') if len(data) > 0: if lastElement == 0: db.db[lastElement].attr.hasChild = 1 else: db.db[lastElement].attr.hasNext = 1 else: return baseLevel = db.db[lastElement].getLevel() for i in range(len(data)): self.log("parsing outline element %s" % i ) item = data[i] for key in item: self.log("%s:%s" % (key, item[key])) desc = item['text'] level = item['level'] progress = 0 note = '' for key in item: if key.strip() == 'text': continue elif key.strip() == 'level': continue elif key.strip() == '_status': if item[key] == 'checked': progress = 10 else: note += "%s: %s\n" % (key.strip(), item[key]) itemType = '' if self.__importType == "progress": #print 'progress' itemType=(ItemType.progressType) elif self.__importType == "action": #print 'action' itemType=(ItemType.actionType) elif self.__importType == "numeric": #print 'numeric' itemType=(ItemType.numericType) elif self.__importType == "info (default)": #print 'info' itemType=(ItemType.informativeType) elif self.__importType == "link": #print 'link' itemType=(ItemType.linkType) else: #print 'info' itemType=(ItemType.informativeType) rec = rf.getRecord(db, description=desc) rec.setType(itemType) rec.setProgress(progress) rec.setLevel(level) self.log('setting level to %s (baselevel is %s)' % (level,baseLevel)) rec.note = note attr = rec.getAttr() # find child attr.hasChild = i < len(data) - 1 and data[i+1]['level'] == level + 1 # find previous if it exists j = i - 1 while j > 0: if data[j]['level'] == level: attr.hasPrev = 1 break elif data[j]['level'] < level: attr.hasPrev = 0 break else: j -= 1 else: if lastElement == 0: attr.hasPrev = 0 else: attr.hasPrev = 1 j = i + 1 # find next if it exists while j < len(data): if data[j]['level'] == level: attr.hasNext = 1 break elif data[j]['level'] < level: attr.hasNext = 0 break else: j += 1 else: attr.hasNext = 0 self.log('hasChild: %s' % attr.hasChild) self.log('hasPrev: %s' % attr.hasPrev) self.log('hasNext: %s' % attr.hasNext) self.log('level: %s' % rec.getLevel()) self.log('appending...') db.db.append(rec) f = open('c:\\trash\\opml.out','w') self.rawwrite(f,db) f.close() def rawwrite(self,f,db): for item in db.db: tmpdata = [] desc = item.description level = item.getLevel() attr = item.getAttr() tmpdata.append("level: %d" % level) tmpdata.append("hasChild: %d" % attr.hasChild) tmpdata.append("hasPrev: %d" % attr.hasPrev) tmpdata.append("hasNext: %d" % attr.hasNext) tmpdata.append("NOTE: %s" % item.note) f.write('\t' * level + desc + '\n') for line in tmpdata: f.write('\t' * (level + 1) + line + '\n') def write(self, file, db, tree): """ Write the db to filename. @since 0.1 """ self.printTree(tree, file) #follow http://opml.scripting.com/spec def printTree(self, node, file=sys.stdout, indent=0): while node != None: if indent == 0: #xml prefix file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") #opml prefix file.write("<opml version=\"1.0\">\n<head>\n<title>%s</title>\n" % node.data.description) # <dateCreated> is a date-time, indicating when the document was created file.write("<dateCreated>xxx</dateCreated>\n") # <dateModified> is a date-time, indicating when the document was last modified. file.write("<dateModified>xxx</dateModified>\n") # <ownerName> is a string, the owner of the document. file.write("<ownerName>Pdesk</ownerName>\n") # <ownerEmail> is a string, the email address of the owner of the document. file.write("<ownerEmail></ownerEmail>\n") # The line numbers in the list tell you which headlines to expand # I am not sure what this means file.write("<expansionState></expansionState>\n") #file.write("<vertScrollState>1</vertScrollState>\n") (no use here) #<windowTop>317</windowTop> #<windowLeft>252</windowLeft> #<windowBottom>514</windowBottom> #<windowRight>634</windowRight> #head footer, A <body> contains one or more <outline> elements. file.write("</head>\n<body>\n") elif indent > 0: mixstring = node.data.description #separate the paragraphs & change mixed char <,>,& mixstring = mixstring.replace("&", "&") mixstring = mixstring.replace("<", "<") mixstring = mixstring.replace(">", ">") mixstring = mixstring.replace("\"", """) file.write("\t<outline text=\"%s\" " % mixstring) # get indent file.write("Indent=\"%s\" "% indent) # get type file.write("Type=\"%s\" "% node.data.getType()) # show priority if node.data.hasPriority()== 1: file.write("Priority=\"%s\" "% node.data.getPriority()) #show progress if node.data.getProgress()!=0: file.write("Progress=\"%s\" "% node.data.getProgress()) #show Duedate if node.data.hasDueDate()==1: file.write("DueDate=\"%s\" "% node.data.dueDate) #show note if node.data.hasNote()== 1: string = node.data.note #separate the paragraphs & change mixed char <,>,&. string = string.replace("&", "&") string = string.replace("<", "<") string = string.replace(">", ">") string = string.replace("\"", """) #string = string.replace("\n", "</para><para>") file.write("Note=\"%s\" "% string) if node.data.hasChild()== 1: child = 1 file.write(">\n") else: child = 0 file.write("/>\n") self.printTree(node.child, file, indent+1) node = node.next if indent == 0: #change the foot message here file.write("</body>\n</opml>") elif indent > 0 : if child == 1: file.write("\t</outline>\n") class dummy: def printTree(self, node, file=sys.stdout, indent=0): """ DOESN'T WORK YET Print this node and it's children to file. @param TreePlusNode node : node to print @param File file : where to print @param int indent : actual indent level @since 1.0 """ while node != None: data = node.data if data.getProgress() == 10 and not self.__exportDone: node = node.next continue line = ("\t" * indent) line += node.data.description if self.__exportProgress: if data.getType() == ItemType.actionType: if data.getProgress() == 0: line += " [ ]" else: line += " [*]" elif data.getType() == ItemType.progressType or \ data.getType() == ItemType.numericType: line += " [%d%%]" % (data.getProgress() * 10) elif data.getType() == ItemType.informativeType: pass if self.__exportPriority: prio = data.getPriority() if prio != priority.NO: line += " <%d>" % (prio) if self.__exportDate and data.hasDueDate(): line += " (%s)" % (str(data.dueDate)) line += "\n" if self.__exportNote and data.hasNote(): for l in data.note.split("\n"): line += ("\t" * (indent + 1)) + l + "\n" file.write(line) self.printTree(node.child, file, indent+1) node = node.next def write(self, file, db, tree): """ Write the db to filename. @param File file : where to write @param Db db : the database to export @param TreePlusNode tree : root of the tree @since 1.0 """ self.printTree(tree.child, file) class OPML: def __init__(self): self.outline = [] self.places = [] def read(self, f): flatflag = 0 raw = f.read() findBody = re.compile(r'<body>(.*)</body>',re.DOTALL) body = findBody.findall(raw)[0] self.outline = self.body2tree(body) #pprint(self.outline) def body2tree(self, body, level = 1): '''if the level is set to 1, then will return a "flat" list with level indicators ''' #get all outline tags outline = re.compile(r'(<outline (.*?)>)|(</outline>)',re.DOTALL) found = outline.findall(body) #pprint (found) tree = [] for item in found: isClosed = 0 if item[2] != '': level = level -1 continue attrs = item[1] if attrs[-1] == '/': isClosed = 1 attrs = attrs[:-1] node = self.getAttributes(attrs) node['level'] = level tree.append(node) if isClosed == 0: level = level + 1 return tree def getAttributes(self,attributes): retVal = {} find_attributes = re.compile(r'(.*?)="([^"]*|"")"',re.DOTALL) attrs = find_attributes.findall(attributes) for item in attrs: retVal[item[0].strip()] = self.unxmlify(item[1].strip()) return retVal def _print(self,node,level=0): print "%s%s" % ("\t"*level, node.name) for key, value in node.attrs.iteritems(): print "%s%s:%s" % ("\t"*level, key[1], value) for item in node.children: self._print(item,level+1) def write(self, f): towrite = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n' towrite = towrite + '<life-balance-exchange xmlns="http://www.llamagraphics.com/life-balance-exchange" xmlns:xcal="http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt">\n\n' def fixUnicode(self,string): try: return string.encode('ASCII') except UnicodeEncodeError: s = '' for char in string: try: s = s + char.encode('ASCII') except UnicodeEncodeError: s = s + '--' return s except: return string def unxmlify(self,text): mapping = [ (' ','&','<','>','"','&apos'), ('\n','&','<','>','"',"'") ] for i in range(len(mapping[0])): text = text.replace(mapping[0][i],mapping[1][i]) return text class oldOPML: def __init__(self): self.outline = [] self.places = [] def read(self, f): raw = f.read() findBody = re.compile(r'<body>(.*)</body>',re.DOTALL) body = findBody.findall(raw)[0] self.outline = self.body2tree(body) def body2tree(self, body, level = 1): '''if the level is set to 1, then will return a "flat" list with level indicators ''' # RETURNS A LIST OF NODES / DIRECTORIES #print body if body == '': if level == 0: return [] else: return tree = [] outline = re.compile(r'<outline ([^>]*?)/>|<outline (.*?)>(.*)</outline>',re.DOTALL) found = outline.findall(body) for item in found: #node = XMLNode() closedTagAttributes = item[0] openTagAttributes = item[1] openTagBody = item[2] if closedTagAttributes: attrs = closedTagAttributes else: attrs = openTagAttributes node = self.getAttributes(attrs) if level == 0: node['children'] = self.body2tree(openTagBody) tree.append(node) else: node['level'] = level tree.append(node) childrenData = self.body2tree(openTagBody, level + 1) if childrenData: tree.extend(childrenData) return tree def getAttributes(self,attributes): retVal = {} find_attributes = re.compile(r'(.*?)="([^"]*|"")"',re.DOTALL) attrs = find_attributes.findall(attributes) for item in attrs: retVal[item[0]] = item[1] return retVal def _print(self,node,level=0): print "%s%s" % ("\t"*level, node.name) for key, value in node.attrs.iteritems(): print "%s%s:%s" % ("\t"*level, key[1], value) for item in node.children: self._print(item,level+1) def write(self, f): towrite = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n' towrite = towrite + '<life-balance-exchange xmlns="http://www.llamagraphics.com/life-balance-exchange" xmlns:xcal="http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt">\n\n' def fixUnicode(self,string): try: return string.encode('ASCII') except UnicodeEncodeError: s = '' for char in string: try: s = s + char.encode('ASCII') except UnicodeEncodeError: s = s + '--' return s except: return string ---------- # # qp_xml: Quick Parsing for XML # # Written by Greg Stein. Public Domain. # No Copyright, no Rights Reserved, and no Warranties. # # This module is maintained by Greg and is available as part of the XML-SIG # distribution. This module and its changelog can be fetched at: # http://www.lyra.org/cgi-bin/viewcvs.cgi/xml/xml/utils/qp_xml.py # # Additional information can be found on Greg's Python page at: # http://www.lyra.org/greg/python/ # # This module was added to the XML-SIG distribution on February 14, 2000. # As part of that distribution, it falls under the XML distribution license. # import string #try: # import pyexpat #except ImportError: # from xml.parsers import pyexpat #except ImportError: from xml.parsers import xmlproc pyexpat = xmlproc error = __name__ + '.error' # # The parsing class. Instantiate and pass a string/file to .parse() # class Parser: def __init__(self): self.reset() def reset(self): self.root = None self.cur_elem = None def find_prefix(self, prefix): elem = self.cur_elem while elem: if elem.ns_scope.has_key(prefix): return elem.ns_scope[prefix] elem = elem.parent if prefix == '': return '' # empty URL for "no namespace" return None def process_prefix(self, name, use_default): idx = string.find(name, ':') if idx == -1: if use_default: return self.find_prefix(''), name return '', name # no namespace if string.lower(name[:3]) == 'xml': return '', name # name is reserved by XML. don't break out a NS. ns = self.find_prefix(name[:idx]) if ns is None: raise error, 'namespace prefix not found' return ns, name[idx+1:] def start(self, name, attrs): elem = _element(name=name, lang=None, parent=None, children=[], ns_scope={}, attrs={}, first_cdata='', following_cdata='') if self.cur_elem: elem.parent = self.cur_elem elem.parent.children.append(elem) self.cur_elem = elem else: self.cur_elem = self.root = elem work_attrs = [ ] # scan for namespace declarations (and xml:lang while we're at it) for name, value in attrs.items(): if name == 'xmlns': elem.ns_scope[''] = value elif name[:6] == 'xmlns:': elem.ns_scope[name[6:]] = value elif name == 'xml:lang': elem.lang = value else: work_attrs.append((name, value)) # inherit xml:lang from parent if elem.lang is None and elem.parent: elem.lang = elem.parent.lang # process prefix of the element name elem.ns, elem.name = self.process_prefix(elem.name, 1) # process attributes' namespace prefixes for name, value in work_attrs: elem.attrs[self.process_prefix(name, 0)] = value def end(self, name): parent = self.cur_elem.parent del self.cur_elem.ns_scope del self.cur_elem.parent self.cur_elem = parent def cdata(self, data): elem = self.cur_elem if elem.children: last = elem.children[-1] last.following_cdata = last.following_cdata + data else: elem.first_cdata = elem.first_cdata + data def parse(self, input): self.reset() p = pyexpat.ParserCreate() p.StartElementHandler = self.start p.EndElementHandler = self.end p.CharacterDataHandler = self.cdata try: if type(input) == type(''): p.Parse(input, 1) else: while 1: s = input.read(_BLOCKSIZE) if not s: p.Parse('', 1) break p.Parse(s, 0) finally: if self.root: _clean_tree(self.root) return self.root # # handy function for dumping a tree that is returned by Parser # def dump(f, root): f.write('<?xml version="1.0"?>\n') namespaces = _collect_ns(root) _dump_recurse(f, root, namespaces, dump_ns=1) f.write('\n') # # This function returns the element's CDATA. Note: this is not recursive -- # it only returns the CDATA immediately within the element, excluding the # CDATA in child elements. # def textof(elem): return elem.textof() ######################################################################### # # private stuff for qp_xml # _BLOCKSIZE = 16384 # chunk size for parsing input class _element: def __init__(self, **kw): self.__dict__.update(kw) def textof(self): '''Return the CDATA of this element. Note: this is not recursive -- it only returns the CDATA immediately within the element, excluding the CDATA in child elements. ''' s = self.first_cdata for child in self.children: s = s + child.following_cdata return s def find(self, name, ns=''): for elem in self.children: if elem.name == name and elem.ns == ns: return elem return None def _clean_tree(elem): elem.parent = None del elem.parent map(_clean_tree, elem.children) def _collect_recurse(elem, dict): dict[elem.ns] = None for ns, name in elem.attrs.keys(): dict[ns] = None for child in elem.children: _collect_recurse(child, dict) def _collect_ns(elem): "Collect all namespaces into a NAMESPACE -> PREFIX mapping." d = { '' : None } _collect_recurse(elem, d) del d[''] # make sure we don't pick up no-namespace entries keys = d.keys() for i in range(len(keys)): d[keys[i]] = i return d def _dump_recurse(f, elem, namespaces, lang=None, dump_ns=0): if elem.ns: f.write('<ns%d:%s' % (namespaces[elem.ns], elem.name)) else: f.write('<' + elem.name) for (ns, name), value in elem.attrs.items(): if ns: f.write(' ns%d:%s="%s"' % (namespaces[ns], name, value)) else: f.write(' %s="%s"' % (name, value)) if dump_ns: for ns, id in namespaces.items(): f.write(' xmlns:ns%d="%s"' % (id, ns)) if elem.lang != lang: f.write(' xml:lang="%s"' % elem.lang) if elem.children or elem.first_cdata: f.write('>' + elem.first_cdata) for child in elem.children: _dump_recurse(f, child, namespaces, elem.lang) f.write(child.following_cdata) if elem.ns: f.write('</ns%d:%s>' % (namespaces[elem.ns], elem.name)) else: f.write('</%s>' % elem.name) else: f.write('/>') [Non-text portions of this message have been removed] Remember to visit Polls, Files and Bookmarks sections at http://groups.yahoo.com/group/progect. You are welcome to vote, upload your files and submit bookmarks. Yahoo! Groups Links <*> To visit your group on the web, go to: http://groups.yahoo.com/group/progect/ <*> To unsubscribe from this group, send an email to: [email protected] <*> Your use of Yahoo! Groups is subject to: http://docs.yahoo.com/info/terms/