]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/samples/stxview/StructuredText/ClassicDocumentClass.py
Added some simple sample apps
[wxWidgets.git] / wxPython / samples / stxview / StructuredText / ClassicDocumentClass.py
diff --git a/wxPython/samples/stxview/StructuredText/ClassicDocumentClass.py b/wxPython/samples/stxview/StructuredText/ClassicDocumentClass.py
new file mode 100644 (file)
index 0000000..23b73d6
--- /dev/null
@@ -0,0 +1,689 @@
+##############################################################################
+# 
+# Zope Public License (ZPL) Version 1.0
+# -------------------------------------
+# 
+# Copyright (c) Digital Creations.  All rights reserved.
+# 
+# This license has been certified as Open Source(tm).
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+# 1. Redistributions in source code must retain the above copyright
+#    notice, this list of conditions, and the following disclaimer.
+# 
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions, and the following disclaimer in
+#    the documentation and/or other materials provided with the
+#    distribution.
+# 
+# 3. Digital Creations requests that attribution be given to Zope
+#    in any manner possible. Zope includes a "Powered by Zope"
+#    button that is installed by default. While it is not a license
+#    violation to remove this button, it is requested that the
+#    attribution remain. A significant investment has been put
+#    into Zope, and this effort will continue if the Zope community
+#    continues to grow. This is one way to assure that growth.
+# 
+# 4. All advertising materials and documentation mentioning
+#    features derived from or use of this software must display
+#    the following acknowledgement:
+# 
+#       "This product includes software developed by Digital Creations
+#       for use in the Z Object Publishing Environment
+#       (http://www.zope.org/)."
+# 
+#    In the event that the product being advertised includes an
+#    intact Zope distribution (with copyright and license included)
+#    then this clause is waived.
+# 
+# 5. Names associated with Zope or Digital Creations must not be used to
+#    endorse or promote products derived from this software without
+#    prior written permission from Digital Creations.
+# 
+# 6. Modified redistributions of any form whatsoever must retain
+#    the following acknowledgment:
+# 
+#       "This product includes software developed by Digital Creations
+#       for use in the Z Object Publishing Environment
+#       (http://www.zope.org/)."
+# 
+#    Intact (re-)distributions of any official Zope release do not
+#    require an external acknowledgement.
+# 
+# 7. Modifications are encouraged but must be packaged separately as
+#    patches to official Zope releases.  Distributions that do not
+#    clearly separate the patches from the original work must be clearly
+#    labeled as unofficial distributions.  Modifications which do not
+#    carry the name Zope may be packaged in any form, as long as they
+#    conform to all of the clauses above.
+# 
+# 
+# Disclaimer
+# 
+#    THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
+#    EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+#    PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
+#    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+#    USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+#    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+#    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+#    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#    SUCH DAMAGE.
+# 
+# 
+# This software consists of contributions made by Digital Creations and
+# many individuals on behalf of Digital Creations.  Specific
+# attributions are listed in the accompanying credits file.
+# 
+##############################################################################
+
+import re, ST, STDOM
+from string import split, join, replace, expandtabs, strip, find
+
+StringType=type('')
+ListType=type([])
+
+class StructuredTextExample(ST.StructuredTextParagraph):
+    """Represents a section of document with literal text, as for examples"""
+
+    def __init__(self, subs, **kw):
+       t=[]; a=t.append
+       for s in subs: a(s.getNodeValue())
+       apply(ST.StructuredTextParagraph.__init__,
+             (self, join(t,'\n\n'), ()),
+             kw)
+
+    def getColorizableTexts(self): return ()
+    def setColorizableTexts(self, src): pass # never color examples
+
+class StructuredTextBullet(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+
+class StructuredTextNumbered(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+
+class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+
+class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+
+class StructuredTextDescription(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+    
+    def __init__(self, title, src, subs, **kw):
+       apply(ST.StructuredTextParagraph.__init__, (self, src, subs), kw)
+       self._title=title
+
+    def getColorizableTexts(self): return self._title, self._src
+    def setColorizableTexts(self, src): self._title, self._src = src
+
+    def getChildren(self):
+       return (StructuredTextDescriptionTitle(self._title),
+               StructuredTextDescriptionBody(self._src, self._subs))
+
+class StructuredTextSectionTitle(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+
+class StructuredTextSection(ST.StructuredTextParagraph):
+    """Represents a section of a document with a title and a body"""
+    def __init__(self, src, subs=None, **kw):
+       apply(ST.StructuredTextParagraph.__init__,
+             (self, StructuredTextSectionTitle(src), subs),
+             kw)
+
+# a StructuredTextTable holds StructuredTextRows
+class StructuredTextTable(ST.StructuredTextDocument):
+    """
+    rows is a list of lists containing tuples, which
+    represent the columns/cells in each rows.
+    EX
+    rows = [[('row 1:column1',1)],[('row2:column1',1)]]
+    """
+    
+    def __init__(self, rows, src, subs, **kw):
+        apply(ST.StructuredTextDocument.__init__,(self,subs),kw)
+        self._rows = []
+        for row in rows:
+            if row:
+                self._rows.append(StructuredTextRow(row,kw))
+    
+    def getRows(self):
+        return [self._rows]
+    
+    def _getRows(self):
+        return self.getRows()
+    
+    def getColorizableTexts(self):
+        """
+        return a tuple where each item is a column/cell's
+        contents. The tuple, result, will be of this format.
+        ("r1 col1", "r1=col2", "r2 col1", "r2 col2")
+        """
+        
+        #result = ()
+        result = []
+        for row in self._rows:
+            for column in row.getColumns()[0]:
+                #result = result[:] + (column.getColorizableTexts(),)
+                result.append(column.getColorizableTexts()[0])
+        return result
+    
+    def setColorizableTexts(self,texts):
+        """
+        texts is going to a tuple where each item is the
+        result of being mapped to the colortext function.
+        Need to insert the results appropriately into the
+        individual columns/cells
+        """
+        for row_index in range(len(self._rows)):
+            for column_index in range(len(self._rows[row_index]._columns)):
+                self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
+                texts = texts[1:]
+        
+    def _getColorizableTexts(self):
+        return self.getColorizableTexts()
+    
+    def _setColorizableTexts(self):
+        return self.setColorizableTexts()
+    
+# StructuredTextRow holds StructuredTextColumns
+class StructuredTextRow(ST.StructuredTextDocument):
+    
+    def __init__(self,row,kw):
+        """
+        row is a list of tuples, where each tuple is
+        the raw text for a cell/column and the span
+        of that cell/column". 
+        EX 
+        [('this is column one',1), ('this is column two',1)]
+        """
+        
+        apply(ST.StructuredTextDocument.__init__,(self,[]),kw)
+        self._columns = []
+        for column in row:            
+            self._columns.append(StructuredTextColumn(column[0],column[1],kw))
+    def getColumns(self):
+        return [self._columns]
+
+    def _getColumns(self):
+        return [self._columns]
+    
+# this holds the raw text of a table cell
+class StructuredTextColumn(ST.StructuredTextParagraph):
+    """
+    StructuredTextColumn is a cell/column in a table.
+    This contains the actual text of a column and is
+    thus a StructuredTextParagraph. A StructuredTextColumn
+    also holds the span of its column
+    """
+    
+    def __init__(self,text,span,kw):
+        apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw)
+        self._span = span
+    
+    def getSpan(self):
+        return self._span
+    
+    def _getSpan(self):
+        return self._span
+    
+class StructuredTextMarkup(STDOM.Element):
+    
+    def __init__(self, v, **kw):
+       self._value=v
+       self._attributes=kw.keys()
+       for k, v in kw.items(): setattr(self, k, v)
+
+    def getChildren(self, type=type, lt=type([])):
+       v=self._value
+       if type(v) is not lt: v=[v]
+       return v
+
+    def getColorizableTexts(self): return self._value,
+    def setColorizableTexts(self, v): self._value=v[0]
+
+    def __repr__(self):
+       return '%s(%s)' % (self.__class__.__name__, `self._value`)
+
+class StructuredTextLiteral(StructuredTextMarkup):
+    def getColorizableTexts(self): return ()
+    def setColorizableTexts(self, v): pass
+
+class StructuredTextEmphasis(StructuredTextMarkup): pass
+
+class StructuredTextStrong(StructuredTextMarkup): pass
+
+class StructuredTextInnerLink(StructuredTextMarkup): pass
+
+class StructuredTextNamedLink(StructuredTextMarkup): pass
+
+class StructuredTextUnderline(StructuredTextMarkup): pass
+
+class StructuredTextLink(StructuredTextMarkup):
+    "A simple hyperlink"
+
+class DocumentClass:    
+    """
+    Class instance calls [ex.=> x()] require a structured text
+    structure. Doc will then parse each paragraph in the structure
+    and will find the special structures within each paragraph.
+    Each special structure will be stored as an instance. Special
+    structures within another special structure are stored within
+    the 'top' structure
+    EX : '-underline this-' => would be turned into an underline
+    instance. '-underline **this**' would be stored as an underline
+    instance with a strong instance stored in its string
+    """
+
+    paragraph_types  = [
+        'doc_bullet',
+        'doc_numbered',
+        'doc_description',
+        'doc_header',
+        'doc_table',
+        ]
+
+    text_types = [
+        'doc_href',
+        'doc_strong',
+        'doc_emphasize',
+        'doc_literal',
+        'doc_inner_link',
+        'doc_named_link',
+        'doc_underline',
+        ]
+
+    def __call__(self, doc):
+        if type(doc) is type(''):
+           doc=ST.StructuredText(doc)
+           doc.setSubparagraphs(self.color_paragraphs(
+              doc.getSubparagraphs()))
+        else:
+           doc=ST.StructuredTextDocument(self.color_paragraphs(
+              doc.getSubparagraphs()))
+        return doc
+
+    def parse(self, raw_string, text_type,
+              type=type, st=type(''), lt=type([])):
+
+       """
+       Parse accepts a raw_string, an expr to test the raw_string,
+       and the raw_string's subparagraphs.
+       
+       Parse will continue to search through raw_string until 
+       all instances of expr in raw_string are found. 
+       
+       If no instances of expr are found, raw_string is returned.
+       Otherwise a list of substrings and instances is returned
+       """
+
+       tmp = []    # the list to be returned if raw_string is split
+       append=tmp.append
+
+       if type(text_type) is st: text_type=getattr(self, text_type)
+
+       while 1:
+          t = text_type(raw_string)
+          if not t: break
+          #an instance of expr was found
+          t, start, end    = t
+
+          if start: append(raw_string[0:start])
+
+          tt=type(t)
+          if tt is st:
+             # if we get a string back, add it to text to be parsed
+             raw_string = t+raw_string[end:len(raw_string)]
+          else:
+             if tt is lt:
+                # is we get a list, append it's elements
+                tmp[len(tmp):]=t
+             else:
+                # normal case, an object
+                append(t)
+             raw_string = raw_string[end:len(raw_string)]
+
+       if not tmp: return raw_string # nothing found
+       
+       if raw_string: append(raw_string)
+       elif len(tmp)==1: return tmp[0]
+       
+       return tmp
+
+
+    def color_text(self, str, types=None):
+       """Search the paragraph for each special structure
+       """
+       if types is None: types=self.text_types
+
+       for text_type in types:
+
+          if type(str) is StringType:
+             str = self.parse(str, text_type)
+          elif type(str) is ListType:
+             r=[]; a=r.append
+             for s in str:
+                if type(s) is StringType:
+                    s=self.parse(s, text_type)
+                    if type(s) is ListType: r[len(r):]=s
+                    else: a(s)
+                else:
+                    s.setColorizableTexts(
+                       map(self.color_text,
+                           s.getColorizableTexts()
+                           ))
+                    a(s)
+             str=r
+          else:
+             r=[]; a=r.append; color=self.color_text
+             for s in str.getColorizableTexts():
+                color(s, (text_type,))
+                a(s)
+                
+             str.setColorizableTexts(r)
+
+       return str
+
+    def color_paragraphs(self, raw_paragraphs,
+                           type=type, sequence_types=(type([]), type(())),
+                           st=type('')):
+       result=[]
+       for paragraph in raw_paragraphs:
+          
+          if paragraph.getNodeName() != 'StructuredTextParagraph':
+             result.append(paragraph)
+             continue
+          
+          for pt in self.paragraph_types:
+             if type(pt) is st:
+                # grab the corresponding function
+                pt=getattr(self, pt)
+             # evaluate the paragraph
+             r=pt(paragraph)
+             if r:
+                if type(r) not in sequence_types:
+                    r=r,
+                new_paragraphs=r
+                for paragraph in new_paragraphs:
+                    paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
+                break
+          else:
+             new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
+                                                          self.color_paragraphs(paragraph.getSubparagraphs()),
+                                                          indent=paragraph.indent),
+          # color the inline StructuredText types
+          # for each StructuredTextParagraph
+          for paragraph in new_paragraphs:
+             paragraph.setColorizableTexts(
+                map(self.color_text,
+                    paragraph.getColorizableTexts()
+                    ))
+             result.append(paragraph)
+
+       return result
+    
+    def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match):
+        text    = paragraph.getColorizableTexts()[0]
+        m       = expr(text)
+        
+        if not (m):
+            return None
+        rows = []
+    
+        # initial split
+        for row in split(text,"\n"):
+            rows.append(row)    
+    
+        # clean up the rows
+        for index in range(len(rows)):
+            tmp = []
+            rows[index] = strip(rows[index])
+            l = len(rows[index])-2
+            result = split(rows[index][:l],"||")
+            for text in result:
+                if text:
+                    tmp.append(text)
+                    tmp.append('')
+                else:
+                    tmp.append(text)
+            rows[index] = tmp
+        # remove trailing '''s
+        for index in range(len(rows)):
+            l = len(rows[index])-1
+            rows[index] = rows[index][:l]
+        
+        result = []
+        for row in rows:
+            cspan   = 0
+            tmp     = []
+            for item in row:
+                if item:
+                    tmp.append(item,cspan)
+                    cspan = 0
+                else:
+                    cspan = cspan + 1
+            result.append(tmp)
+        
+        subs = paragraph.getSubparagraphs()
+        indent=paragraph.indent
+        return StructuredTextTable(result,text,subs,indent=paragraph.indent)
+            
+    def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
+        top=paragraph.getColorizableTexts()[0]
+        m=expr(top)
+
+        if not m:
+            return None
+            
+        subs=paragraph.getSubparagraphs()
+        if top[-2:]=='::':
+           subs=[StructuredTextExample(subs)]
+           top=top[:-1]
+        return StructuredTextBullet(top[m.span()[1]:], subs,
+                                     indent=paragraph.indent,
+                                     bullet=top[:m.span()[1]]
+                                     )
+
+    def doc_numbered(
+        self, paragraph,
+        expr = re.compile('(\s*[a-zA-Z]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)').match):
+        
+        # This is the old expression. It had a nasty habit
+        # of grabbing paragraphs that began with a single
+        # letter word even if there was no following period.
+        
+        #expr = re.compile('\s*'
+        #                   '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
+        #                   '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
+        #                   '\s+').match):
+        
+        top=paragraph.getColorizableTexts()[0]
+        m=expr(top)
+        if not m: return None
+        subs=paragraph.getSubparagraphs()
+        if top[-2:]=='::':
+           subs=[StructuredTextExample(subs)]
+           top=top[:-1]
+        return StructuredTextNumbered(top[m.span()[1]:], subs,
+                                        indent=paragraph.indent,
+                                        number=top[:m.span()[1]])
+
+    def doc_description(
+        self, paragraph,
+        delim = re.compile('\s+--\s+').search,
+        nb=re.compile(r'[^\0- ]').search,
+        ):
+
+        top=paragraph.getColorizableTexts()[0]
+        d=delim(top)
+        if not d: return None
+        start, end = d.span()
+        title=top[:start]
+        if find(title, '\n') >= 0: return None
+        if not nb(title): return None
+        d=top[start:end]
+        top=top[end:]
+
+        subs=paragraph.getSubparagraphs()
+        if top[-2:]=='::':
+           subs=[StructuredTextExample(subs)]
+           top=top[:-1]
+
+        return StructuredTextDescription(
+           title, top, subs,
+           indent=paragraph.indent,
+           delim=d)
+
+    def doc_header(self, paragraph,
+                    expr    = re.compile('[ a-zA-Z0-9.:/,-_*<>\?\'\"]+').match
+                    ):
+        subs=paragraph.getSubparagraphs()
+        if not subs: return None
+        top=paragraph.getColorizableTexts()[0]
+        if not strip(top): return None
+        if top[-2:]=='::':
+           subs=StructuredTextExample(subs)
+           if strip(top)=='::': return subs
+           return ST.StructuredTextParagraph(top[:-1],
+                                             [subs],
+                                             indent=paragraph.indent,
+                                             level=paragraph.level)
+
+        if find(top,'\n') >= 0: return None
+        return StructuredTextSection(top, subs, indent=paragraph.indent, level=paragraph.level)
+
+    def doc_literal(
+        self, s,
+        expr=re.compile(
+          "(?:\s|^)'"                                                  # open
+          "([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
+          "'(?:\s|[,.;:!?]|$)"                                        # close
+          ).search):
+        
+        r=expr(s)
+        if r:
+           start, end = r.span(1)
+           return (StructuredTextLiteral(s[start:end]), start-1, end+1)
+        else:
+           return None
+
+    def doc_emphasize(
+        self, s,
+        expr = re.compile('\s*\*([ \na-zA-Z0-9.:/;,\'\"\?]+)\*(?!\*|-)').search
+        ):
+
+        r=expr(s)
+        if r:
+           start, end = r.span(1)
+           return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
+        else:
+           return None
+    
+    def doc_inner_link(self,
+                       s,
+                       expr1 = re.compile("\.\.\s*").search,
+                       expr2 = re.compile("\[[a-zA-Z0-9]+\]").search):
+        
+        # make sure we dont grab a named link
+        if expr2(s) and expr1(s):
+            start1,end1 = expr1(s).span()
+            start2,end2 = expr2(s).span()
+            if end1 == start2:
+                # uh-oh, looks like a named link
+                return None
+            else:
+                # the .. is somewhere else, ignore it
+                return (StructuredTextInnerLink(s[start2+1,end2-1],start2,end2))
+            return None
+        elif expr2(s) and not expr1(s):
+            start,end = expr2(s).span()
+            return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
+        return None
+    
+    def doc_named_link(self,
+                       s,
+                       expr=re.compile("(\.\.\s)(\[[a-zA-Z0-9]+\])").search):
+        
+        result = expr(s)
+        if result:
+            start,end   = result.span(2)
+            a,b         = result.span(1)
+            str         = strip(s[a:b]) + s[start:end]
+            str         = s[start+1:end-1]
+            st,en       = result.span()
+            return (StructuredTextNamedLink(str),st,en)
+            #return (StructuredTextNamedLink(s[st:en]),st,en)
+        return None
+    
+    def doc_underline(self,
+                      s,
+                      expr=re.compile("\_([a-zA-Z0-9\s\.,\?\/]+)\_").search):
+        
+        result = expr(s)
+        if result:
+            start,end = result.span(1)
+            st,e = result.span()
+            return (StructuredTextUnderline(s[start:end]),st,e)
+        else:
+            return None
+    
+    def doc_strong(self, 
+                   s,
+        expr = re.compile('\s*\*\*([ \na-zA-Z0-9.:/;\-,!\?\'\"]+)\*\*').search
+        ):
+
+        r=expr(s)
+        if r:
+           start, end = r.span(1)
+           return (StructuredTextStrong(s[start:end]), start-2, end+2)
+        else:
+           return None
+    
+    def doc_href(
+        
+        self, s,
+        expr1 = re.compile("(\"[ a-zA-Z0-9\n\-\.\,\;\(\)\/\:\/]+\")(:)([a-zA-Z0-9\:\/\.\~\-]+)([,]*\s*)").search,
+        expr2 = re.compile('(\"[ a-zA-Z0-9\n\-\.\:\;\(\)\/]+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#]+)(\s*)').search):
+        
+        #expr1=re.compile('\"([ a-zA-Z0-9.:/;,\n\~\(\)\-]+)\"'
+        #                  ':'
+        #                  '([a-zA-Z0-9.:/;,\n\~]+)(?=(\s+|\.|\!|\?))'
+        #                  ).search,
+        #expr2=re.compile('\"([ a-zA-Z0-9./:]+)\"'
+        #                  ',\s+'
+        #                  '([ a-zA-Z0-9@.:/;]+)(?=(\s+|\.|\!|\?))'
+        #                  ).search,
+        
+        punctuation = re.compile("[\,\.\?\!\;]+").match
+        r=expr1(s) or expr2(s)
+
+        if r:
+            # need to grab the href part and the
+            # beginning part
+                        
+            start,e = r.span(1)
+            name    = s[start:e]
+            name    = replace(name,'"','',2)
+            #start   = start + 1
+            st,end   = r.span(3)
+            if punctuation(s[end-1:end]):
+                end = end -1
+            link    = s[st:end]
+            #end     = end - 1                        
+            
+            # name is the href title, link is the target
+            # of the href
+            return (StructuredTextLink(name, href=link),
+                    start, end)
+            
+            #return (StructuredTextLink(s[start:end], href=s[start:end]),
+            #        start, end)
+        else:
+            return None