def parseHeadline(s): """ Parse a headline of the form @kind:name=val Return (kind,name,val). Leo 4.11.1: Ignore everything after @data name. """ kind = name = val = None if g.match(s, 0, g.u('@')): i = g.skip_id(s, 1, chars=g.u('-')) i = g.skip_ws(s, i) kind = s[1: i].strip() if kind: # name is everything up to '=' if kind == g.u('data'): # i = g.skip_ws(s,i) j = s.find(g.u(' '), i) if j == -1: name = s[i:].strip() else: name = s[i: j].strip() else: j = s.find(g.u('='), i) if j == -1: name = s[i:].strip() else: name = s[i: j].strip() # val is everything after the '=' val = s[j + 1:].strip() # g.trace("%50s %10s %s" %(name,kind,val)) return kind, name, val
def toMarkdown(self): references = '' i = 1 # doc = QString() # the full document doc = g.u('') block = self.document().begin( ) # block is like a para; text fragment is sequence of same char format while block.isValid(): #print "block=",block.text() if block.blockFormat().nonBreakableLines(): doc += ' ' + block.text() + '\n' #elif block.textList(): #textList = block.textList() #print block.textList().count() #print g.u(block.textList().itemText(block)) #print block.textList().itemNumber(block) #print block.textList().item(block.textList().itemNumber(block)).text() #doc += textList.itemText(block) + ' ' + textList.item(textList.itemNumber(block)).text() + '\n\n' else: if block.textList(): doc += ' ' + block.textList().itemText(block) + ' ' # para = QString() para = g.u('') iterator = block.begin() while iterator != block.end(): fragment = iterator.fragment() if fragment.isValid(): char_format = fragment.charFormat() text = g.u(Qt.escape(fragment.text()) ) # turns chars like < into entities < font_size = char_format.font().pointSize() # a fragment can only be an anchor, italics or bold if char_format.isAnchor(): ref = text if text.startswith( 'http://') else 'http://{0}'.format(text) # too lazy right now to check if URL has already been referenced but should references += " [{0}]: {1}\n".format(i, ref) text = "[{0}][{1}]".format(text, i) i += 1 elif font_size > 10: if font_size > 15: text = '#{0}'.format(text) elif font_size > 12: text = '##{0}'.format(text) else: text = '###{0}'.format(text) elif char_format.fontFixedPitch( ): #or format.fontFamily=='courier': text = QString("`%1`").arg(text) elif char_format.fontItalic(): text = QString("*%1*").arg(text) elif char_format.fontWeight( ) > QFont.Normal: #font-weight:600; same as for an H1; H1 font-size:xx-large; H1 20; H2 15 H3 12 text = QString("**%1**").arg(text) para += text iterator += 1 doc += para + '\n\n' block = block.next() return doc + references
def parseHeadline(s): """ Parse a headline of the form @kind:name=val Return (kind,name,val). Leo 4.11.1: Ignore everything after @data name. """ kind = name = val = None if g.match(s, 0, g.u('@')): i = g.skip_id(s, 1, chars=g.u('-')) i = g.skip_ws(s, i) kind = s[1:i].strip() if kind: # name is everything up to '=' if kind == g.u('data'): # i = g.skip_ws(s,i) j = s.find(g.u(' '), i) if j == -1: name = s[i:].strip() else: name = s[i:j].strip() else: j = s.find(g.u('='), i) if j == -1: name = s[i:].strip() else: name = s[i:j].strip() # val is everything after the '=' val = s[j + 1:].strip() # g.trace("%50s %10s %s" %(name,kind,val)) return kind, name, val
def toMarkdown(self): references = '' i = 1 # doc = QString() # the full document doc = g.u('') block = self.document().begin() # block is like a para; text fragment is sequence of same char format while block.isValid(): #print "block=",block.text() if block.blockFormat().nonBreakableLines(): doc += ' '+block.text()+'\n' #elif block.textList(): #textList = block.textList() #print block.textList().count() #print g.u(block.textList().itemText(block)) #print block.textList().itemNumber(block) #print block.textList().item(block.textList().itemNumber(block)).text() #doc += textList.itemText(block) + ' ' + textList.item(textList.itemNumber(block)).text() + '\n\n' else: if block.textList(): doc += ' '+block.textList().itemText(block) + ' ' # para = QString() para = g.u('') iterator = block.begin() while iterator != block.end(): fragment = iterator.fragment() if fragment.isValid(): char_format = fragment.charFormat() # pylint: disable=no-member # EKR: I'm not sure whether this warning is valid. # I'm going to kill it because this is an experimental plugin. text = g.u(Qt.escape(fragment.text())) # turns chars like < into entities < font_size = char_format.font().pointSize() # a fragment can only be an anchor, italics or bold if char_format.isAnchor(): ref = text if text.startswith('http://') else 'http://{0}'.format(text) # too lazy right now to check if URL has already been referenced but should references += " [{0}]: {1}\n".format(i,ref) text = "[{0}][{1}]".format(text,i) i+=1 elif font_size > 10: if font_size > 15: text = '#{0}'.format(text) elif font_size > 12: text = '##{0}'.format(text) else: text = '###{0}'.format(text) elif char_format.fontFixedPitch(): #or format.fontFamily=='courier': text = QString("`%1`").arg(text) elif char_format.fontItalic(): text = QString("*%1*").arg(text) elif char_format.fontWeight() > QFont.Normal: #font-weight:600; same as for an H1; H1 font-size:xx-large; H1 20; H2 15 H3 12 text = QString("**%1**").arg(text) para += text iterator += 1 doc += para+'\n\n' block = block.next() return doc+references
def qtKey(self, event): ''' Return the components of a Qt key event. Modifiers are handled separately. Return (keynum, text, toString, ch). keynum: event.key() ch: g.u(chr(keynum)) or '' if there is an exception. toString: For special keys: made-up spelling that become part of the setting. For all others: QtGui.QKeySequence(keynum).toString() text: event.text() ''' keynum = event.key() text = event.text() # This is the unicode character! qt = QtCore.Qt d = { qt.Key_Alt: 'Key_Alt', qt.Key_AltGr: 'Key_AltGr', # On Windows, when the KeyDown event for this key is sent, # the Ctrl+Alt modifiers are also set. qt.Key_Control: 'Key_Control', # MacOS: Command key qt.Key_Meta: 'Key_Meta', # MacOS: Control key, Alt-Key on Microsoft keyboard on MacOs. qt.Key_Shift: 'Key_Shift', qt.Key_NumLock: 'Num_Lock', # 868. } if d.get(keynum): if 0: # Allow bare modifier key. toString = d.get(keynum) else: toString = '' else: toString = QtGui.QKeySequence(keynum).toString() # Fix bug 1244461: Numpad 'Enter' key does not work in minibuffer if toString == 'Enter': toString = 'Return' if toString == 'Esc': toString = 'Escape' try: ch1 = chr(keynum) except ValueError: ch1 = '' try: ch = g.u(ch1) except UnicodeError: ch = ch1 text = g.u(text) toString = g.u(toString) return keynum, text, toString, ch
def mouseReleaseEvent(self, event): #print("mouseReleaseEvent") pos = event.pos() url = g.u(self.anchorAt(pos)) if url: if not url.startswith('http://'): #linux seems to need this url = 'http://{0}'.format(url) webbrowser.open(g.u(url),new=2,autoraise=True) else: QTextEdit.mouseReleaseEvent(self,event)
def qtKey(self, event): ''' Return the components of a Qt key event. Modifiers are handled separately. Return keynum,text,toString,ch keynum: event.key() ch: g.u(chr(keynum)) or '' if there is an exception. toString: For special keys: made-up spelling that become part of the setting. For all others: QtGui.QKeySequence(keynum).toString() text: event.text() ''' trace = False and not g.unitTesting keynum = event.key() text = event.text() # This is the unicode text. qt = QtCore.Qt d = { qt.Key_Shift: 'Key_Shift', qt.Key_Control: 'Key_Control', # MacOS: Command key qt.Key_Meta: 'Key_Meta', # MacOS: Control key, Alt-Key on Microsoft keyboard on MacOs. qt.Key_Alt: 'Key_Alt', qt.Key_AltGr: 'Key_AltGr', # On Windows, when the KeyDown event for this key is sent, # the Ctrl+Alt modifiers are also set. } if d.get(keynum): toString = d.get(keynum) else: toString = QtGui.QKeySequence(keynum).toString() # Fix bug 1244461: Numpad 'Enter' key does not work in minibuffer if toString == 'Enter': toString = 'Return' try: ch1 = chr(keynum) except ValueError: ch1 = '' try: ch = g.u(ch1) except UnicodeError: ch = ch1 text = g.u(text) toString = g.u(toString) if trace and self.keyIsActive: mods = '+'.join(self.qtMods(event)) g.trace( 'keynum %7x ch %3s toString %s %s' % ( keynum, repr(ch), mods, repr(toString))) return keynum, text, toString, ch
def returnPressed(self): t = g.u(self.ui.lineEdit.text()) if not t.strip(): return if t == g.u('m'): self.scon.doShowMarked() else: self.scon.doSearch(t) if self.scon.its: self.ui.listWidget.blockSignals(True) # don't jump to first hit self.ui.listWidget.setFocus() self.ui.listWidget.blockSignals(False) # ok, respond if user moves
def walk_tree(node, vns=None, seq=None): if vns is None: vns = {} if seq is None: seq = [] if not node.gnx in seq: seq.append(node.gnx) pgnx = node.parent.gnx v = vns.get(node.gnx, (node.h, g.u('').join(node.b), g.u(' ').join( n.gnx for n in node.children), [], 1 if node.b else 0, pickle.dumps(node.u))) v[3].append(pgnx) vns[node.gnx] = v for n in node.children: walk_tree(n, vns, seq) return vns, seq
def returnPressed(self): self.scon.freeze() t = g.u(self.ui.lineEdit.text()) if not t.strip(): return if t == g.u('m'): self.scon.doShowMarked() else: self.scon.doSearch(t) if self.scon.its: self.ui.listWidget.blockSignals(True) # don't jump to first hit self.ui.listWidget.setFocus() self.ui.listWidget.blockSignals(False) # ok, respond if user moves
def showColorWheel(self, event=None): '''Show a Qt color dialog.''' c = self.c p = c.p picker = QtWidgets.QColorDialog() in_color_setting = p.h.startswith('@color ') try: text = QtWidgets.QApplication.clipboard().text() if in_color_setting: text = p.h.split('=', 1)[1].strip() color = QtGui.QColor(text) picker.setCurrentColor(color) except (ValueError, IndexError) as e: g.trace('error caught', e) if not picker.exec_(): g.es("No color selected") elif in_color_setting: udata = c.undoer.beforeChangeNodeContents(p) p.h = '%s = %s' % (p.h.split( '=', 1)[0].strip(), g.u(picker.selectedColor().name())) c.undoer.afterChangeNodeContents(p, 'change-color', udata) else: text = picker.selectedColor().name() g.es("copied to clipboard:", text) QtWidgets.QApplication.clipboard().setText(text)
def update_jupyter(self, s, keywords): '''Update @jupyter node in the vr pane.''' pc = self c = pc.c if pc.must_change_widget(QtWebKitWidgets.QWebView): # g.trace('===== instantiating QWebView') w = QtWebKitWidgets.QWebView() n = c.config.getInt('qweb_view_font_size') if n: settings = w.settings() settings.setFontSize(settings.DefaultFontSize, n) pc.embed_widget(w) assert(w == pc.w) else: w = pc.w url = g.getUrlFromNode(c.p) if url and nbformat: s = urlopen(url).read().decode() try: nb = nbformat.reads(s, as_version=4) e = HTMLExporter() (s, junk_resources) = e.from_notebook_node(nb) except nbformat.reader.NotJSONError: # Assume the result is html. pass elif url: s = 'can not import nbformt: %r' % url else: s = g.u('') if isQt5: w.hide() # This forces a proper update. w.setHtml(s) w.show() c.bodyWantsFocusNow()
def liveUpdate(self): t = g.u(self.ui.lineEdit.text()) if not t.strip(): if self.scon.frozen: self.scon.freeze(False) self.scon.clear() return if len(t) < 3: return if self.scon.frozen: return if t == g.u('m'): self.scon.doShowMarked() return self.scon.worker.set_input(t)
def create_nodes(self, parent, parent_d): '''Create the tree of nodes rooted in parent.''' import pprint trace = False and not g.unitTesting d = self.gnx_dict if trace: g.trace(parent.h, pprint.pprint(parent_d)) for child_gnx in parent_d.get('children'): d2 = d.get(child_gnx) if trace: g.trace('child', pprint.pprint(d2)) if child_gnx in self.vnodes_dict: # It's a clone. v = self.vnodes_dict.get(child_gnx) n = parent.numberOfChildren() child = leoNodes.Position(v) child._linkAsNthChild(parent, n) # Don't create children again. else: child = parent.insertAsLastChild() child.h = d2.get('h') or '<**no h**>' child.b = d2.get('b') or g.u('') if d2.get('gnx'): child.v.findIndex = gnx = d2.get('gnx') self.vnodes_dict[gnx] = child.v if d2.get('ua'): child.u = d2.get('ua') self.create_nodes(child, d2)
def showColorWheel(self, event=None): '''Show a Qt color dialog.''' c = self.c; p = c.p picker = QtWidgets.QColorDialog() in_color_setting = p.h.startswith('@color ') try: text = QtWidgets.QApplication.clipboard().text() if in_color_setting: text = p.h.split('=', 1)[1].strip() color = QtGui.QColor(text) picker.setCurrentColor(color) except (ValueError, IndexError) as e: g.trace('error caught', e) pass if not picker.exec_(): g.es("No color selected") elif in_color_setting: udata = c.undoer.beforeChangeNodeContents(p) p.h = '%s = %s'%(p.h.split('=', 1)[0].strip(), g.u(picker.selectedColor().name())) c.undoer.afterChangeNodeContents(p, 'change-color', udata) else: text = picker.selectedColor().name() g.es("copied to clipboard:", text) QtWidgets.QApplication.clipboard().setText(text)
def focusin(): if v is c.p.v: if v.b.encode('utf-8') != nf.toPlainText(): # only when needed to avoid scroll jumping nf.setPlainText(g.u(v.b)) nf.setWindowTitle(v.h) nf.dirty = False
def showFonts(self, event=None): '''Open a tab in the log pane showing a font picker.''' c = self.c; p = c.p picker = QtWidgets.QFontDialog() if p.h.startswith('@font'): (name, family, weight, slant, size) = leoConfig.parseFont(p.b) else: name, family, weight, slant, size = None, None, False, False, 12 try: font = QtGui.QFont() if family: font.setFamily(family) font.setBold(weight) font.setItalic(slant) font.setPointSize(size) picker.setCurrentFont(font) except ValueError: pass if not picker.exec_(): g.es("No font selected") else: font = picker.selectedFont() udata = c.undoer.beforeChangeNodeContents(p) comments = [x for x in g.splitLines(p.b) if x.strip().startswith('#')] defs = [ '\n' if comments else '', '%s_family = %s\n'%(name, font.family()), '%s_weight = %s\n'%(name, 'bold' if font.bold() else 'normal'), '%s_slant = %s\n'%(name, 'italic' if font.italic() else 'roman'), '%s_size = %s\n'%(name, font.pointSizeF()) ] p.b = g.u('').join(comments + defs) c.undoer.afterChangeNodeContents(p, 'change-font', udata)
def properties(self, event=None): """Display a modal properties dialog for this plugin""" if self.hasapply: def callback(name, data): self.updateConfiguration(data) self.mod.applyConfiguration(self.config) self.writeConfiguration() buttons = ['Apply'] else: callback = None buttons = [] self.config = config = ConfigParser.ConfigParser() config.read(self.configfilename) # Load config data into dictionary of dictianaries. # Do no allow for nesting of sections. data = {} for section in config.sections(): options = {} for option in config.options(section): #g.pr('config', section, option ) options[option] = g.u(config.get(section, option)) data[section] = options # Save the original config data. This will not be changed. self.sourceConfig = data # Open a modal dialog and wait for it to return. # Provide the dialog with a callback for the 'Appply' function. title = "Properties of " + self.name result, data = g.app.gui.runPropertiesDialog(title, data, callback, buttons) if result != 'Cancel' and data: self.updateConfiguration(data) self.writeConfiguration()
def get_data(node): data = blines(node) for x in subtree(node): if x.b and not x.h.startswith('@'): data.extend(blines(x)) if not x.b[-1].endswith('\n'): data.append(g.u('\n')) return data
def end (self,completion=''): body = self.body cpl = self.completer if not completion: completion = g.u(cpl.currentCompletion()) if completion: cmpl = g.u(completion).split(None,1)[0] cmpl = g.u(cmpl) prefix = g.u(cpl.completionPrefix()) tc = body.textCursor() extra = len(cmpl) - len(prefix) tc.movePosition(tc.Left) tc.movePosition(tc.EndOfWord) tc.insertText(cmpl[-extra:]) body.setTextCursor(tc) self.kill()
def do_edit_h(): p, w = self.get_current_pos() new, r = QInputDialog.getText(None, "Edit headline", "", QLineEdit.Normal, p.h) if not r: return new = g.u(new) p.h = new w.setWindowTitle(new)
def returnPressed(self): w = self.ui.listWidget self.scon.freeze() t = g.u(self.ui.lineEdit.text()) if not t.strip(): return # Handle Easter eggs. if t == g.u('m'): self.scon.doShowMarked() elif t == g.u('h'): self.scon.doSearchHistory() else: self.scon.doSearch(t) if self.scon.its: w.blockSignals(True) # don't jump to first hit w.setFocus() w.blockSignals(False)
def focusout(): #print "focus out" if not nf.dirty: return v.b = g.u(nf.toPlainText()) v.setDirty() nf.dirty = False p = c.p if p.v is v: c.selectPosition(c.p)
def focusout(): # print("focus out") if not nf.dirty: return v.b = g.u(nf.toHtml()) v.setDirty() nf.dirty = False p = c.p if p.v is v: c.selectPosition(c.p)
def focusout(): if nf.dirty: v.b = g.u(nf.toHtml()) v.setDirty() nf.dirty = False p = c.p if p.v is v: c.selectPosition(c.p) # Fix #249: Leo and Stickynote plugin do not request to save c.setChanged(True) c.redraw()
def focusout(): if nf.dirty: if v.b.encode('utf-8') != nf.toPlainText(): v.b = g.u(nf.toPlainText()) v.setDirty() # Fix #249: Leo and Stickynote plugin do not request to save c.setChanged(True) nf.dirty = False p = c.p if p.v is v: c.selectPosition(c.p) c.redraw()
def insertFromMimeData(self, source): # not sure really necessary since it actually appears to paste URLs correctly # I am stripping the http print("Paste") text = g.u(source.text()) if len(text.split())==1 and (text.startswith('http://') or 'www' in text or '.com' in text or '.html' in text): if text.startswith('http://'): text = '<a href="{0}">{1}</a> '.format(text, text[7:]) else: text = '<a href="http://{0}">{0}</a> '.format(text) self.insertHtml(text) else: QTextEdit.insertFromMimeData(self, source)
def __init__(self, *args, **kwargs): """Set ivars""" super().__init__(*args, **kwargs) self.root = LeoNode() self.root.h = g.u('ROOT') # changes type from [] to str, done by endElement() for other vnodes self.cur = self.root self.idx = {} self.in_ = None self.in_attrs = {} self.path = []
def __init__(self, *args, **kwargs): """Set ivars""" ContentHandler.__init__(self, *args, **kwargs) self.root = LeoNode() self.root.h = g.u('ROOT') # changes type from [] to str, done by endElement() for other vnodes self.cur = self.root self.idx = {} self.in_ = None self.in_attrs = {} self.path = []
def walk_tree(node, vns=None, seq=None): if vns is None: vns = {} if seq is None: seq = [] if not node.gnx in seq: seq.append(node.gnx) pgnx = node.parent.gnx v = vns.get(node.gnx, ( node.h, g.u('').join(node.b), g.u(' ').join(n.gnx for n in node.children), [], 1 if node.b else 0, pickle.dumps(node.u) )) v[3].append(pgnx) vns[node.gnx] = v for n in node.children: walk_tree(n, vns, seq) return vns, seq
def main(src, dest): print('src', src) print('dest', dest) root = get_leo_data(g.readFileIntoEncodedString(src)) root.gnx = 'hidden-root-vnode-gnx' vns, seq = walk_tree(root) data = vnode_data(vns, seq[1:]) # skip hidden root with sqlite3.connect(dest) as conn: resetdb(conn) conn.executemany(sqls('insert-vnode'), data) conn.commit() acc = [] settings_harvester(root, [], acc) for gnx, kind, name, value, cond in acc: if kind == g.u('data'): value = repr(value)[:30] print(cond or "always", kind, name, pprint.pformat(value))
def settings_harvester(node, conds, acc): if conds: if isSimpleSetting(node): harvest_one_simple_setting(node, conds, acc) elif isCondition(node): descend(node, conds + [node], acc) elif isComplexSetting(node): harvest_one_complex_setting(node, conds, acc) elif node.h.startswith('@ignore'): pass else: descend(node, conds, acc) elif node.h.startswith('@ignore'): pass elif node.h.startswith(g.u('@settings')): descend(node, [True], acc) else: descend(node, conds, acc)
def create_anchor(self): cursor = self.textCursor() if not cursor.hasSelection(): return text = g.u(cursor.selectedText()) # need unicode for if below if text.startswith('http://'): text = '<a href="{0}">{1}</a> '.format(text, text[7:]) else: text = '<a href="http://{0}">{0}</a> '.format(text) # the below works but doesn't pick up highlighting of an anchor - would have to do the underlining and blue color #format = QTextCharFormat() #format.setAnchor(True) #format.setAnchorHref(text) #cursor.setCharFormat(format) ##self.setTextCursor(cursor) #this also works and generates highlighting cursor.deleteChar() cursor.insertHtml(text) # also self.insertHtml should work
def endElement(self, name): """decode unknownAttributes when t element is done""" self.in_ = None # could maintain a stack, but we only need to know for # character collection, so it doesn't matter if name == 'v': self.cur.h = g.u('').join(self.cur.h) self.cur = self.cur.parent if self.path: del self.path[-1] if name == 't': nd = self.idx[self.in_attrs['tx']] for k in nd.u: s = nd.u[k] if not k.startswith('str_'): try: s = loads(unhexlify(s)) except Exception: pass nd.u[k] = s
def addButton(self, which, type_="move", v=None, parent=None): '''Add a button that creates a target for future moves.''' c = self.c p = c.p if v is None: v = p.v sc = scriptingController(c) mb = quickMoveButton(self, v, which, type_=type_) txt = self.txts[type_] if parent: # find parent button for i in self.buttons: if i[0].target.gnx == parent: parent = i[1] break else: g.es('Move to button parent not found, placing at top level') parent = None header = v.anyAtFileNodeName() or v.h # drop @auto etc. text = txt + ":" + header if txt else header # createButton truncates text. if parent and g.app.gui.guiName() == "qt": pb = parent.button rc = QtWidgets.QAction(text, pb) rc.triggered.connect(mb.moveCurrentNodeToTarget) pb.insertAction(pb.actions()[0], rc) # insert at top b = None mb.has_parent = True t = QtCore.QString( c.config.getString('mod_scripting_subtext') or '') if not g.u(pb.text()).endswith(g.u(t)): pb.setText(pb.text() + t) else: b = sc.createIconButton( args=None, text=text, command=mb.moveCurrentNodeToTarget, statusLine='%s current node to %s child of %s' % (type_.title(), which, v.h), kind="quick-move") if g.app.gui.guiName() == "qt": def cb_goto_target(checked, c=c, v=v): p = c.vnode2position(v) c.selectPosition(p) c.redraw() def cb_set_parent(checked, c=c, v=v, first=which, type_=type_): c.quickMove.set_parent(v, first, type_) def cb_permanent(checked, c=c, v=v, type_=type_, first=which): c.quickMove.permanentButton(v=v, type_=type_, first=first) # def cb_clear(event=None, c=c, v=v): # c.quickMove.clearButton(v) for cb, txt in [ (cb_goto_target, 'Goto target'), (cb_permanent, 'Make permanent'), # (cb_clear, 'Clear permanent'), (cb_set_parent, 'Set parent'), ]: but = b.button rc = QtWidgets.QAction(txt, but) rc.triggered.connect(cb) but.insertAction(but.actions()[-1], rc) # insert rc before Remove Button self.buttons.append((mb, b))
def focusin(): if v is c.p.v: nf.setHtml(g.u(v.b)) nf.setWindowTitle(p.h) nf.dirty = False
def onHeadChanged (self,p,undoType='Typing',s=None,e=None): '''Officially change a headline.''' trace = False and not g.unitTesting verbose = False c = self.c ; u = c.undoer if not p: if trace: g.trace('** no p') return item = self.getCurrentItem() if not item: if trace and verbose: g.trace('** no item') return if not e: e = self.getTreeEditorForItem(item) if not e: if trace and verbose: g.trace('** not editing') return s = g.u(e.text()) if g.doHook("headkey1",c=c,p=c.p,v=c.p,s=s): return self.closeEditorHelper(e,item) oldHead = p.h changed = s != oldHead if changed: # New in Leo 4.10.1. if trace: g.trace('new',repr(s),'old',repr(p.h)) #@+<< truncate s if it has multiple lines >> #@+node:ekr.20120409185504.10028: *4* << truncate s if it has multiple lines >> # Remove trailing newlines before warning of truncation. while s and s[-1] == '\n': s = s[:-1] # Warn if there are multiple lines. i = s.find('\n') if i > -1: s = s[:i] if s != oldHead: g.warning("truncating headline to one line") limit = 1000 if len(s) > limit: s = s[:limit] if s != oldHead: g.warning("truncating headline to",limit,"characters") #@-<< truncate s if it has multiple lines >> p.initHeadString(s) item.setText(0,s) # Required to avoid full redraw. undoData = u.beforeChangeNodeContents(p,oldHead=oldHead) if not c.changed: c.setChanged(True) # New in Leo 4.4.5: we must recolor the body because # the headline may contain directives. c.frame.body.recolor(p,incremental=True) dirtyVnodeList = p.setDirty() u.afterChangeNodeContents(p,undoType,undoData, dirtyVnodeList=dirtyVnodeList) g.doHook("headkey2",c=c,p=c.p,v=c.p,s=s) # This is a crucial shortcut. if g.unitTesting: return if changed: self.redraw_after_head_changed() if 0: # Don't do this: it interferes with clicks, and is not needed. if self.stayInTree: c.treeWantsFocus() else: c.bodyWantsFocus() p.v.contentModified() c.outerUpdate()