def __init__(self, master, **kw): apply(ScrolledText.__init__, (self, master), kw) self.pool = Pool(self) self.sorts = {} self.sorts_order = [] self.clip_text = '' self.clip_styling = [] # XXX force-mechanism should be moved to TextEditor self.style_force = [] self._patch_tk() self.bind('<Control-c>', self.copyevent) self.bind('<Control-v>', self.pasteevent) self.bind('<Alt-p>', lambda e, s=self:s.pool.dump()) self.bind('<Alt-t>', lambda e, s=self:s.tag_dump()) self.bind('<Alt-m>', lambda e, s=self:s.mark_dump()) self.bind('<Alt-g>', lambda e, s=self:s.styling_dump()) self.bind('<Alt-s>', lambda e, s=self:s.print_current_style()) self.bind('<Alt-f>', lambda e, s=self:s.fontify('1.0','end'))
def style_removeall(self): for mark in self.mark_names(): if mark[0] == '_': self.mark_unset(mark) self.pool = Pool(self) self.fontify('1.0','end')
class StyleText(ScrolledText): def __init__(self, master, **kw): apply(ScrolledText.__init__, (self, master), kw) self.pool = Pool(self) self.sorts = {} self.sorts_order = [] self.clip_text = '' self.clip_styling = [] # XXX force-mechanism should be moved to TextEditor self.style_force = [] self._patch_tk() self.bind('<Control-c>', self.copyevent) self.bind('<Control-v>', self.pasteevent) self.bind('<Alt-p>', lambda e, s=self:s.pool.dump()) self.bind('<Alt-t>', lambda e, s=self:s.tag_dump()) self.bind('<Alt-m>', lambda e, s=self:s.mark_dump()) self.bind('<Alt-g>', lambda e, s=self:s.styling_dump()) self.bind('<Alt-s>', lambda e, s=self:s.print_current_style()) self.bind('<Alt-f>', lambda e, s=self:s.fontify('1.0','end')) def _patch_tk(self): insert_tcl = self.register(self.insert) delete_tcl = self.register(self.delete) tclstr = """ rename _w _old_w proc _w { command args } { if { $command == "insert" } { set ret [ eval "insert_tcl $args" ] } elseif {$command == "delete"} { set ret [ eval "delete_tcl $args" ] } else { set ret [ eval "_old_w $command $args" ] } return $ret } """ self._old_w = '.old'+self._w tclstr = replace(tclstr, '_old_w', self._old_w) tclstr = replace(tclstr, '_w', self._w) tclstr = replace(tclstr, 'insert_tcl', insert_tcl) tclstr = replace(tclstr, 'delete_tcl', delete_tcl) self.tk.eval(tclstr) def copyevent(self, event): range = self.tag_ranges('sel') if len(range) !=2: return self.clip_text = apply(self.get, range) self.clip_styling = apply(self.styling_get, range) def pasteevent(self, event): self.delselection() index = self.index('insert') self.insert(index, self.clip_text) self.styling_apply(index, self.clip_styling) def delselection(self): sel = self.tag_ranges('sel') if len(sel) == 2: apply(self.delete, sel) def register_sort(self, sortid, function, default): defaultstyle = apply(Style,(),{sortid : default}) self.sorts[sortid] = (function, defaultstyle) self.sorts_order.insert(0, sortid) def print_current_style(self,event=None): print "_____ current style %s ______" % self.index('insert') for style in self.style_get('insert'): print self.index('insert'),"\t", style def mark_dump(self): list = [] for name in self.mark_names(): list.append((self.index(name), name)) list.sort() print "============== mark dump =================" for pos, name in list: # if name[0] == '_': print pos,"\t", name def tag_dump(self): print "============== tag dump ===================" names = list(self.tag_names()) out = [] for tag in names: ranges = self.tag_ranges(tag) if len(ranges)>0: out.append((ranges, tag)) out.sort() for ranges, tag in out: print tag,"\t\t", ranges def delete(self, index1, index2=None): index1 = self.index(index1) it = self.pool.iterator() self.tk.call(self._old_w, 'delete', index1, index2) # remove all tag changes which became obsolete. # Obsolete means, (1) the tag is at or after 'end' or (2) there # is another tag of the same sort at the same index position. it.before(index1) it.next() dict = {} while (not it.outofboundary) and self.compare(it, '==', index1): sort = it.current.sort if dict.has_key(sort): it.delete() else: dict[sort] = 1 it.next() it.i = len(self.pool)-1 it._update() while (not it.outofboundary) and self.compare(it, '>=', 'end'): it.delete() def insert(self, index, chars, *tagsnstyles): tags = [] styles = self.style_force for arg in tagsnstyles: if type(arg).__name__ == 'string': tags = tags + arg elif hasattr(arg, 'is_Style'): styles.append(arg) else: raise StyletextError( "arguments should be tags and styles: %s" % arg) begin = self.index(index) self.tk.call((self._old_w, 'insert', index, chars) + tuple(tags)) end = self.index('%s + %dc' % (begin, len(chars))) for style in styles: self.style_add(style, begin, end, supressfontify=1) self.fontify("%s linestart - 1 lines" % begin, "%s lineend" % end) self.style_force = [] def style_get(self, index, sort = None): '''Returns the style of type sort at position index. If sort is not given, returns a complete list of all styles. ''' it = self.pool.iterator() it.before(index) if sort is not None: # look for a style of type 'sort' if it.outofboundary: return self.sorts[sort][1] while not it.is_first() and not it.current.sort == sort: it.prev() if it.current.sort == sort: return it.current else: return self.sorts[sort][1] else: dict = {} if not it.outofboundary: dict[it.current.sort] = it.current while (not it.is_first()) and len(dict)<len(self.sorts): it.prev() if not dict.has_key(it.current.sort): dict[it.current.sort] = it.current # if incomplete: fill up with defaults for sort in self.sorts.keys(): if not dict.has_key(sort): dict[sort] = self.sorts[sort][1] ret = [] # keep the order as given in self.sorts.keys() for sort in self.sorts_order: ret.append(dict[sort]) return ret def style_get_range(self, begin, end, sort=None): '''returns all styles between begin and end. If sort is given, only those of type sort are returned. ''' styles = self.style_get(begin+'+1 chars') it = self.pool.iterator() it.before(begin+'+1 chars') while not it.is_last(): it.next() if self.compare(it.id,'>=', end): break try: i = styles.index(it.current) except: styles.append(it.current) return styles def style_removeall(self): for mark in self.mark_names(): if mark[0] == '_': self.mark_unset(mark) self.pool = Pool(self) self.fontify('1.0','end') def styling_get(self, begin, end): it = self.pool.iterator() styles = self.style_get(begin+"+ 1 chars") styling = [] for style in styles: styling.append((style, begin)) it.before(begin) # need to replace befores when there a style at while 1: if it.i >= len(self.pool): break elif it.i >= 0: if self.compare(it, '>', end): break elif self.compare(it, '>', begin): style = it.current styling.append((style, self.index(it))) it.next() # end = self.index(end) styling.append((None, end)) # relocate indizes ret = [] bline, bchar = map(atoi, split(begin, '.')) for style, indexstr in styling: line, char = map(atoi, split(indexstr, '.')) if line == bline: char = char-bchar line = line-bline ret.append((style, line, char)) return ret def styling_apply(self, index, styling): if styling is None or len(styling) == 0: return index = self.index(index) iline, ichar = map(atoi, split(index, '.')) it = self.pool.iterator() # determine end tmp, eline, echar = styling[-1] if eline == 0: echar = echar+ichar eline = eline+iline end = "%i.%i" % (eline, echar) i = 0 for style, line, char in styling[:-1]: i = i+1 if line == 0: char = char+ichar line = line+iline indexstr = "%i.%i" % (line, char) if i<4: self.style_add(style, index, end, supressfontify=1) else: # insert it it.insert_right(style, indexstr) self.fontify(index, end) def styling_dump(self): range = self.tag_ranges('sel') if len(range) != 2: return print apply(self.styling_get, range) def fontify(self, begin, end): for name in self.tag_names(): if name[0]=='_': self.tag_remove(name, begin, end) styles = self.style_get(begin) dict = {} for style in styles: name = name+style._signature_ dict[style.sort] = style.options it = self.pool.iterator() it.before(begin) # last style change before' begin' a = begin while self.compare(a,'<',end): if it.is_last(): b = end else: it.next() b = it.id options = {} name = '_'+str(dict) self.tag_add(name,a, b) _dict = dict.copy() for sort in self.sorts_order: func = self.sorts[sort][0] apply(func, (options, _dict)) self.tag_configure(name, options) if not it.outofboundary: dict[it.current.sort] = it.current.options a = b def style_add(self, style, begin, end, supressfontify=0): if self.compare(begin,'>=',end): return it = self.pool.iterator() # this is the simplest, definitly not the fastest solution ostyle_begin = self.style_get(begin, style.sort) ostyle_end = self.style_get(end+' +1 chars', style.sort) # delete all style changes of sort "style.sort" between "begin" and # "end" it.before(begin) it.next() while (not it.outofboundary) and self.compare(it,'<=',end): if it.current.sort == style.sort: it.delete() else: it.next() if ostyle_begin != style: it.insert_left(style, begin) if ostyle_end != style: it.insert_right(ostyle_end, end) if not supressfontify: self.fontify(begin, end) # for older Tkinter version (those coming with Python 1.5.2) add # some missing Text methods def mark_next(self, index): """Return the name of the next mark after INDEX.""" return self.tk.call(self._w, 'mark', 'next', index) or None def mark_previous(self, index): """Return the name of the previous mark before INDEX.""" return self.tk.call(self._w, 'mark', 'previous', index) or None