# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
class StyletextError(Exception):
pass
class Style:
is_Style = 1
def __init__(self, **kw):
if len(kw) != 1:
raise StyletextError(
"Style should be called with exactly one parameter "
"eg. Style(size=3): %s" % kw)
self.sort = kw.keys()[0]
self.options = kw.values()[0]
self._signature_ = str(kw)
def __cmp__ (self, other):
if not hasattr(other, 'is_Style'):
raise StyletextError(
"can't compare styles to non-style objects: %s" %
other)
s1 = self._signature_
s2 = other._signature_
return cmp(s1,s2)
class Id:
def __init__(self):
self.next_id = -1
def new_id(self):
self.next_id = self.next_id+1
return '_'+str(self.next_id)
class Pool(Id):
def __init__(self, text_inst=None):
self.keys = []
self.signatures = []
self.dict = {}
self.Text = text_inst
Id.__init__(self)
def __len__(self):
return len(self.keys)
def __getitem__(self, key):
i = self.keys.index(key)
sig = self.signatures[i]
return self.dict[sig]
def __setitem__(self, key, value):
# XXX inconistency: keys should be created solely by pool
try:
i = self.keys.index(key)
sig = value._signature_
except:
self.keys.append(key)
sig = value._signature_
self.signatures.append(sig)
self.dict[sig] = value
def __delitem__(self, key):
i = self.keys.index(key)
del self.keys[i]
sig = self.signatures[i]
del self.signatures[i]
try:
i = self.signatures.index(sig)
except:
del self.dict[sig]
def dump(self):
out = []
print "______________ POOL DUMP __________"
for i in range(len(self.keys)):
sig = self.signatures[i]
id = self.keys[i]
if self.Text is not None:
index = self.Text.index(id)
else: index = ""
out.append((index, id, self.dict[sig]))
for index, id, style in out:
print index,"\t", id,"\t", style
print "# of keys = ", len(self.keys)
print "# of signatures = ", len(self.signatures)
class Iterator:
def __init__(self, pool):
self.pool = pool
self.i = -1
self._update()
def __repr__(self):
return "Iterator of %s" % repr(self.pool)
def __str__(self):
if self.i<0:
index = "1.0"
elif self.i>=len(self.pool):
index = "end"
else:
index = self.id
return str(index)
def _update(self):
if self.i >= len(self.pool) or self.i <0:
self.outofboundary = 1
self.current = None
self.id = None
else:
self.outofboundary = 0
self.id = self.pool.keys[self.i]
sig = self.pool.signatures[self.i]
self.current = self.pool.dict[sig]
def next(self):
if self.i<len(self.pool):
self.i = self.i+1
self._update()
return self.current
def prev(self):
if self.i>=0:
self.i = self.i-1
self._update()
return self.current
def insert_left(self, style, index=None):
'''inserts stlye before index or if it is not given, after
current id'''
texti = self._textinst()
if index is None:
index = self.current
if index is None:
raise StyletextError(
"Can't insert at index: %s" % index)
it = self.pool.iterator()
it.before(index)
it.next()
it.align_left()
# case 1: there is no mark before index
if it.outofboundary:
i = it.i
# case 2: there is already a mark at index
elif texti.compare(it, '==', index):
it.align_left()
i = it.i
# case 3: there is no mark at index, but after index
else:
i = it.i
sig = style._signature_
id = self.pool.new_id()
texti.mark_set(id, index)
texti.mark_gravity(id, 'right')
self.pool.keys.insert(i, id)
self.pool.signatures.insert(i, sig)
self.pool.dict[sig] = style
self.i = i
return self._update()
def insert_right(self, style, index=None):
'''inserts stlye after index or if it is not given,
after current id'''
texti = self._textinst()
if index is None:
index = self.current
if index is None:
raise StyletextError(
"Can't insert at index '%s'" % index)
it = self.pool.iterator()
it.after(index)
it.align_right()
# case 1: there is no mark at or after index
if it.outofboundary:
i = it.i
# case 2: there is already a mark at index
elif texti.compare(it, '==', index):
it.align_right()
i = it.i+1
# case 3: there is no mark at index, but after index
else:
i = it.i
sig = style._signature_
id = self.pool.new_id()
texti.mark_set(id, index)
texti.mark_gravity(id, 'right')
self.pool.keys.insert(i, id)
self.pool.signatures.insert(i, sig)
self.pool.dict[sig] = style
self.i = i
return self._update()
def delete(self):
texti = self._textinst()
del self.pool.keys[self.i]
del self.pool.signatures[self.i]
texti.mark_unset(self.id)
if self.i>len(self.pool):
self.i = self.i-1
self._update()
def _textinst(self):
'''Returns pools assigned text instance, error if no Text is
assigned'''
if self.pool.Text is None:
raise StyletextError("pool has no 'text' instance")
return self.pool.Text
def guess(self, indexOrId):
'''finds a style mark which is before indexOrId. Not Necessarily
the nearest one.
'''
if len(self.pool) == 0:
return self._update()
texti = self._textinst()
# start with a good guess
index = indexOrId
while 1:
mark = texti.mark_previous(index)
#print "mark = ",mark
if mark is None or type(mark)=='None':
# there is no mark before indexorid then
self.i = -1
return self._update()
if mark[0] == '_':
id = mark
break
index = mark
i = self.pool.keys.index(id)
self.i = i
return self._update()
def after(self, indexOrId):
'''finds last stylemark which is at index or, if there is none,
the next one'''
if len(self.pool) == 0:
return self._update()
texti = self._textinst()
it = self.pool.iterator()
# start with a good guess
it.guess(indexOrId)
if it.i<0:
it.next()
i = it.i
# fine tuning
while texti.compare(self.pool.keys[i],'<', indexOrId):
i = i+1
if i>=len(self.pool):
break
self.i = i
def before(self, indexOrId):
'''finds last style mark which is before indexOrId
# XXX inconsistent to Tk: previous = next mark bevor or at index
'''
if len(self.pool) == 0:
return self._update()
texti = self._textinst()
it = self.pool.iterator()
# start with a good guess
it.guess(indexOrId)
while (not it.outofboundary) and (texti.compare(it,'>=', \
indexOrId)):
it.prev()
if it.outofboundary:
self.i = it.i
return self._update()
i = it.i
# now fine tuning
oi = i
while texti.compare(self.pool.keys[i], '<',indexOrId):
#~ print i, "<", texti.index(indexOrId)
oi = i
i = i+1
if i>=len(self.pool):
break
# now oi is at the last stylemark which is before indexOrId
self.i = oi
return self._update()
def set(self, id):
self.i = self.pool.keys.index(id)
self._update()
return current
def align_left(self):
if self.outofboundary:
return self.current
ids = self.pool.keys
compare = self._textinst().compare
i = self.i
while i>0 and i<len(self.pool) and compare(ids[i-1],'==',ids[i]):
i = i-1
self.i = i
self._update()
return self.current
def align_right(self):
if self.outofboundary:
return self.current
ids = self.pool.keys
compare = self._textinst().compare
i = self.i
while i<len(ids)-1 and compare(ids[i],'==',ids[i+1]):
i = i+1
self.i = i
self._update()
return self.current
def is_first(self):
return self.i == 0
def is_last(self):
return self.i == len(self.pool)-1
def iterator(self):
return Pool.Iterator(pool=self)
if __name__=='__main__':
from Tkinter import *
tk = Tk()
text = Text(tk)
text.pack()
for i in range(5):
text.insert('end', '0123456789\n')
pool = Pool(text)
s1 = Style(q=1)
s2 = Style(w=1)
s3 = Style(w=1)
if s2 == s3: print "error"
if s2 != s3: print "error"
i = pool.iterator()
i.insert_left(Style(color = "blue"),'1.0')
i.insert_left(Style(color = "red"),'2.0')
i.insert_left(Style(color = "green"),'3.0')
i.insert_left(Style(color = "black"),'1.0')
i.insert_right(Style(color = "grey"),'1.0')
pool.dump()
i.after("1.0")
print "after 1.0: ",i,i.i
i.after("2.0")
print "after 2.0: ",i,i.i
|