import Qd import TE import Fm import waste import WASTEconst import Res import Evt import Events import Scrap import string import Win import Wbase import Wcontrols from SpecialKeys import * import PyFontify from types import * import Fonts import TextEdit class TextBox(Wbase.Widget): def __init__(self, possize, text = "", align = TextEdit.teJustLeft, fontsettings = ("Monaco", 0, 9, (0, 0, 0)), backcolor = (0xffff, 0xffff, 0xffff) ): Wbase.Widget.__init__(self, possize) self.fontsettings = fontsettings self.text = text self.align = align self.backcolor = backcolor # Settings of editor added by Petr 9/7/97 def draw(self, visRgn = None): if self._visible: (font, style, size, color) = self.fontsettings fontid = GetFNum(font) savestate = Qd.GetPenState() Qd.TextFont(fontid) Qd.TextFace(style) Qd.TextSize(size) Qd.RGBForeColor(color) Qd.RGBBackColor(self.backcolor) # Added by Petr 9/7/97 TE.TETextBox(self.text, self._bounds, self.align) Qd.RGBBackColor((0xffff, 0xffff, 0xffff)) # Reset color Added by Petr 9/7/97 Qd.SetPenState(savestate) def get(self): return self.text def set(self, text): self.text = text if self._parentwindow and self._parentwindow.wid: self.SetPort() self.draw() class ScrollWidget: # to be overridden def getscrollbarvalues(self): return None, None # internal method def updatescrollbars(self): vx, vy = self.getscrollbarvalues() if self._parent._barx: if vx <> None: self._parent._barx.enable(1) self._parent._barx.set(vx) else: self._parent._barx.enable(0) if self._parent._bary: if vy <> None: self._parent._bary.enable(1) self._parent._bary.set(vy) else: self._parent._bary.enable(0) UNDOLABELS = [ # Indexed by WEGetUndoInfo() value None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"] class EditText(Wbase.SelectableWidget, ScrollWidget): def __init__(self, possize, text = "", callback = None, inset = (3, 3), fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)), readonly = 0): Wbase.SelectableWidget.__init__(self, possize) self.temptext = text self.ted = None self.selection = None self._callback = callback self.changed = 0 self.selchanged = 0 self._selected = 0 self._enabled = 1 self.wrap = 1 self.readonly = readonly self.fontsettings = fontsettings if type(inset) <> TupleType: self.inset = (inset, inset) else: self.inset = inset def open(self): if not hasattr(self._parent, "_barx"): self._parent._barx = None if not hasattr(self._parent, "_bary"): self._parent._bary = None self._calcbounds() self.SetPort() viewrect, destrect = self._calctextbounds() flags = self._getflags() self.ted = waste.WENew(destrect, viewrect, flags) self.ted.WEInstallTabHooks() self.ted.WESetAlignment(WASTEconst.weFlushLeft) self.setfontsettings(self.fontsettings) self.ted.WEUseText(Res.Resource(self.temptext)) self.ted.WECalText() if self.selection: self.setselection(self.selection[0], self.selection[1]) self.selection = None else: self.selview() self.temptext = None self.updatescrollbars() self.bind("pageup", self.scrollpageup) self.bind("pagedown", self.scrollpagedown) self.bind("top", self.scrolltop) self.bind("bottom", self.scrollbottom) self.selchanged = 0 def close(self): self._parent._barx = None self._parent._bary = None self.ted = None self.temptext = None Wbase.SelectableWidget.close(self) def getfontsettings(self): import Res (font, style, size, color) = self.ted.WEGetRunInfo(0)[4] font = GetFName(font) return (font, style, size, color) def setfontsettings(self, (font, style, size, color)): self.SetPort() if type(font) <> StringType: font = GetFName(font) self.fontsettings = (font, style, size, color) fontid = GetFNum(font) readonly = self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, -1) if readonly: self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 0) self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1) selstart, selend = self.ted.WEGetSelection() self.ted.WESetSelection(0, self.ted.WEGetTextLength()) self.ted.WESetStyle(WASTEconst.weDoFace, (0, 0, 0, (0, 0, 0))) self.ted.WESetStyle(WASTEconst.weDoFace | WASTEconst.weDoColor | WASTEconst.weDoFont | WASTEconst.weDoSize, (fontid, style, size, color)) self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0) self.ted.WECalText() self.ted.WESetSelection(selstart, selend) if readonly: self.ted.WEFeatureFlag(WASTEconst.weFReadOnly, 1) viewrect = self.ted.WEGetViewRect() Qd.EraseRect(viewrect) self.ted.WEUpdate(self._parentwindow.wid.GetWindowPort().visRgn) self.selchanged = 1 self.updatescrollbars() def adjust(self, oldbounds): self.SetPort() if self._selected and self._parentwindow._hasselframes: Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3)) Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3)) else: Win.InvalRect(oldbounds) Win.InvalRect(self._bounds) viewrect, destrect = self._calctextbounds() self.ted.WESetViewRect(viewrect) self.ted.WESetDestRect(destrect) if self.wrap: self.ted.WECalText() if self.ted.WEGetDestRect()[3] < viewrect[1]: self.selview() self.updatescrollbars() # interface ----------------------- # selection stuff def selview(self): self.ted.WESelView() def selectall(self): self.ted.WESetSelection(0, self.ted.WEGetTextLength()) self.selchanged = 1 self.updatescrollbars() def selectline(self, lineno, charoffset = 0): newselstart, newselend = self.ted.WEGetLineRange(lineno) self.ted.WESetSelection(newselstart + charoffset, newselend) self.selchanged = 1 self.updatescrollbars() def getselection(self): if self.ted: return self.ted.WEGetSelection() else: return self.selection def setselection(self, selstart, selend): self.selchanged = 1 if self.ted: self.ted.WESetSelection(selstart, selend) self.ted.WESelView() self.updatescrollbars() else: self.selection = selstart, selend def offsettoline(self, offset): return self.ted.WEOffsetToLine(offset) def countlines(self): return self.ted.WECountLines() def getselectedtext(self): selstart, selend = self.ted.WEGetSelection() return self.ted.WEGetText().data[selstart:selend] def expandselection(self): oldselstart, oldselend = self.ted.WEGetSelection() selstart, selend = min(oldselstart, oldselend), max(oldselstart, oldselend) if selstart <> selend and chr(self.ted.WEGetChar(selend-1)) == '\r': selend = selend - 1 newselstart, dummy = self.ted.WEFindLine(selstart, 0) dummy, newselend = self.ted.WEFindLine(selend, 0) if oldselstart <> newselstart or oldselend <> newselend: self.ted.WESetSelection(newselstart, newselend) self.updatescrollbars() self.selchanged = 1 def insert(self, text): self.ted.WEInsert(text, None, None) self.changed = 1 self.selchanged = 1 def shiftleft(self): self.expandselection() selstart, selend = self.ted.WEGetSelection() selstart, selend = min(selstart, selend), max(selstart, selend) snippet = self.getselectedtext() lines = string.split(snippet, '\r') for i in range(len(lines)): if lines[i][:1] == '\t': lines[i] = lines[i][1:] snippet = string.join(lines, '\r') self.insert(snippet) self.ted.WESetSelection(selstart, selstart + len(snippet)) def shiftright(self): self.expandselection() selstart, selend = self.ted.WEGetSelection() selstart, selend = min(selstart, selend), max(selstart, selend) snippet = self.getselectedtext() lines = string.split(snippet, '\r') for i in range(len(lines) - 1): lines[i] = '\t' + lines[i] snippet = string.join(lines, '\r') self.insert(snippet) self.ted.WESetSelection(selstart, selstart + len(snippet)) # text def set(self, text): if not self.ted: self.temptext = text else: self.ted.WEUseText(Res.Resource(text)) self.ted.WECalText() self.SetPort() viewrect, destrect = self._calctextbounds() self.ted.WESetViewRect(viewrect) self.ted.WESetDestRect(destrect) rgn = Qd.NewRgn() Qd.RectRgn(rgn, viewrect) Qd.EraseRect(viewrect) self.draw(rgn) #Win.InvalRect(self.ted.WEGetViewRect()) self.updatescrollbars() def get(self): if not self._parent: return self.temptext else: return self.ted.WEGetText().data # events def key(self, char, event): (what, message, when, where, modifiers) = event if self._enabled and not modifiers & Events.cmdKey or char in arrowkeys: self.ted.WEKey(ord(char), modifiers) if char not in navigationkeys: self.changed = 1 if char not in scrollkeys: self.selchanged = 1 self.updatescrollbars() if self._callback: Wbase.CallbackCall(self._callback, 0, char, modifiers) def click(self, point, modifiers): if not self._enabled: return self.ted.WEClick(point, modifiers, Evt.TickCount()) self.selchanged = 1 self.updatescrollbars() return 1 def idle(self): self.SetPort() self.ted.WEIdle() def rollover(self, point, onoff): if onoff: Wbase.SetCursor("iBeam") def activate(self, onoff): self._activated = onoff if self._selected and self._visible: if onoff: self.ted.WEActivate() else: self.ted.WEDeactivate() if self._selected: self.drawselframe(onoff) def select(self, onoff, isclick = 0): if Wbase.SelectableWidget.select(self, onoff): return self.SetPort() if onoff: self.ted.WEActivate() if self._parentwindow._tabbable and not isclick: self.selectall() else: self.ted.WEDeactivate() self.drawselframe(onoff) def draw(self, visRgn = None): if self._visible: if not visRgn: visRgn = self._parentwindow.wid.GetWindowPort().visRgn self.ted.WEUpdate(visRgn) if self._selected and self._activated: self.drawselframe(1) Qd.FrameRect(self._bounds) # scrolling def scrollpageup(self): if self._parent._bary and self._parent._bary._enabled: self.vscroll("++") def scrollpagedown(self): if self._parent._bary and self._parent._bary._enabled: self.vscroll("--") def scrolltop(self): if self._parent._bary and self._parent._bary._enabled: self.vscroll(0) if self._parent._barx and self._parent._barx._enabled: self.hscroll(0) def scrollbottom(self): if self._parent._bary and self._parent._bary._enabled: self.vscroll(32767) # menu handlers def domenu_copy(self, *args): selbegin, selend = self.ted.WEGetSelection() if selbegin == selend: return Scrap.ZeroScrap() self.ted.WECopy() self.updatescrollbars() def domenu_cut(self, *args): selbegin, selend = self.ted.WEGetSelection() if selbegin == selend: return Scrap.ZeroScrap() self.ted.WECut() self.updatescrollbars() self.selview() self.changed = 1 self.selchanged = 1 if self._callback: Wbase.CallbackCall(self._callback, 0, "", None) def domenu_paste(self, *args): if not self.ted.WECanPaste(): return self.selview() self.ted.WEPaste() self.updatescrollbars() self.changed = 1 self.selchanged = 1 if self._callback: Wbase.CallbackCall(self._callback, 0, "", None) def domenu_clear(self, *args): self.ted.WEDelete() self.selview() self.updatescrollbars() self.changed = 1 self.selchanged = 1 if self._callback: Wbase.CallbackCall(self._callback, 0, "", None) def domenu_undo(self, *args): which, redo = self.ted.WEGetUndoInfo() if not which: return self.ted.WEUndo() self.updatescrollbars() self.changed = 1 self.selchanged = 1 if self._callback: Wbase.CallbackCall(self._callback, 0, "", None) def can_undo(self, menuitem): which, redo = self.ted.WEGetUndoInfo() which = UNDOLABELS[which] if which == None: return None if redo: which = "Redo "+which else: which = "Undo "+which menuitem.settext(which) return 1 def domenu_selectall(self, *args): self.selectall() # private def getscrollbarvalues(self): dr = self.ted.WEGetDestRect() vr = self.ted.WEGetViewRect() vx = Wcontrols._scalebarvalue(dr[0], dr[2], vr[0], vr[2]) vy = Wcontrols._scalebarvalue(dr[1], dr[3], vr[1], vr[3]) return vx, vy def vscroll(self, value): lineheight = self.ted.WEGetHeight(0, 1) dr = self.ted.WEGetDestRect() vr = self.ted.WEGetViewRect() destheight = dr[3] - dr[1] viewheight = vr[3] - vr[1] viewoffset = maxdelta = vr[1] - dr[1] mindelta = vr[3] - dr[3] if value == "+": delta = lineheight elif value == "-": delta = - lineheight elif value == "++": delta = viewheight - lineheight elif value == "--": delta = lineheight - viewheight else: # in thumb cur = (32767 * viewoffset) / (destheight - viewheight) delta = (cur-value)*(destheight - viewheight)/32767 if abs(delta - viewoffset) <=2: # compensate for irritating rounding error delta = viewoffset delta = min(maxdelta, delta) delta = max(mindelta, delta) self.ted.WEScroll(0, delta) self.updatescrollbars() def hscroll(self, value): dr = self.ted.WEGetDestRect() vr = self.ted.WEGetViewRect() destwidth = dr[2] - dr[0] viewwidth = vr[2] - vr[0] viewoffset = maxdelta = vr[0] - dr[0] mindelta = vr[2] - dr[2] if value == "+": delta = 32 elif value == "-": delta = - 32 elif value == "++": delta = 0.5 * (vr[2] - vr[0]) elif value == "--": delta = 0.5 * (vr[0] - vr[2]) else: # in thumb cur = (32767 * viewoffset) / (destwidth - viewwidth) delta = (cur-value)*(destwidth - viewwidth)/32767 if abs(delta - viewoffset) <=2: # compensate for irritating rounding error delta = viewoffset delta = min(maxdelta, delta) delta = max(mindelta, delta) self.ted.WEScroll(delta, 0) self.updatescrollbars() # some internals def _getflags(self): flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \ WASTEconst.weDoUndo if self.readonly: flags = flags | WASTEconst.weDoReadOnly return flags def _getviewrect(self): return Qd.InsetRect(self._bounds, self.inset[0], self.inset[1]) def _calctextbounds(self): viewrect = l, t, r, b = self._getviewrect() if self.ted: dl, dt, dr, db = self.ted.WEGetDestRect() vl, vt, vr, vb = self.ted.WEGetViewRect() yshift = t - vt if (db - dt) < (b - t): destrect = viewrect else: destrect = l, dt + yshift, r, db + yshift else: destrect = viewrect return viewrect, destrect class TextEditor(EditText): def __init__(self, possize, text = "", callback = None, wrap = 1, inset = (4, 4), fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)), readonly = 0): EditText.__init__(self, possize, text, callback, inset, fontsettings, readonly) self.wrap = wrap def _getflags(self): flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoMonoStyled | \ WASTEconst.weDoOutlineHilite if self.readonly: flags = flags | WASTEconst.weDoReadOnly else: flags = flags | WASTEconst.weDoUndo return flags def _getviewrect(self): l, t, r, b = self._bounds return (l + 5, t + 2, r, b - 2) def _calctextbounds(self): if self.wrap: return EditText._calctextbounds(self) else: viewrect = l, t, r, b = self._getviewrect() if self.ted: dl, dt, dr, db = self.ted.WEGetDestRect() vl, vt, vr, vb = self.ted.WEGetViewRect() xshift = l - vl yshift = t - vt if (db - dt) < (b - t): yshift = t - dt destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift) else: destrect = (l, t, r + 5000, b) return viewrect, destrect def draw(self, visRgn = None): if self._visible: if not visRgn: visRgn = self._parentwindow.wid.GetWindowPort().visRgn self.ted.WEUpdate(visRgn) if self._selected and self._activated: self.drawselframe(1) class PyEditor(TextEditor): def __init__(self, possize, text = "", callback = None, inset = (4, 4), fontsettings = ("Python-Sans", 0, 9, (0, 0, 0)), readonly = 0, debugger = None, file = ''): TextEditor.__init__(self, possize, text, callback, 0, inset, fontsettings, readonly) self.bind("cmd[", self.shiftleft) self.bind("cmd]", self.shiftright) self.file = file # only for debugger reference self._debugger = debugger if debugger: debugger.register_editor(self, self.file) def setfile(self, file): self.file = file def set(self, text, file = ''): oldfile = self.file self.file = file if self._debugger: self._debugger.unregister_editor(self, oldfile) self._debugger.register_editor(self, file) TextEditor.set(self, text) def close(self): if self._debugger: self._debugger.unregister_editor(self, self.file) self._debugger = None TextEditor.close(self) def click(self, point, modifiers): if not self._enabled: return if self._debugger and self.pt_in_breaks(point): self.breakhit(point, modifiers) elif self._debugger: bl, bt, br, bb = self._getbreakrect() Qd.EraseRect((bl, bt, br-1, bb)) TextEditor.click(self, point, modifiers) self.drawbreakpoints() else: TextEditor.click(self, point, modifiers) if self.ted.WEGetClickCount() >= 3: # select block with our indent lines = string.split(self.get(), '\r') selstart, selend = self.ted.WEGetSelection() lineno = self.ted.WEOffsetToLine(selstart) tabs = 0 line = lines[lineno] while line[tabs:] and line[tabs] == '\t': tabs = tabs + 1 tabstag = '\t' * tabs fromline = 0 toline = len(lines) if tabs: for i in range(lineno - 1, -1, -1): line = lines[i] if line[:tabs] <> tabstag: fromline = i + 1 break for i in range(lineno + 1, toline): line = lines[i] if line[:tabs] <> tabstag: toline = i - 1 break selstart, dummy = self.ted.WEGetLineRange(fromline) dummy, selend = self.ted.WEGetLineRange(toline) self.ted.WESetSelection(selstart, selend) def breakhit(self, point, modifiers): if not self.file: return offset, edge = self.ted.WEGetOffset(point) lineno = self.ted.WEOffsetToLine(offset) + 1 if edge < 0: self._debugger.clear_breaks_above(self.file, lineno) else: self._debugger.clear_breaks_above(self.file, self.countlines()) self._debugger.toggle_break(self.file, lineno) def key(self, char, event): (what, message, when, where, modifiers) = event if modifiers & Events.cmdKey and not char in arrowkeys: return if char == '\r': selstart, selend = self.ted.WEGetSelection() selstart, selend = min(selstart, selend), max(selstart, selend) lastchar = chr(self.ted.WEGetChar(selstart-1)) if lastchar <> '\r' and selstart: pos, dummy = self.ted.WEFindLine(selstart, 0) lineres = Res.Resource('') self.ted.WECopyRange(pos, selstart, lineres, None, None) line = lineres.data + '\n' tabcount = self.extratabs(line) self.ted.WEKey(ord('\r'), 0) for i in range(tabcount): self.ted.WEKey(ord('\t'), 0) else: self.ted.WEKey(ord('\r'), 0) elif char in ')]}': self.ted.WEKey(ord(char), modifiers) self.balanceparens(char) else: self.ted.WEKey(ord(char), modifiers) if char not in navigationkeys: self.changed = 1 self.selchanged = 1 self.updatescrollbars() def balanceparens(self, char): if char == ')': target = '(' elif char == ']': target = '[' elif char == '}': target = '{' recursionlevel = 1 selstart, selend = self.ted.WEGetSelection() count = min(selstart, selend) - 2 mincount = max(0, count - 2048) lastquote = None while count > mincount: testchar = chr(self.ted.WEGetChar(count)) if testchar in "\"'" and chr(self.ted.WEGetChar(count - 1)) <> '\\': if lastquote == testchar: recursionlevel = recursionlevel - 1 lastquote = None elif not lastquote: recursionlevel = recursionlevel + 1 lastquote = testchar elif not lastquote and testchar == char: recursionlevel = recursionlevel + 1 elif not lastquote and testchar == target: recursionlevel = recursionlevel - 1 if recursionlevel == 0: import time autoscroll = self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, -1) if autoscroll: self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 0) self.ted.WESetSelection(count, count + 1) time.sleep(0.2) self.ted.WESetSelection(selstart, selend) if autoscroll: self.ted.WEFeatureFlag(WASTEconst.weFAutoScroll, 1) break count = count - 1 def extratabs(self, line): tabcount = 0 for c in line: if c <> '\t': break tabcount = tabcount + 1 last = 0 cleanline = '' tags = PyFontify.fontify(line) # strip comments and strings for tag, start, end, sublist in tags: if tag in ('string', 'comment'): cleanline = cleanline + line[last:start] last = end cleanline = cleanline + line[last:] cleanline = string.strip(cleanline) if cleanline and cleanline[-1] == ':': tabcount = tabcount + 1 else: for open, close in (('(', ')'), ('[', ']'), ('{', '}')): count = string.count(cleanline, open) if count and count <> string.count(cleanline, close): tabcount = tabcount + 2 break return tabcount def rollover(self, point, onoff): if onoff: if self._debugger and self.pt_in_breaks(point): Wbase.SetCursor("arrow") else: Wbase.SetCursor("iBeam") def draw(self, visRgn = None): TextEditor.draw(self, visRgn) if self._debugger: self.drawbreakpoints() def showbreakpoints(self, onoff): if (not not self._debugger) <> onoff: if onoff: import PyDebugger self._debugger = PyDebugger.getdebugger() self._debugger.register_editor(self, self.file) elif self._debugger: self._debugger.unregister_editor(self, self.file) self._debugger = None self.adjust(self._bounds) def togglebreakpoints(self): self.showbreakpoints(not self._debugger) def clearbreakpoints(self): if self.file: self._debugger.clear_all_file_breaks(self.file) def editbreakpoints(self): if self._debugger: self._debugger.edit_breaks() self._debugger.breaksviewer.selectfile(self.file) def drawbreakpoints(self, eraseall = 0): breakrect = bl, bt, br, bb = self._getbreakrect() br = br - 1 self.SetPort() Qd.PenPat(Qd.qd.gray) Qd.PaintRect((br, bt, br + 1, bb)) Qd.PenNormal() self._parentwindow.tempcliprect(breakrect) Qd.RGBForeColor((0xffff, 0, 0)) try: lasttop = bt self_ted = self.ted Qd_PaintOval = Qd.PaintOval Qd_EraseRect = Qd.EraseRect for lineno in self._debugger.get_file_breaks(self.file): start, end = self_ted.WEGetLineRange(lineno - 1) if lineno <> self_ted.WEOffsetToLine(start) + 1: # breakpoints beyond our text: erase rest, and back out Qd_EraseRect((bl, lasttop, br, bb)) break (x, y), h = self_ted.WEGetPoint(start, 0) bottom = y + h #print y, (lasttop, bottom) if bottom > lasttop: Qd_EraseRect((bl, lasttop, br, y + h * eraseall)) lasttop = bottom redbullet = bl + 2, y + 3, bl + 8, y + 9 Qd_PaintOval(redbullet) else: Qd_EraseRect((bl, lasttop, br, bb)) Qd.RGBForeColor((0, 0, 0)) finally: self._parentwindow.restoreclip() def updatescrollbars(self): if self._debugger: self.drawbreakpoints(1) TextEditor.updatescrollbars(self) def pt_in_breaks(self, point): return Qd.PtInRect(point, self._getbreakrect()) def _getbreakrect(self): if self._debugger: l, t, r, b = self._bounds return (l+1, t+1, l + 12, b-1) else: return (0, 0, 0, 0) def _getviewrect(self): l, t, r, b = self._bounds if self._debugger: return (l + 17, t + 2, r, b - 2) else: return (l + 5, t + 2, r, b - 2) def _calctextbounds(self): viewrect = l, t, r, b = self._getviewrect() if self.ted: dl, dt, dr, db = self.ted.WEGetDestRect() vl, vt, vr, vb = self.ted.WEGetViewRect() xshift = l - vl yshift = t - vt if (db - dt) < (b - t): yshift = t - dt destrect = (dl + xshift, dt + yshift, dr + xshift, db + yshift) else: destrect = (l, t, r + 5000, b) return viewrect, destrect def GetFNum(font): if font <> 'Chicago': fontid = Fm.GetFNum(font) if fontid == 0: fontid = Fonts.monaco else: fontid = 0 return fontid def GetFName(fontid): try: res = Res.GetResource('FOND', fontid) except Res.Error: fontname = 'Monaco' else: fontname = res.GetResInfo()[2] return fontname