def embed(self): """called from ns_do_context - embed layout in outline's @settings, an alternative to the Load/Save named layout system """ # Careful: we could be unit testing. top_splitter = self.get_top_splitter() if not top_splitter: return c = self.c layout=top_splitter.get_saveable_layout() nd = g.findNodeAnywhere(c, "@data free-layout-layout") if not nd: settings = g.findNodeAnywhere(c, "@settings") if not settings: settings = c.rootPosition().insertAfter() settings.h = "@settings" nd = settings.insertAsNthChild(0) nd.h = "@data free-layout-layout" nd.b = json.dumps(layout, indent=4) nd = nd.parent() if not nd or nd.h != "@settings": g.es("WARNING: @data free-layout-layout node is not " \ "under an active @settings node") c.redraw()
def openwith_rclick_cb(): #print "Editing", path, fname if editor: cmd = '%s "%s"' % (editor, absp) g.es('Edit: %s' % cmd) subprocess.Popen(cmd, shell=True)
def init_env(self): ''' Init c.abbrev_subst_env by executing the contents of the @data abbreviations-subst-env node. ''' c = self.c at = c.atFileCommands if c.abbrev_place_start and self.enabled: aList = self.subst_env script = [] for z in aList: # Compatibility with original design. if z.startswith('\\:'): script.append(z[2:]) else: script.append(z) script = ''.join(script) # Allow Leo directives in @data abbreviations-subst-env trees. import leo.core.leoNodes as leoNodes v = leoNodes.VNode(context=c) root = leoNodes.Position(v) # Similar to g.getScript. script = at.writeFromString( root=root, s=script, forcePythonSentinels=True, useSentinels=False) script = script.replace("\r\n", "\n") try: exec(script, c.abbrev_subst_env, c.abbrev_subst_env) except Exception: g.es('Error exec\'ing @data abbreviations-subst-env') g.es_exception() else: c.abbrev_subst_start = False
def find(self, event=None): """Find the next unknown word.""" trace = False and not g.unitTesting trace_lookup = False trace_end_body = False if not self.loaded: return c, n, p = self.c, 0, self.c.p if trace: g.trace('entry', p.h) sc = self.spellController w = c.frame.body.wrapper c.selectPosition(p) s = w.getAllText().rstrip() ins = w.getInsertPoint() # New in Leo 5.3: use regex to find words. last_p = p.copy() while True: for m in self.re_word.finditer(s[ins:]): start, word = m.start(0), m.group(0) if word in self.seen: continue n += 1 # Ignore the word if numbers precede or follow it. # Seems difficult to do this in the regex itself. k1 = ins + start - 1 if k1 >= 0 and s[k1].isdigit(): continue k2 = ins + start + len(word) if k2 < len(s) and s[k2].isdigit(): continue if trace and trace_lookup: g.trace('lookup', word) alts = sc.processWord(word) if alts: if trace: g.trace('%s searches' % n) self.currentWord = word i = ins + start j = i + len(word) self.showMisspelled(p) self.tab.fillbox(alts, word) c.invalidateFocus() c.bodyWantsFocus() w.setSelectionRange(i, j, insert=j) w.see(j) return else: self.seen.add(word) # No more misspellings in p if trace and trace_end_body: g.trace('----- end of text', p.h) p.moveToThreadNext() if p: ins = 0 s = p.b else: if trace: g.trace('%s searches' % n) g.es("no more misspellings") c.selectPosition(last_p) self.tab.fillbox([]) c.invalidateFocus() c.bodyWantsFocus() return
def WriteOutputLine(lineString): 'Writes a line of text to the output file.' try: ascFile.write("%s\n" % lineString) except IOError: g.es("Could not write to output file: %s" % ascFile.name) statusOfWriteOutputLine = CV.END_PROGRAM
def next_place(self, s, offset=0): """ Given string s containing a placeholder like <| block |>, return (s2,start,end) where s2 is s without the <| and |>, and start, end are the positions of the beginning and end of block. """ trace = False c = self.c new_pos = s.find(c.abbrev_place_start, offset) new_end = s.find(c.abbrev_place_end, offset) if (new_pos < 0 or new_end < 0) and offset: new_pos = s.find(c.abbrev_place_start) new_end = s.find(c.abbrev_place_end) if not (new_pos < 0 or new_end < 0): g.es("Found placeholder earlier in body") if new_pos < 0 or new_end < 0: if trace: g.trace('new_pos', new_pos, 'new_end', new_end) return s, None, None start = new_pos place_holder_delim = s[new_pos: new_end + len(c.abbrev_place_end)] place_holder = place_holder_delim[ len(c.abbrev_place_start): -len(c.abbrev_place_end)] s2 = s[: start] + place_holder + s[start + len(place_holder_delim):] end = start + len(place_holder) if trace: g.trace(start, end, g.callers()) return s2, start, end
def toggleAbbrevMode(self, event=None): '''Toggle abbreviation mode.''' k = self.c.k k.abbrevOn = not k.abbrevOn k.keyboardQuit() if not g.unitTesting and not g.app.batchMode: g.es('Abbreviations are ' + 'on' if k.abbrevOn else 'off')
def reclassify(self): """change priority codes""" g.es('\n Current distribution:') self.showDist() dat = {} for end in 'from', 'to': x0 = g.app.gui.runAskOkCancelStringDialog( self.c,'Reclassify priority' ,'%s priorities (1-7,19)' % end.upper()) try: x0 = [int(i) for i in x0.replace(',',' ').split() if int(i) in self.todo_priorities] except: g.es('Not understood, no action') return if not x0: g.es('No action') return dat[end] = x0 if len(dat['from']) != len(dat['to']): g.es('Unequal list lengths, no action') return cnt = 0 for p in self.pickleP.subtree(): pri = int(self.getat(p.v, 'priority')) if pri in dat['from']: self.setat(p.v, 'priority', dat['to'][dat['from'].index(pri)]) self.loadIcons(p) cnt += 1 g.es('\n%d priorities reclassified, new distribution:' % cnt) self.showDist() if cnt: self.c.redraw_now()
def jump_to_error_internal(c): import re regex = re.compile(r' File "(.*)", line (\d+), in') lines = c.leo_screen.get_all(c) lines = lines.split('\n') skipped = 0 for i in reversed(lines): match = regex.match(i) if match: if skipped == c.leo_screen.stack_frame: g.es("Line %s in %s"%(match.group(2), match.group(1))) filename = g.os_path_basename(match.group(1)) for p in c.all_unique_positions(): if p.h.startswith('@') and p.h.endswith(filename): c.selectPosition(p) c.goToLineNumber(c).go(n=int(match.group(2))) c.bodyWantsFocusNow() break break skipped += 1 else: g.es("%d error frames found in console content"%skipped)
def find_todo(self, p, stage = 0): """Recursively find the next todo""" # search is like XPath 'following' axis, all nodes after p in document order. # returning True should always propogate all the way back up to the top # stages: 0 - user selected start node, 1 - searching siblings, parents siblings, 2 - searching children if not p: return True # not required - safety net # see if this node is a todo if stage != 0 and self.getat(p.v, 'priority') in self.todo_priorities: if p.getParent(): self.c.selectPosition(p.getParent()) self.c.expandNode() self.c.selectPosition(p) self.c.redraw() return True for nd in p.children(): if self.find_todo(nd, stage = 2): return True if stage < 2 and p.getNext(): if self.find_todo(p.getNext(), stage = 1): return True if stage < 2 and p.getParent() and p.getParent().getNext(): if self.find_todo(p.getParent().getNext(), stage = 1): return True if stage == 0: g.es("None found") return False
def colours_menu(self,parent, p): c = self.c self.prep_pickle(p.v, 'fg') self.prep_pickle(p.v, 'bg') for var in (self.pickles['fg'].get(), self.pickles['bg'].get()): if var and var != '' and var != 'Other' and not self.colours.count(var): self.colours.insert(0, var) g.es("Added %s to the colour list" % (var)) for label,var in (('Foreground',self.pickles['fg']),('Background',self.pickles['bg'])): menu = Tk.Menu(parent,tearoff=0,takefocus=1) for color in self.colours: menu.add_radiobutton(label=color, variable=var, value=color, command=self.redraw) parent.add_cascade(label=label,underline=0,menu=menu) def cleoColorsMenuCallback(): self.remove_colours(p.v) def cleoColorsMenuSubtree(): self.subtree_colours(p) c.add_command(parent,label='Remove Colouring', underline=0, command=cleoColorsMenuCallback) c.add_command(parent,label='Colour subtree', underline=0, command=cleoColorsMenuSubtree) def cleoAddColorsMenuCallback(): self.add_colour() c.add_command(parent,label='New Colour', underline=0, command=cleoAddColorsMenuCallback)
def run(self, p): c = self.c q = p.b if not q.strip() or p.b.strip().startswith('--- '): q = p.h if p.h.strip().startswith('--'): q = None if q is None: g.es('No valid / uncommented query') else: self.psqlLink.send(q.strip()+'\n') ans = [] maxlen=100 for d in self.psqlReader(self.psqlLink): if d.strip(): ans.append(d) if len(ans) > maxlen: del ans[maxlen-10] ans[maxlen-10] = ' ... skipping ...' n = p.insertAsNthChild(0) n.h = '-- ' + time.asctime() n.b = '\n'.join(ans) if p.b.strip().startswith('--- ') or not p.b.strip(): p.b = '-'+n.h+'\n\n'+n.b p.contract() else: c.selectPosition(n) c.redraw()
def run(self, p): c = self.c q = p.b if not q.strip() or p.b.strip().startswith('### '): q = p.h if p.h.strip().startswith('#'): q = None if q is None: g.es('No valid / uncommented statement') else: path = self.getPath(c, p) if not path: path = os.path.dirname(c.fileName()) if path: self.bashLink.send('cd %s\n' % path) self.bashLink.send(q.strip()+'\n') ans = [] maxlen=100 for d in self.bashReader(self.bashLink): if d.strip(): ans.append(d) if len(ans) > maxlen: del ans[maxlen-10] ans[maxlen-10] = ' ... skipping ...' n = p.insertAsNthChild(0) n.h = '## ' + time.asctime() n.b = '\n'.join(ans) if p.b.strip().startswith('### ') or not p.b.strip(): p.b = '#'+n.h+'\n\n'+n.b p.contract() else: c.selectPosition(n) c.redraw()
def startup (self): path = self.lib ; global dbs, libraries try: # 'r' and 'w' fail if the database doesn't exist. # 'c' creates it only if it doesn't exist. # 'n' always creates a new database. if dbs.has_key(path): self.db = dbs [path] self.trace('Library reusing: %s' % path) elif g.os_path_exists(path): self.db = anydbm.open(path,"rw") self.trace('Library reopening: %s' % path) dbs [path] = self.db else: self.trace('Library creating: %s' % path) self.db = anydbm.open(path,"c") self.path = path except Exception as err: g.es('Library: Exception creating database: %s' % (err,)) ok = (self.path and self.db and hasattr(self.db,'isOpen') and self.db.isOpen() and hasattr(self.db,'sync')) if ok: dbs [path] = self.db else: g.es('problem starting Library: %s' % (path)) return ok
def createAttrib(self, v, gui_parent=None): path,ok = QtWidgets.QInputDialog.getText(gui_parent, "Enter attribute path", "Enter path to attribute (space separated words)") ns = str(path).split() # not the QString if not ok or not ns: g.es("Cancelled") return #FIXME type_ = {True: '_view', False: '_edit'}[readonly] type_ = '_edit' if '|' in ns[-1]: nslist = [ ns[:-1] + [i.strip()] for i in ns[-1].split('|') ] else: nslist = [ns] for ns in nslist: if type_ not in ns: ns.insert(-1, type_) self.setAttrib(v, ns, '')
def selectChapter(self, event=None): '''Use the minibuffer to get a chapter name, then create the chapter.''' cc, k = self, self.c.k names = cc.setAllChapterNames() g.es('Chapters:\n' + '\n'.join(names)) k.setLabelBlue('Select chapter: ') k.get1Arg(event, handler=self.selectChapter1, tabList=names)
def leo_echo_cb(out, err, code, ent): arg = ent['arg'] g.es("> " + arg[0] + " " + repr(arg[1:]) ) if out: g.es(out) if err: g.es_error(err)
def label_to_subnode(self,event=None): """ Convert a label of the current node to a subnode. """ title = "Creating subnode" label_text = "Existing labels (on this node)" p = self.c.p labels = self.get_labels_dict(p) if labels is None: g.es("No labels defined for this node") return labelnames = labels.keys() labelnames.sort() root = self.c.frame.outerFrame widget = Pmw_combobox(root, title = title, label_text = label_text, scrolledlist_items = labelnames) result = widget.doit() if result == 'Cancel': return labelname = widget.dialog.get() g.es("Create subnode for label %s" % labelname) self.insert_node_for_label_as_child(p=p,labelname=labelname,labelcontent=labels[labelname])
def cmd_PickDir(c): """cmd_PickDir - Show user a folder picker to create a new top level @path node :Parameters: - `c`: outline """ p = c.p aList = g.get_directives_dict_list(p) path = c.scanAtPathDirectives(aList) if p.h.startswith('@'): # see if it's a @<file> node of some sort nodepath = p.h.split(None, 1)[-1] nodepath = g.os_path_join(path, nodepath) if not g.os_path_isdir(nodepath): # remove filename nodepath = g.os_path_dirname(nodepath) if g.os_path_isdir(nodepath): # append if it's a directory path = nodepath ocwd = os.getcwd() try: os.chdir(path) except OSError: g.es("Couldn't find path %s"%path) dir_ = g.app.gui.runOpenDirectoryDialog("Pick a folder", "Pick a folder") os.chdir(ocwd) if not dir_: g.es("No folder selected") return nd = c.p.insertAfter() nd.h = "@path %s" % dir_ c.redraw()
def zoom_toggle(self): """zoom_toggle - (Un)zoom current pane to be only expanded pane """ if self.root.zoomed: for ns in self.top().self_and_descendants(): if hasattr(ns, '_unzoom'): # this splitter could have been added since ns.setSizes(ns._unzoom) else: focused = Qt.QApplication.focusWidget() parents = [] parent = focused while parent: parents.append(parent) parent = parent.parent() if not focused: g.es("Not zoomed, and no focus") for ns in self.top().self_and_descendants(): ns._unzoom = ns.sizes() for i in range(ns.count()): w = ns.widget(i) if w in parents: sizes = [0] * len(ns._unzoom) sizes[i] = sum(ns._unzoom) ns.setSizes(sizes) break self.root.zoomed = not self.root.zoomed
def marks_to_label(self,event=None): """ Convert the existing marks to a label. """ title = "show labels" labellist = self.collect_labels() root = self.c.frame.outerFrame widget = Pmw_combobox(root, title = title, label_text = 'Existing labels', scrolledlist_items = labellist) result = widget.doit() if result == 'Cancel': return labelname = widget.dialog.get() g.es("Convert the existing marks to label %s" % labelname) # markedBit = self.c.rootVnode().__class__.markedBit for p in self.c.all_positions(): if p.isMarked(): # if (v.statusBits & markedBit ) != 0: labels_dict = self.get_labels_dict(p) if labels_dict is None: labels_dict = {} # do nothing if the node already has such a label. if not labels_dict.has_key(labelname): labels_dict[labelname] = '' self.set_labels_dict(p, labels_dict) p.setDirty()
def callTagHandler (self,bunch,tag,keywords): '''Call the event handler.''' trace = False and not g.unitTesting traceIdle = True handler,moduleName = bunch.fn,bunch.moduleName if trace and (traceIdle or tag != 'idle'): c = keywords.get('c') name = moduleName ; tag2 = 'leo.plugins.' if name.startswith(tag2): name = name[len(tag2):] g.trace('c: %s %23s : %s . %s' % ( c and c.shortFileName() or '<no c>', tag,handler.__name__,name)) # Make sure the new commander exists. for key in ('c','new_c'): c = keywords.get(key) if c: # Make sure c exists and has a frame. if not c.exists or not hasattr(c,'frame'): # g.pr('skipping tag %s: c does not exist or does not have a frame.' % tag) return None # Calls to registerHandler from inside the handler belong to moduleName. self.loadingModuleNameStack.append(moduleName) try: result = handler(tag,keywords) except Exception: g.es("hook failed: %s, %s, %s" % (tag, handler, moduleName)) g.es_exception() result = None self.loadingModuleNameStack.pop() return result
def styleNodeSelected( c ): '''Determines if a XSLT Style node has not been selected''' # if not stylenodes.has_key( c ): if c not in stylenodes: g.es( "No Style Node selected" ) return False return True
def sn_decode(s): try: return AES.new(__ENCKEY[0]).decrypt(base64.b64decode(s)).decode('utf-8').strip() except UnicodeDecodeError: g.es("Decode failed") __ENCKEY[0] = None return None
def processDocumentNode( c ): '''this executes the stylesheet node against the current node''' try: if not styleNodeSelected( c ): return proc = Processor() stylenode = stylenodes[ c ] pos = c.p c.selectPosition( stylenode ) sIO = getString( c ) mdom1 = minidom.parseString( sIO ) sIO = str( mdom1.toxml() ) hstring = str( stylenode.h ) if hstring == "": hstring = "no headline" stylesource = InputSource.DefaultFactory.fromString( sIO, uri = hstring) proc.appendStylesheet( stylesource ) c.selectPosition( pos ) xmlnode = pos.v xIO = getString( c ) mdom2 = minidom.parseString( xIO ) xIO = str( mdom2.toxml()) xhead = str( xmlnode.headString ) if xhead == "": xhead = "no headline" xmlsource = InputSource.DefaultFactory.fromString( xIO, uri = xhead ) result = proc.run( xmlsource ) nhline = "xsl:transform of " + str( xmlnode.headString ) p2 = pos.insertAfter() # tnode ) p2.setBodyString(result) p2.setHeadString(nhline) c.redraw() except Exception as x: g.es( 'exception ' + str( x )) c.redraw()
def keyboard_popup(self): """Assign a quick move action with the current node as a target, to be triggered with quickmove_keyboard_action """ c = self.c menu = QtGui.QMenu(c.frame.top) cmds = {} for name, first_last, long, short in quickMove.flavors: if first_last: todo = 'first child', 'last child', 'next sibling', 'prev sibling' else: todo = [''] for which in todo: if which: which = " "+which.title() k = "Set as "+long+" "+short+which+' target' cmds[k] = {'first': which, 'type': name} menu.addAction(k) pos = c.frame.top.window().frameGeometry().center() action = menu.exec_(pos) if action is None: return k = str(action.text()) g.es(k) self.keyboard_target = quickMoveButton( self, c.p.v, cmds[k]['first'], type_=cmds[k]['type'])
def unl_to_pos(self, c2, for_p, bookmark=False): """"c2 may be an outline (like c) or an UNL (string) return c, p where c is an outline and p is a node to copy data to in that outline for_p is the p to be copied - needed to check for invalid recursive copy / move """ if g.isString(c2): # c2 is an UNL indicating where to insert full_path = c2 path, unl = full_path.split('#', 1) c2 = g.openWithFileName(path, old_c=self.c) self.c.bringToFront(c2=self.c) found, maxdepth, maxp = g.recursiveUNLFind(unl.split('-->'), c2) if found: if not bookmark and (for_p == maxp or for_p.isAncestorOf(maxp)): g.es("Invalid move") return None, None nd = maxp.insertAsNthChild(0) else: g.es("Could not find '%s'"%full_path) self.c.bringToFront(c2=self.c) return None, None else: # c2 is an outline, insert at top nd = c2.rootPosition().insertAfter() nd.copy().back().moveAfter(nd) return c2, nd
def check_blanks_and_tabs(self, lines): '''Check for intermixed blank & tabs.''' # Do a quick check for mixed leading tabs/blanks. fn = g.shortFileName(self.root.h) w = self.tab_width blanks = tabs = 0 for s in g.splitLines(lines): lws = self.get_str_lws(s) blanks += lws.count(' ') tabs += lws.count('\t') # Make sure whitespace matches @tabwidth directive. if w < 0: ok = tabs == 0 message = 'tabs found with @tabwidth %s in %s' % (w, fn) elif w > 0: ok = blanks == 0 message = 'blanks found with @tabwidth %s in %s' % (w, fn) if ok: ok = blanks == 0 or tabs == 0 message = 'intermixed blanks and tabs in: %s' % (fn) if not ok: if g.unitTesting: self.report(message) else: g.es(message) return ok
def permanentButton (self, event=None, v=None, type_=None, first=None): """make buttons on this node permanent NOTE: includes buttons deleted""" c = self.c if not v: p = c.p v = p.v qm = c.quickMove if 'quickMove' not in v.u or 'buttons' not in v.u['quickMove']: # possibly deleting old style list v.u['quickMove'] = {'buttons':[]} cnt = 0 for mover, button in qm.buttons: if (mover.target == v and (not type_ or mover.type_ == type_) and (not first or mover.first == first)): cnt += 1 v.u['quickMove']['buttons'].append( {'first':mover.first, 'type': mover.type_}) if cnt: g.es('Made buttons permanent') else: g.es("Didn't find button")
def printHandlers (self,c,moduleName=None): '''Print the handlers for each plugin.''' tabName = 'Plugins' c.frame.log.selectTab(tabName) if moduleName: s = 'handlers for %s...\n' % (moduleName) else: s = 'all plugin handlers...\n' g.es(s+'\n',tabName=tabName) data = [] modules = {} for tag in self.handlers: bunches = self.handlers.get(tag) for bunch in bunches: name = bunch.moduleName tags = modules.get(name,[]) tags.append(tag) modules[name] = tags n = 4 for key in sorted(modules): tags = modules.get(key) if moduleName in (None,key): for tag in tags: n = max(n,len(tag)) data.append((tag,key),) lines = ['%*s %s\n' % (-n,s1,s2) for (s1,s2) in data] g.es('',''.join(lines),tabName=tabName)
def openwith_rclick_cb(): if editor: cmd = '%s "%s"' % (editor, absp) g.es('Edit: %s' % cmd) subprocess.Popen(cmd, shell=True)
def settings_find_undefined(self, event): g.es("Settings finder: no setting defined")
def printLastMacro(self, event=None): '''Print the last (unnamed) macro.''' if self.lastMacro: for event in self.lastMacro: g.es(repr(event.stroke))
def importDir(self, dir, compteurglobal): """ La routine récursive de lecture des fichiers """ if not g.os_path_exists(dir): if language == 'french': g.es("Ce répertoire n'existe pas: %s" + dir) else: g.es("No such Directory: %s" + dir) return compteurglobal head, tail = g.os_path_split(dir) c = self.c current = c.p try: #ici, on liste le contenu du répertoire body = "" #@+<< listdir >> #@+node:ekr.20050301083306.11: *4* << listdir >> try: fichiers = os.listdir(dir) dossiers = [] for f in fichiers: # mettre ici le code de création des noeuds path = g.os_path_join(dir, f) # est-ce un fichier ? if g.os_path_isfile(path): body += (f + "\n") else: # c'est alors un répertoire dossiers.append(path) compteurglobal += 1 except Exception: if language == 'french': g.es("erreur dans listage fichiers...") else: g.es("os.listdir error...") g.es_exception() #@-<< listdir >> p = c.importCommands.createHeadline(current, body, tail) c.selectPosition(p) if dossiers: for d in dossiers: compteurglobal = self.importDir(d, compteurglobal) c.setChanged(True) #sélectionne le noeud parent c.selectPosition(current) except Exception: if language == 'french': g.es("erreur d'insertion de noeud...") else: g.es("error while creating node...") g.es_exception() return compteurglobal
def doNodeAction(pClicked, c): hClicked = pClicked.h.strip() #Display messages based on 'messageLevel'. Valid values: # 0 = log no messages # 1 = log that the plugin was triggered and each matched patterns # 2 = log 1 & 'event passed' # 3 = log 1,2 & 'no match to pattern' # 4 = log 1,2,3, & any code debugging messages, # matched pattern's 'directives', and '@file saved' settings messageLevel = c.config.getInt('nodeActions_message_level') if messageLevel >= 1: g.es( "nodeActions: triggered" ) #Save @file type nodes before running script if enabled saveAtFile = c.config.getBool('nodeActions_save_atFile_nodes') if messageLevel >= 4: g.blue( "nA: Global nodeActions_save_atFile_nodes=",saveAtFile) #Find the "nodeActions" node pNA = g.findNodeAnywhere(c,"nodeActions") if not pNA: pNA = g.findNodeAnywhere(c,"NodeActions") if pNA: #Found "nodeActions" node foundPattern = False passEventExternal = False #No pass to next plugin after pattern matched #Check entire subtree under the "nodeActions" node for pattern for pScript in pNA.subtree(): #Nodes with subnodes are not tested for a match if pScript.hasChildren(): continue #Don't trigger on double click of a nodeActions' pattern node if pClicked == pScript: continue pattern = pScript.h.strip() #Pattern node's header if messageLevel >= 4: g.blue( "nA: Checking pattern '" + pattern) #if directives exist, parse them and set directive flags for later use directiveExists = re.search( " \[[V>X],?[V>X]?,?[V>X]?]$", pattern ) if directiveExists: directives = directiveExists.group(0) else: directives = "[]" #What directives exist? useRegEx = re.search("X", directives) != None passEventInternal = re.search("V", directives) != None if not passEventExternal: #don't disable once enabled. passEventExternal = re.search(">", directives) != None #Remove the directives from the end of the pattern (if they exist) pattern = re.sub( " \[.*]$", "", pattern, 1) if messageLevel >= 4: g.blue( "nA: Pattern='" + pattern + "' " + "(after directives removed)") #Keep copy of pattern without directives for message log patternOriginal = pattern #if pattern begins with "@files" and clicked node is an @file type #node then replace "@files" in pattern with clicked node's @file type patternBeginsWithAtFiles = re.search( "^@files ", pattern ) clickedAtFileTypeNode = False #assume @file type node not clicked if patternBeginsWithAtFiles: #Check if first word in clicked header is in list of @file types firstWordInClickedHeader = hClicked.split()[0] if firstWordInClickedHeader in atFileTypes: clickedAtFileTypeNode = True #Tell "write @file type nodes" code #Replace "@files" in pattern with clicked node's @file type pattern = re.sub( "^@files", firstWordInClickedHeader, pattern) if messageLevel >= 4: g.blue( "nA: Pattern='" + pattern + "' " + "(after @files substitution)") #Check for pattern match to clicked node's header if useRegEx: match = re.search(pattern, hClicked) else: match = fnmatch.fnmatchcase(hClicked, pattern) if match: if messageLevel >= 1: g.blue( "nA: Matched pattern '" + patternOriginal + "'") if messageLevel >= 4: g.blue( "nA: Directives: X=",useRegEx, "V=",passEventInternal, ">=",passEventExternal,) #if @file type node, save node to disk (if configured) if clickedAtFileTypeNode: if saveAtFile: #Problem - No way found to just save clicked node, saving all c.fileCommands.writeAtFileNodes() c.requestRedrawFlag = True c.redraw() if messageLevel >= 3: g.blue( "nA: Saved '" + hClicked + "'") #Run the script applyNodeAction(pScript, pClicked, c) #Indicate that at least one pattern was matched foundPattern = True #Don't trigger more patterns unless enabled in patterns' headline if passEventInternal == False: break else: if messageLevel >= 3: g.blue("nA: Did not match '" + patternOriginal + "'") #Finished checking headline against patterns if not foundPattern: #no match to any pattern, always pass event to next plugin if messageLevel >= 1: g.blue("nA: No patterns matched to """ + hClicked + '"') return False #TL - Inform onIconDoubleClick that no action was taken elif passEventExternal == True: #last matched pattern has directive to pass event to next plugin if messageLevel >= 2: g.blue("nA: Event passed to next plugin") return False #TL - Inform onIconDoubleClick to pass double-click event else: #last matched pattern did not have directive to pass event to plugin if messageLevel >= 2: g.blue("nA: Event not passed to next plugin") return True #TL - Inform onIconDoubleClick to not pass double-click else: #nodeActions plugin enabled without a 'nodeActions' node if messageLevel >= 4: g.blue("nA: The ""nodeActions"" node does not exist") return False #TL - Inform onIconDoubleClick that no action was taken
def showMenu(self, tag, k): # deprecated g.app.gui.killPopupMenu() if k['c'] != self.c: return # not our problem p = k['p'] self.c.selectPosition(p) v = p.v c = self.c # Create the menu. menu = Tk.Menu(None, tearoff=0, takefocus=0) commands = [ (True, 'Mark as link source', self.markSrc), (True, 'Mark as link dest.', self.markDst), (True, 'Link to source', self.linkSrc), (True, 'Link to dest.', self.linkDst), (True, 'Undirected link', self.linkUnd), (True, 'Rescan links', self.loadLinksInt), ] if hasattr(v, 'unknownAttributes') and '_bklnk' in v.unknownAttributes: i = 0 links = v.unknownAttributes['_bklnk']['links'] dests = [] while i < len(links): linkType, other = links[i] otherV = self.vnode[other] otherP = self.vnodePosition(otherV) if not self.c.positionExists(otherP): g.es('Deleting lost link') del links[i] else: i += 1 dests.append((linkType, otherP)) if dests: smenu = Tk.Menu(menu, tearoff=0, takefocus=1) for i in dests: def goThere(where=i[1]): c.selectPosition(where) c.add_command(menu, label={ 'S': '->', 'D': '<-', 'U': '--' }[i[0]] + i[1].h, underline=0, command=goThere) def delLink(on=v, to=i[1].v.unknownAttributes['_bklnk']['id'], type_=i[0]): self.deleteLink(on, to, type_) c.add_command(smenu, label={ 'S': '->', 'D': '<-', 'U': '--' }[i[0]] + i[1].h, underline=0, command=delLink) menu.add_cascade(label='Delete link', menu=smenu, underline=1) menu.add_separator() for command in commands: available, text, com = command if not available: continue c.add_command(menu, label=text, underline=0, command=com) # Show the menu. event = k['event'] g.app.gui.postPopupMenu(self.c, menu, event.x_root, event.y_root) return None # 'break' # EKR: Prevent other right clicks.
def create_rclick_cb(): os.makedirs(absp) g.es("Created " + absp)
def ns_do_context(self, id_, splitter, index): if id_.startswith('_fl_embed_layout'): self.embed() return True if id_.startswith('_fl_restore_default'): self.get_top_splitter().load_layout({ 'content': [{ 'content': ['_leo_pane:outlineFrame', '_leo_pane:logFrame'], 'orientation': 1, 'sizes': [509, 275] }, '_leo_pane:bodyFrame'], 'orientation': 2, 'sizes': [216, 216] }) if id_.startswith('_fl_help'): self.c.putHelpFor(__doc__) # g.handleUrl("http://leoeditor.com/") return True if id_ == '_fl_save_layout': if self.c.config.getData("free-layout-layout"): g.es( "WARNING: embedded layout in @settings/@data free-layout-layout " "will override saved layout") layout = self.get_top_splitter().get_saveable_layout() name = g.app.gui.runAskOkCancelStringDialog( self.c, title="Save layout", message="Name for layout?", ) if name: self.c.db['_ns_layout'] = name d = g.app.db.get('ns_layouts', {}) d[name] = layout # make sure g.app.db's __set_item__ is hit so it knows to save g.app.db['ns_layouts'] = d return True if id_.startswith('_fl_load_layout:'): if self.c.config.getData("free-layout-layout"): g.es( "WARNING: embedded layout in @settings/@data free-layout-layout " "will override saved layout") name = id_.split(':', 1)[1] self.c.db['_ns_layout'] = name layout = g.app.db['ns_layouts'][name] self.get_top_splitter().load_layout(layout) return True if id_.startswith('_fl_delete_layout:'): name = id_.split(':', 1)[1] if ('yes' == g.app.gui.runAskYesNoCancelDialog( self.c, "Really delete Layout?", "Really permanently delete the layout '%s'?" % name)): d = g.app.db.get('ns_layouts', {}) del d[name] # make sure g.app.db's __set_item__ is hit so it knows to save g.app.db['ns_layouts'] = d if '_ns_layout' in self.c.db: del self.c.db['_ns_layout'] return True if id_.startswith('_fl_forget_layout:'): if '_ns_layout' in self.c.db: del self.c.db['_ns_layout'] return True if id_.startswith('_fl_restore_layout:'): self.loadLayouts("reload", {'c': self.c}, reloading=True) return True return False
def cmd_ShowCurrentPath(event): """Just show the path to the current file/directory node in the log pane.""" c = event.get('c') g.es(getPath(c, c.p))
def onActivated(n, *args, **keys): color = color_list[n] sheet = template % (color, color) box.setStyleSheet(sheet) g.es("copied to clipboard:", color) QtWidgets.QApplication.clipboard().setText(color)
def moveCurrentNodeToTarget(self, checked=False): '''Move the current position to the last child of self.target.''' c = self.c p = c.p vnodes = [i.v for i in c.getSelectedPositions()] needs_undo = self.type_ != "jump" if needs_undo: bunch = c.undoer.beforeMoveNode(p) for v in vnodes: p2 = c.vnode2position(self.target) p = c.vnode2position(v) if not c.positionExists(p2): g.error('Target no longer exists: %s' % self.targetHeadString) return if self.type_ in ('clone', 'move'): # all others are always valid? if p.v == p2.v or not self.checkMove(p,p2): g.error('Invalid move: %s' % (self.targetHeadString)) return if p2.isAncestorOf(p): # not for sibling moves p2.expand() nxt = p.visNext(c) or p.visBack(c) nxt = nxt.v # store a VNode instead of position as positions are too easily lost if self.type_ != 'jump': p.setDirty() # before move to dirty current parent p2.setDirty() c.setChanged(changedFlag=True) if self.type_ == 'clone': p = p.clone() if self.type_ in ('move', 'clone'): if self.which == 'first child': p.moveToFirstChildOf(p2) elif self.which == 'last child': p.moveToLastChildOf(p2) elif self.which in ('next sibling', 'prev sibling'): if not p2.parent(): raise Exception("Not implemented for top-level nodes") #FIXME if self.which == 'next sibling': p.moveToNthChildOf(p2.parent(), p2._childIndex) elif self.which == 'prev sibling': p.moveToNthChildOf(p2.parent(), p2._childIndex-1) else: raise Exception("Unknown move type "+self.which) elif self.type_ == 'bkmk': unl = self.computeUNL(p) # before tree changes if self.which == 'first child': nd = p2.insertAsNthChild(0) elif self.which == 'last child': nd = p2.insertAsLastChild() elif self.which == 'next sibling': nd = p2.insertAfter() elif self.which == 'prev sibling': nd = p2.insertBefore() else: raise Exception("Unknown move type "+self.which) h = p.anyAtFileNodeName() or p.h while h and h[0] == '@': h = h[1:] nd.h = h nd.b = unl elif self.type_ == 'copy': if self.which == 'first child': nd = p2.insertAsNthChild(0) quickMove.copy_recursively(p, nd) # unlike p.copyTreeFromSelfTo, deepcopys p.v.u elif self.which == 'last child': nd = p2.insertAsLastChild() quickMove.copy_recursively(p, nd) elif self.which == 'next sibling': nd = p2.insertAfter() quickMove.copy_recursively(p, nd) elif self.which == 'prev sibling': nd = p2.insertBefore() quickMove.copy_recursively(p, nd) else: raise Exception("Unknown move type "+self.which) elif self.type_ in ('linkTo', 'linkFrom'): blc = getattr(c, 'backlinkController', None) if blc is None: g.es("Linking requires backlink.py plugin") return if self.type_ == 'linkTo': blc.vlink(p.v, p2.v) else: blc.vlink(p2.v, p.v) if self.type_ in ('bkmk', 'clone', 'copy', 'move'): nxt = c.vnode2position(nxt) elif self.type_ == 'jump': nxt = c.vnode2position(self.target) else: nxt = None # linkTo / linkFrom don't move if nxt is not None and c.positionExists(nxt): c.selectPosition(nxt) if needs_undo: c.undoer.afterMoveNode(p,'Quick Move', bunch) c.setChanged(True) c.redraw()
def cmd_ToggleAutoLoad(event): """cmd_ToggleAutoLoad - toggle autoloading behavior """ c = event.get('c') c.__active_path['do_autoload'] = not c.__active_path['do_autoload'] g.es("Autoload: %s" % c.__active_path['do_autoload'])
def renameBuffer(self, event): """Rename a buffer, i.e., change a node's headline.""" g.es('rename-buffer not ready yet') if 0: self.c.k.setLabelBlue('Rename buffer from: ') self.getBufferName(event, self.renameBufferFinisher1)
def openDir(c, parent, d): """ Expand / refresh an existing folder Note: With the addition of per folder inclusion/exclusion a check is done against both the current list of nodes and against the files/folders as they exist on the system. This check must be done in both places to keep the node list in sync with the file system while respecting the inc/exc lists - John Lunzer """ # compare folder content to children try: path, dirs, files = next(os.walk(d)) except StopIteration: # directory deleted? c.setHeadString(parent, '*' + parent.h.strip('*') + '*') return # parent.expand() # why? oldlist = set() toRemove = set() newlist = [] bodySplit = parent.b.splitlines() excdirs = False excfiles = False regEx = False if re.search('^excdirs', parent.b, flags=re.MULTILINE): excdirs = True if re.search('^excfiles', parent.b, flags=re.MULTILINE): excfiles = True if re.search('^re', parent.b, flags=re.MULTILINE): regEx = True inc = [ line.replace('inc=', '') for line in bodySplit if line.startswith('inc=') ] exc = [ line.replace('exc=', '') for line in bodySplit if line.startswith('exc=') ] #flatten lists if using comma separations inc = [item for line in inc for item in line.strip(' ').split(',')] exc = [item for line in exc for item in line.strip(' ').split(',')] # get children info for p in flattenOrganizers(parent): entry = p.h.strip('/*') if entry.startswith('@'): # remove only the @part directive = entry.split(None, 1) if len(directive) > 1: entry = entry[len(directive[0]):].strip() #find existing inc/exc nodes to remove #using p.h allows for example exc=/ to remove all directories if not checkIncExc(p.h,inc,exc, regEx) or \ (excdirs and entry in dirs) or \ (excfiles and entry in files): toRemove.add(p.h) #must not strip '/', so nodes can be removed else: oldlist.add(entry) # remove existing found inc/exc nodes for headline in toRemove: found = g.findNodeInChildren(c, parent, headline) if found: found.doDelete() # dirs trimmed by toRemove to remove redundant checks for d2 in set(dirs) - set([h.strip('/') for h in toRemove]): if d2 in oldlist: oldlist.discard(d2) else: if checkIncExc(d2, [i.strip('/') for i in inc], [e.strip('/') for e in exc], regEx) and not excdirs: newlist.append('/' + d2 + '/') # files trimmed by toRemove, retains original functionality of plugin for f in set(files) - toRemove: if f in oldlist: oldlist.discard(f) else: if checkIncExc(f, inc, exc, regEx) and not excfiles: newlist.append(f) # insert newlist newlist.sort() ignored = 0 newlist.reverse() # un-reversed by the following loop for name in newlist: if inReList(name, c.__active_path['ignore']): ignored += 1 continue p = parent.insertAsNthChild(0) c.setChanged(True) c.setHeadString(p, name) if name.startswith('/'): # sufficient test of dirness as we created newlist c.setBodyString(p, '@path ' + name.strip('/')) elif (c.__active_path['do_autoload'] and inReList(name, c.__active_path['autoload'])): openFile(c, p, os.path.join(d, p.h), autoload=True) elif (c.__active_path['do_autoload'] and c.__active_path['load_docstring'] and name.lower().endswith(".py")): # do_autoload suppresses doc string loading because turning # autoload off is supposed to address situations where autoloading # causes problems, so don't still do some form of autoloading p.b = c.__active_path['DS_SENTINEL'] + "\n\n" + loadDocstring( os.path.join(d, p.h)) p.setMarked() p.contract() if ignored: g.es('Ignored %d files in directory' % ignored) # warn / mark for orphan oldlist for p in flattenOrganizers(parent): h = p.h.strip('/*') # strip / and * if (h not in oldlist or (p.hasChildren() and not isDirNode(p))): # clears bogus '*' marks nh = p.h.strip('*') # strip only * else: nh = '*' + p.h.strip('*') + '*' if isDirNode(p): for orphan in p.subtree(): c.setHeadString(orphan, '*' + orphan.h.strip('*') + '*') if p.h != nh: # don't dirty node unless we must c.setHeadString(p, nh) c.selectPosition(parent)
def styleNodeSelected(c): '''Determines if a XSLT Style node has not been selected''' if not stylenodes.has_key(c): g.es("No Style Node selected") return False return True
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 ### New code. t = c.config.getString('mod_scripting_subtext') or '' t2 = g.u(pb.text()) if not t.endswith(t): pb.setText(t2+t) # else: # 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 show_note(self): if stickynotes: stickynotes.stickynote_f({'c': self.c}) else: g.es('stickynotes not available')
def mylyn_scores_f(*a): for k, v in self.scoring.items(): g.es(str(k) + " " + str(v))
def itp(itPoll, cmdrB, cmdrRes, resultsRoot, subPscript, subPbabKill, wro, reo, wre, ree, itOut, itErr, start, createNodes): """ Poll for subprocess done Arguments: itPoll: Idle time object for itp() cmdrB: Leo-Editor commander for the Leo-Babel root node cmdRes: Leo-Editor commander for resultsRoot resultsRoot: Results root subPscript: Script subprocess object subPbabKill: babel_kill subprocess object wro: File Descriptor reo: File Descriptor wre: File Descriptor ree: File Descriptor itOut: Idle time object itErr: Idle time object start: Script start time createNodes: False --> Don't create results nodes True --> Create results nodes Returns: None """ babelCmdr = cmdrB.user_dict['leo_babel'] if babelCmdr.cmdDoneFlag: if babelCmdr.cmdDoneStdPolled and babelCmdr.cmdDoneErrPolled: itOut.stop() itErr.stop() itPoll.stop() wro.close() wre.close() colorCompletion = babelCmdr.colorCompletion leoG.es(babelCmdr.termMsg, color=colorCompletion) leoG.es(babelCmdr.etMsg, color=colorCompletion) if createNodes: makeBabelNodes(cmdrRes, resultsRoot, reo, ree, babelCmdr.termMsg, babelCmdr.etMsg) reo.close() ree.close() babelCmdr.babelExecCnt += 1 else: rc = subPscript.poll() if not rc is None: end = time.time() babelCmdr.cmdDoneFlag = True if subPbabKill.poll() is None: # Kill subprocess has not terminated # pylint: disable=no-member os.kill(subPbabKill.pid, signal.SIGHUP) babelCmdr.termMsg = '{0} Subprocess Termination Code'.format( rc) et = int(round(end - start)) minutes, secs = divmod(et, 60) hours, minutes = divmod(minutes, 60) babelCmdr.etMsg = '{hr:02d}:{mi:02d}:{se:02d} Elapsed Time. {end} End Time'.format( hr=hours, mi=minutes, se=secs, end=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
def insert_read_only_node(c, p, name): if name == "": name = g.app.gui.runOpenFileDialog( c, title="Open", filetypes=[("All files", "*")], ) c.setHeadString(p, "@read-only %s" % name) c.redraw() parse = urlparse(name) try: if parse[0] == 'ftp': f = FTPurl(name) # FTP URL elif parse[0] == 'http': f = urlopen(name) # HTTP URL else: f = open(name, "r") # local file g.es("..." + name) new = f.read() f.close() except IOError: # as msg: # g.es("error reading %s: %s" % (name, msg)) # g.es("...not found: " + name) c.setBodyString(p, "") # Clear the body text. return True # Mark the node as changed. else: ext = os.path.splitext(parse[2])[1] if ext.lower() in ['.htm', '.html']: #@+<< convert HTML to text >> #@+node:edream.110203113231.895: *3* << convert HTML to text >> fh = StringIO() fmt = AbstractFormatter(DumbWriter(fh)) # the parser stores parsed data into fh (file-like handle) ### pylint: disable=too-many-function-args parser = HTMLParser(fmt) # send the HTML text to the parser parser.feed(new) parser.close() # now replace the old string with the parsed text new = fh.getvalue() fh.close() # finally, get the list of hyperlinks and append to the end of the text ### pylint: disable=no-member hyperlinks = parser.anchorlist numlinks = len(hyperlinks) if numlinks > 0: hyperlist = ['\n\n--Hyperlink list follows--'] for i in range(numlinks): hyperlist.append("\n[%d]: %s" % (i + 1, hyperlinks[i])) # 3/26/03: was i. new = new + ''.join(hyperlist) #@-<< convert HTML to text >> previous = p.b c.setBodyString(p, new) changed = (g.toUnicode(new) != g.toUnicode(previous)) if changed and previous != "": g.es("changed: %s" % name) # A real change. return changed
def open_temp_file(self, c, d, fn, testing=False): ''' Open a temp file corresponding to fn in an external editor. d is a dictionary created from an @openwith settings node. 'args': the command-line arguments to be used to open the file. 'ext': the file extension. 'kind': the method used to open the file, such as subprocess.Popen. 'name': menu label (used only by the menu code). 'shortcut': menu shortcut (used only by the menu code). ''' trace = False and not g.unitTesting testing = testing or g.unitTesting arg_tuple = d.get('args', []) arg = ' '.join(arg_tuple) kind = d.get('kind') # This doesn't handle %ProgramFiles% # if kind in ('os.spawnl', 'subprocess.Popen'): # if not g.os_path_exists(arg): # g.trace('Executable not found', arg, arg_tuple) # return try: # All of these must be supported because they # could exist in @open-with nodes. command = '<no command>' if kind in ('os.system', 'os.startfile'): # New in Leo 5.7: # Use subProcess.Popen(..., shell=True) c_arg = self.join(arg, fn) if trace: command = '%s -> subprocess.Popen(%s)' % ( kind, g.shortFileName(c_arg)) g.trace(command) if not testing: try: subprocess.Popen(c_arg, shell=True) except OSError: g.es_print('c_arg', repr(c_arg)) g.es_exception() # Legacy code. # command = 'os.startfile(%s)' % self.join(arg, fn) # if trace: g.trace(command) # # pylint: disable=no-member # # trust the user not to use this option on Linux. # if not testing: # os.startfile(arg, fn) elif kind == 'exec': g.es_print('open-with exec no longer valid.') # command = 'exec(%s)' % self.join(arg,fn) # if trace: g.trace(command) # if not testing: # exec(self.join(arg,fn),{},{}) elif kind == 'os.spawnl': filename = g.os_path_basename(arg) command = 'os.spawnl(%s,%s,%s)' % (arg, filename, fn) if trace: g.trace(command) if not testing: os.spawnl(os.P_NOWAIT, arg, filename, fn) elif kind == 'os.spawnv': filename = os.path.basename(arg_tuple[0]) vtuple = arg_tuple[1:] vtuple.insert(0, filename) # add the name of the program as the first argument. # Change suggested by Jim Sizelove. vtuple.append(fn) command = 'os.spawnv(%s)' % (vtuple) if trace: g.trace(command) if not testing: os.spawnv(os.P_NOWAIT, arg[0], vtuple) #??? elif kind == 'subprocess.Popen': c_arg = self.join(arg, fn) command = 'subprocess.Popen(%s)' % c_arg if trace: g.trace(command) if not testing: try: subprocess.Popen(c_arg, shell=True) except OSError: g.es_print('c_arg', repr(c_arg)) g.es_exception() elif g.isCallable(kind): # Invoke openWith like this: # c.openWith(data=[func,None,None]) # func will be called with one arg, the filename if trace: g.trace('%s(%s)' % (kind, fn)) command = '%s(%s)' % (kind, fn) if not testing: kind(fn) else: command = 'bad command:' + str(kind) if not testing: g.trace(command) return command # for unit testing. except Exception: g.es('exception executing open-with command:', command) g.es_exception() return 'oops: %s' % command
def run_diff_on_vcs(self, event=None): """run_diff_on_vcs - try and check out the previous version of the Leo file and compare a node with the same gnx in that file with the current node :Parameters: - `event`: Leo event """ c = self.c dir_, filename = g.os_path_split(c.fileName()) relative_path = [] # path from top of repo. to .leo file mode = None # mode is which VCS to use # given A=/a/b/c/d/e, B=file.leo adjust to A=/a/b/c, B=d/e/file.leo # so that A is the path to the repo. and B the path in the repo. to # the .leo file path = dir_ while not mode: for vcs in 'git', 'bzr': if g.os_path_exists(g.os_path_join(path, '.' + vcs)): mode = vcs break else: path, subpath = g.os_path_split(path) if not subpath: break relative_path[0:0] = [subpath] if not mode: g.es("No supported VCS found in '%s'" % dir_) return gnx = c.p.gnx if mode == 'git': cmd = [ 'git', '--work-tree=%s' % path, 'show', 'HEAD:%s' % g.os_path_join(*(relative_path + [filename])), ] if mode == 'bzr': cmd = [ 'bzr', 'cat', '--revision=revno:-1', c.fileName(), # path, # g.os_path_join( *(relative_path + [filename]) ), ] cmd = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) data, err = cmd.communicate() aFile = StringIO(data) tree = leosax.get_leo_data(aFile) for node in tree.flat(): if node.gnx == gnx: break else: g.es("Node (gnx) not found in previous version") return node.b = ''.join(node.b) self.run_appropriate_diff([node, c.p])
def dumpfocus(): f = QtWidgets.QApplication.instance().focusWidget() g.es("Focus: " + f) print("Focus: " + f)
def load_clouds(self, from_background=None): """ load_clouds - Handle loading from cloud on startup and after background checking for changes. :param set from_background: set of (remote, ID) str tuples if we're called after a background check process finds changes. :return: <|returns|> :rtype: <|return type|> """ if from_background is None: from_background = set() skipped = [] background = [] # things to check in background for lc_v in self.find_clouds(): kwargs = self.kw_from_node(lc_v) if from_background and \ (kwargs['remote'], kwargs['ID']) not in from_background: # only process nodes from the background checking continue read = False read_on_load = kwargs.get('read_on_load', '').lower() if from_background: # was 'background', changes found, so now treat as 'ask' read_on_load = 'ask' if read_on_load == 'yes': read = True elif read_on_load == 'ask': try: last_read = datetime.strptime( lc_v.u['_leo_cloud']['last_read'], "%Y-%m-%dT%H:%M:%S.%f") except KeyError: last_read = None message = "Read cloud data '%s', overwriting local nodes?" % kwargs[ 'ID'] if last_read: delta = datetime.now() - last_read message = "%s\n%s, %sh:%sm:%ss ago" % ( message, last_read.strftime("%a %b %d %H:%M"), 24 * delta.days + int(delta.seconds / 3600), int(delta.seconds / 60) % 60, delta.seconds % 60) read = g.app.gui.runAskYesNoCancelDialog(self.c, "Read cloud data?", message=message) read = str(read).lower() == 'yes' if read: self.read_current(p=self.c.vnode2position(lc_v)) elif read_on_load == 'background': # second time round, with from_background data, this will # have been changed to 'ask' (above), so no infinite loop background.append((lc_v, kwargs, self.recursive_hash(lc_v, [], include_current=False))) elif read_on_load == 'no': g.es("NOTE: not reading '%s' from cloud" % kwargs['ID']) elif read_on_load != 'ask': skipped.append(kwargs['ID']) if skipped: g.app.gui.runAskOkDialog( self.c, "Unloaded cloud data", message= "There is unloaded (possibly stale) cloud data, use\nread_on_load: yes|no|ask\n" "in @leo_cloud nodes to avoid this message.\nUnloaded data:\n%s" % ', '.join(skipped)) if background: # send to background thread for checking names = ', '.join([i[1]['ID'] for i in background]) g.es("Checking cloud trees in background:\n%s" % names) thread = threading.Thread(target=self.bg_check, args=(background, )) thread.start() # start watching for results g.IdleTime(self.bg_post_process).start()
def save(self, event=None, fileName=None): '''Save a Leo outline to a file.''' if False and g.app.gui.guiName() == 'curses': g.trace('===== Save disabled in curses gui =====') return c = self p = c.p # Do this now: w may go away. w = g.app.gui.get_focus(c) inBody = g.app.gui.widget_name(w).startswith('body') if inBody: p.saveCursorAndScroll() if g.unitTesting and g.app.unitTestDict.get( 'init_error_dialogs') is not None: # A kludge for unit testing: # indicated that c.init_error_dialogs and c.raise_error_dialogs # will be called below, *without* actually saving the .leo file. c.init_error_dialogs() c.raise_error_dialogs(kind='write') return if g.app.disableSave: g.es("save commands disabled", color="purple") return c.init_error_dialogs() # 2013/09/28: use the fileName keyword argument if given. # This supports the leoBridge. # Make sure we never pass None to the ctor. if fileName: c.frame.title = g.computeWindowTitle(fileName) c.mFileName = fileName if not c.mFileName: c.frame.title = "" c.mFileName = "" if c.mFileName: # Calls c.setChanged(False) if no error. g.app.syntax_error_files = [] c.fileCommands.save(c.mFileName) c.syntaxErrorDialog() else: root = c.rootPosition() if not root.next() and root.isAtEditNode(): # There is only a single @edit node in the outline. # A hack to allow "quick edit" of non-Leo files. # See https://bugs.launchpad.net/leo-editor/+bug/381527 fileName = None # Write the @edit node if needed. if root.isDirty(): c.atFileCommands.writeOneAtEditNode(root, toString=False, force=True) c.setChanged(False) else: fileName = ''.join(c.k.givenArgs) if not fileName: fileName = g.app.gui.runSaveFileDialog( c, initialfile=c.mFileName, title="Save", filetypes=[g.fileFilters('LEOFILES')], defaultextension=g.defaultLeoFileExtension(c)) c.bringToFront() if fileName: # Don't change mFileName until the dialog has suceeded. c.mFileName = g.ensure_extension(fileName, g.defaultLeoFileExtension(c)) c.frame.title = c.computeWindowTitle(c.mFileName) c.frame.setTitle(c.computeWindowTitle(c.mFileName)) # 2013/08/04: use c.computeWindowTitle. c.openDirectory = c.frame.openDirectory = g.os_path_dirname( c.mFileName) # Bug fix in 4.4b2. if g.app.qt_use_tabs and hasattr(c.frame, 'top'): c.frame.top.leo_master.setTabName(c, c.mFileName) c.fileCommands.save(c.mFileName) g.app.recentFilesManager.updateRecentFiles(c.mFileName) g.chdir(c.mFileName) # Done in FileCommands.save. # c.redraw_after_icons_changed() c.raise_error_dialogs(kind='write') # *Safely* restore focus, without using the old w directly. if inBody: c.bodyWantsFocus() p.restoreCursorAndScroll() else: c.treeWantsFocus()
def focusOutEvent(self, event): QtWidgets.QGraphicsItemGroup.focusOutEvent(self, event) self.bg.setBrush(QtGui.QBrush(QtGui.QColor(200, 240, 200))) g.es("focusOutEvent")
def xml2leo(event, from_string=None): """handle import of an .xml file, places new subtree after c.p """ c = event['c'] p = c.p if from_string: parser_func = etree.fromstring file_name = from_string else: parser_func = etree.parse cd_here(c, p) file_name = g.app.gui.runOpenFileDialog(c, title="Open", filetypes=table, defaultextension=".xml") if not file_name: raise Exception("No file selected") try: xml_ = parser_func(file_name) except etree.XMLSyntaxError: xml_ = parser_func(file_name, parser=etree.HTMLParser()) except Exception: g.es("Failed to read '%s'" % file_name) raise if from_string: # etree.fromstring and etree.parse return Element and # ElementTree respectively xml_ = etree.ElementTree(xml_) nd = p.insertAfter() nd.h = os.path.basename(file_name) # the root Element isn't necessarily the first thing in the XML file # move to the beginning of the list to capture preceding comments # and processing instructions toplevel = xml_.getroot() while toplevel.getprevious() is not None: toplevel = toplevel.getprevious() # move through list, covering root Element and any comments # or processing instructions which follow it while toplevel is not None: append_element(toplevel, nd) toplevel = toplevel.getnext() nd.b = '<?xml version="%s"?>\n' % (xml_.docinfo.xml_version or '1.0') if xml_.docinfo.encoding: nd.b = '<?xml version="%s" encoding="%s"?>\n' % ( xml_.docinfo.xml_version or '1.0', xml_.docinfo.encoding) if NSMAP: for k in sorted(NSMAP): if k: nd.b += "%s: %s\n" % (k, NSMAP[k]) else: nd.b += "%s\n" % NSMAP[k] nd.b += xml_.docinfo.doctype + '\n' c.redraw() return nd
def focus(): g.es(stack) c.selectPosition(pos)
def log(self, s, color=None): if color: g.es('sftp.py:', s, color=color) else: g.es('sftp.py:', s)
def xml_validate(event): """Perform DTD validation on the xml and return error output or an empty string if there is none""" c = event['c'] p = c.p # first just try and create the XML try: xml_ = xml_for_subtree(p) except ValueError: g.es('ERROR generating XML') g.es(traceback.format_exc()) return g.es('XML generated, attempting DTD validation') if xml_.startswith('<?xml '): # Unicode strings with encoding declaration are not supported # so cut off the xml declaration xml_ = xml_.split('\n', 1)[1] # set cwd so local .dtd files can be found cd_here(c, p) # make xml indented because for some unknown reason pretty_print=True # in xml_for_subtree doesn't work # etree.fromstring only returns the root node, # losing the DTD, so etree.parse instead from io import StringIO xml_ = StringIO(xml_) xml_ = etree.tostring(etree.parse(xml_), pretty_print=True) parser = etree.XMLParser(dtd_validation=True) try: etree.fromstring(xml_, parser=parser) g.es('No errors found') except etree.XMLSyntaxError as xse: g.es('ERROR validating XML') g.es(str(xse)) # seems XMLSyntaxError doesn't set lineno? Get from message lineno = int(str(xse).split()[-3].strip(',')) - 1 xml_text = xml_.split('\n') for i in range(max(0, lineno - 6), min(len(xml_text), lineno + 3)): g.es("%d%s %s" % (i, ':' if i != lineno else '*', xml_text[i]))