#@+leo-ver=5-thin
#@+node:ekr.20101028112631.4959: * @file doc-startup.txt
#@@language python

#@+all
#@+node:ekr.20070325123558: ** @chapters
#@+node:ekr.20050404094627: ** Buttons
#@+node:ekr.20090712122931.10453: *3* Disabled buttons
#@+node:ekr.20050901060119: *4* @@button def list
@color

# Converts text selection to def list.

@others

createDefList(c,p)
#@+node:ekr.20050901061220: *5* createDefList
def createDefList(c,p):

    undoType = 'Create Def List'
    head,lines,tail,oldSel,oldYview = c.getBodyLines()
    if not lines: return

    result = ['``%s``\n' % lines[0].strip()]

    for line in lines[1:]:
        result.append('    %s' % line.lstrip()+'\n')
    lines = ''.join(result).rstrip()

    c.updateBodyPane(head,lines,tail,undoType,oldSel,oldYview)
#@+node:ekr.20050901055150: *4* @@button promote
@color
# Script to promote headlines and body text

root = p.copy()
body = [root.bodyString()+'\n']
for p in root.subtree_iter():
    body.append(p.headString())
    body.append(p.bodyString().rstrip()+'\n')

body = '\n'.join(body)
root.setBodyString(body)
#@+node:ekr.20091112060950.6677: *4* @@button Remove newlines
s = p.bodyString()

s = s.replace('\n\n','***2***')
s = s.replace('\n',' ')
s = s.replace('***2***','\n\n')
s = s.replace('\n  ','\n')
s = s.replace('\n ','\n')
s = s.replace('     ',' ')
s = s.replace('    ',' ')
s = s.replace('   ',' ')
s = s.replace('  ',' ')

p.setBodyString(s)
#@+node:ekr.20060525104232: *4* @@button rst->html
import leo.core.leoPlugins as leoPlugins

rst3 = leoPlugins.getPluginModule('rst3')

if rst3:
    controller = rst3.controllers.get(c)
    if controller:
        p,s = controller.writeNodeToString(ext='.html')
        print '*' * 40,p
        print s
else:
    rst3 = leoPlugins.loadOnePlugin('rst3',verbose=True)
    if rst3:
        g.es('rst3 loaded')
        rst3.onCreate('tag',{'c':c})
    else:
        # Ask to be removed.
        g.app.scriptDict['removeMe'] = True
#@+node:ekr.20060525110135: *4* @@button rst->pdf
# Problems with pdf files.

import leo.core.leoPlugins as leoPlugins

rst3 = leoPlugins.getPluginModule('rst3')

if rst3:
    controller = rst3.controllers.get(c)
    if controller:
        p,s = controller.writeNodeToString(ext='.pdf')
        print '*' * 40,p
        print s
else:
    rst3 = leoPlugins.loadOnePlugin('rst3',verbose=True)
    if rst3:
        g.es('rst3 loaded')
        rst3.onCreate('tag',{'c':c})
    else:
        # Ask to be removed.
        g.app.scriptDict['removeMe'] = True
#@+node:ekr.20060525110217: *4* @@button rst->rst
import leo.core.leoPlugins as leoPlugins

rst3 = leoPlugins.getPluginModule('rst3')

if rst3:
    controller = rst3.controllers.get(c)
    if controller:
        p,s = controller.writeNodeToString(ext=None) # Convert rst nodes to rst text.
        print '*' * 40,p
        print s
else:
    rst3 = leoPlugins.loadOnePlugin('rst3',verbose=True)
    if rst3:
        g.es('rst3 loaded')
        rst3.onCreate('tag',{'c':c})
    else:
        # Ask to be removed.
        g.app.scriptDict['removeMe'] = True
#@+node:ekr.20060525110135.1: *4* @@button rst->tex
import leo.core.leoPlugins as leoPlugins

rst3 = leoPlugins.getPluginModule('rst3')

if rst3:
    controller = rst3.controllers.get(c)
    if controller:
        p,s = controller.writeNodeToString(ext='.tex')
        print '*' * 40,p
        print s
else:
    rst3 = leoPlugins.loadOnePlugin('rst3',verbose=True)
    if rst3:
        g.es('rst3 loaded')
        rst3.onCreate('tag',{'c':c})
    else:
        # Ask to be removed.
        g.app.scriptDict['removeMe'] = True
#@+node:ekr.20101025080245.6075: *3* @button promote-bodies
'''Copy the body text of all descendants to the parent's body text.'''

# Great for creating what's new nodes.
result = [p.b]
b = c.undoer.beforeChangeNodeContents(p)
for child in p.subtree():
    if child.b:
        result.append('\n- %s\n\n%s\n' % (child.h,child.b))
    else:
        result.append('\n- %s\n\n' % (child.h))
p.b = ''.join(result)
c.undoer.afterChangeNodeContents(p,'promote-child-bodies',b)
#@+node:ekr.20101118091038.5398: *3* @button reformat
# Ctrl-shift-p is Leo's regular reformat-paragraph command.

@others

reformatParagraph (c)

#@+node:ekr.20101118091038.5399: *4* reformatParagraph & helpers
def reformatParagraph (self,event=None,undoType='Reformat Paragraph'):

    """Reformat a text paragraph in a Tk.Text widget

Wraps the concatenated text to present page width setting. Leading tabs are
sized to present tab width setting. First and second line of original text is
used to determine leading whitespace in reformatted text. Hanging indentation
is honored.

Paragraph is bound by start of body, end of body, blank lines, and lines
starting with "@". Paragraph is selected by position of current insertion
cursor."""

    c = self ; body = c.frame.body ; w = body.bodyCtrl

    if g.app.batchMode:
        c.notValidInBatchMode("xxx")
        return

    if body.hasTextSelection():
        i,j = w.getSelectionRange()
        w.setInsertPoint(i)

    ### To do: change f(c,args) to c.f(args)
    oldSel,oldYview,original,pageWidth,tabWidth = rp_get_args(c)
    head,lines,tail = c.findBoundParagraph()
    if lines:
        indents,leading_ws = rp_get_leading_ws(c,lines,tabWidth)
        result = rp_wrap_all_lines(c,indents,leading_ws,lines,pageWidth)
        rp_reformat(c,head,oldSel,oldYview,original,result,tail,undoType)
#@+node:ekr.20101118091038.5400: *5* rp_get_args
def rp_get_args (self):

    '''Compute and return oldSel,oldYview,original,pageWidth,tabWidth.'''

    c = self ; body = c.frame.body ;  w = body.bodyCtrl

    d = c.scanAllDirectives()
    pageWidth = d.get("pagewidth")
    tabWidth  = d.get("tabwidth")

    original = w.getAllText()
    oldSel =  w.getSelectionRange()
    oldYview = body.getYScrollPosition()

    return oldSel,oldYview,original,pageWidth,tabWidth
#@+node:ekr.20101118091038.5401: *5* rp_get_leading_ws
def rp_get_leading_ws (self,lines,tabWidth):

    '''Compute and return indents and leading_ws.'''

    c = self

    indents = [0,0]
    leading_ws = ["",""]

    for i in (0,1):
        if i < len(lines):
            # Use the original, non-optimized leading whitespace.
            leading_ws[i] = ws = g.get_leading_ws(lines[i])
            indents[i] = g.computeWidth(ws,tabWidth)

    indents[1] = max(indents)

    if len(lines) == 1:
        leading_ws[1] = leading_ws[0]

    return indents,leading_ws
#@+node:ekr.20101118091038.5402: *5* rp_reformat
def rp_reformat (self,head,oldSel,oldYview,original,result,tail,undoType):

    '''Reformat the body and update the selection.'''

    c = self ; body = c.frame.body ; w = body.bodyCtrl
    s = w.getAllText()

    # This destroys recoloring.
    junk, ins = body.setSelectionAreas(head,result,tail)

    changed = original != head + result + tail
    if changed:
        # 2010/11/16: stay in the paragraph.
        body.onBodyChanged(undoType,oldSel=oldSel,oldYview=oldYview)
    else:
        # Advance to the next paragraph.
        ins += 1 # Move past the selection.
        while ins < len(s):
            i,j = g.getLine(s,ins)
            line = s[i:j]
            # 2010/11/16: it's annoying, imo, to treat @ lines differently.
            if line.isspace(): ### or line.startswith('@'):
                ins = j+1
            else:
                ins = i ; break

        # setSelectionAreas has destroyed the coloring.
        c.recolor()

    w.setSelectionRange(ins,ins,insert=ins)
    w.see(ins)
#@+node:ekr.20101118091038.5403: *5* rp_wrap_all_lines
def rp_wrap_all_lines (self,indents,leading_ws,lines,pageWidth):

    '''compute the result of wrapping all lines.'''

    trailingNL = lines and lines[-1].endswith('\n')
    lines = [g.choose(z.endswith('\n'),z[:-1],z) for z in lines]

    # Wrap the lines, decreasing the page width by indent.
    result = g.wrap_lines(lines,
        pageWidth-indents[1],
        pageWidth-indents[0])

    # prefix with the leading whitespace, if any
    paddedResult = []
    paddedResult.append(leading_ws[0] + result[0])
    for line in result[1:]:
        paddedResult.append(leading_ws[1] + line)

    # Convert the result to a string.
    result = '\n'.join(paddedResult)
    if trailingNL: result = result + '\n'

    return result
#@+node:ville.20090520232034.6345: *3* @button preview
g.app.gui.runScrolledMessageDialog(c=c, msg = g.u('rst:') + p.b)
#@+node:ekr.20101109084947.4909: *3* @button preview-tree
result = []
for p2 in p.subtree():
    result.append(p2.b)
s = '\n'.join(result)

g.app.gui.runScrolledMessageDialog(c=c, msg = g.u('rst:') + s)
#@+node:ekr.20110406082808.18151: *3* @button rst3
c.rstCommands.rst3()
#@+node:ekr.20110611085637.5020: *3* @button clean spellpyx
'''Use only '\n' as the line ending in spellpyx.txt.'''
    
force = False
fn = g.os_path_finalize_join(
    g.app.loadDir,'..','plugins','spellpyx.txt')
f = open(fn,mode='rb')
s = f.read()
f.close()
s2 = s.replace(b'\r',b'')
if force or s != s2:
    f = open(fn,mode='wb')
    f.write(s2)
    f.close()
    g.es_print('cleaned',g.shortFileName(fn))
#@+node:ekr.20101009111520.4525: *3*  Slideshow Buttons
#@+node:ekr.20101008061729.4443: *4* @@button copy-@screenshot-node
'''
Copy the @screenshot node (a child of this node)
to all @slide nodes under p, (an @slideshow node),
that do not contain an @screenshot node.
'''

error = None
# Find this node:
h = '@button copy-@screenshot-node'
p2 = g.findNodeAnywhere(c,h)
if not p2:
    error = 'Can not find',p.h
# Find the @screenshot tree and the optional @select node.
if not error:
    select,template = None,None
    for child in p2.children():
        if g.match_word(child.h,0,'@screenshot'):
            template = child.copy()
        if g.match_word(child.h,0,'@select'):
            select = child.copy()
    if not template:
        error = 'No template @slideshow node in %s' % p2.h
if not error:
    if not g.match_word(p.h,0,'@slideshow'):
        error = 'not an @slideshow node',p.h
if error:
    g.error(error)
else:
    c.selectPosition(template)
    c.copyOutline()
    changed = False
    b = c.undoer.beforeChangeTree(p)
    for child in p.children():
        if not g.match_word(child.h,0,'@slide'):
            continue
        for grandChild in child.children():
            if g.match_word(grandChild.h,0,'@screenshot'):
                break
        else:
            changed = True
            p3 = child.insertAsLastChild()
            c.selectPosition(p3)
            c.pasteOutline()
            g.note('copied @screenshot to %s' % child.h)
            if select:
                c.selectPosition(p3)
                p4 = child.insertAsLastChild()
                p4.h = select.h
                g.note('copied %s to %s' % (select.h,child.h))
            c.selectPosition(p3)
            c.deleteOutline(p3)
            child.contract()
    if changed:
        c.undoer.afterChangeTree(p,'copy-@screenshot',b)
    c.redraw()
#@+node:ekr.20101008061729.4552: *5* @screenshot
#@+node:ekr.20101008061729.4553: *6* To Do List
My to-do list.
#@+node:ekr.20101008061729.4554: *7* Urgent
1. Make Leo tutorials.  The world is waiting.
2. Pay phone bill or the world will never know.
#@+node:ekr.20101008061729.4555: *7* Important
#@+node:ekr.20101008061729.4556: *7* Soon
#@+node:ekr.20101008061729.4557: *7* Whenever
#@+node:ekr.20101008061729.4558: *6* Diary
#@+node:ekr.20101008061729.4559: *7* 2009
@language rest

This is my diary.
#@+node:ekr.20101008061729.4567: *8* Jul 2009
July 1
    Started writing in my diary.
July 2
    Wrote another sentence in my diary.
July 3
    Keeping my diary very regularly.
July 5
    Oops...Yesterday I forgot towrite in my diary.
#@+node:ekr.20101008061729.4568: *8* Aug 2009
#@+node:ekr.20101008061729.4569: *8* Sep 2009
#@+node:ekr.20101008061729.4570: *8* Oct 2009
#@+node:ekr.20101008061729.4571: *8* Nov 2009
#@+node:ekr.20101008061729.4572: *8* Dec 2009
#@+node:ekr.20101008061729.4561: *7* 2010
#@+node:ekr.20101008061729.4637: *5* @select Urgent
#@+node:ekr.20101008061729.4425: *5* @@button ins-@slide-nodes
'''Create @slide nodes under p, an @slideshow node.'''

n = 23 # Number of last slide to be created.

existing = [z.copy().h for z in p.children() 
    if g.match_word(z.h,0,'@slide')]

if g.match_word(p.h,0,'@slideshow'):
    b = c.undoer.beforeChangeTree(p)
    changed = False
    for n in range(1,n+1):
        h = '@slide %03d' % n
        if h not in existing:
            changed = True
            child = p.insertAsLastChild()
            child.h = h
            g.note('created %s' % h)
    if changed:
        c.undoer.afterChangeTree(p,'ins-@slide-nodes',b)
    else:
        g.note('no @slide nodes inserted')
    c.redraw()
else:
    g.error('not an @slideshow node',p.h)
#@+node:ekr.20101009114655.4531: *5* @@button make-slide @key=Alt-8
m = g.loadOnePlugin('screenshots')
m.make_slide_command(event={'c':c})
#@+node:ekr.20101007100904.4371: *5* @@button make-slide-show @key=Alt-8
m = g.loadOnePlugin('screenshots')
m.make_slide_show_command(event={'c':c})
#@+node:ekr.20101014110348.5338: *5* @@button meld
'''Meld Wink slides into an @slideshow folder.

   Copy screenshot files from the wink_dir to slideshow_dir, numbering
   the destination files to reflect "holes" created by @no-screenshot
   nodes.

   This script carefully checks that the number of screenshot files
   matches the number of screenshots referenced by the @slide nodes.
   No copying takes place if the numbers are not as expected.'''

@language python

import glob
import os
import shutil

slideshow_dir = 'C:/leo.repo/trunk/leo/doc/html/slides/leo-basics-step-by-step'

wink_dir = 'C:/leo.repo/trunk/leo/doc/html/slides/leo-basics-step-by-step/_files'
    # The directory containing the wink screenshots.
    # This will usually be <slideshow_dir>/_files.
    # **Important** You generate these screenshots using Wink's 
    # Export As Html command (!)

@others

mc = MeldController(c,p,slideshow_dir,wink_dir)
mc.run()
#@+node:ekr.20101014110348.5339: *6* class MeldController
class MeldController:

    def __init__ (self,c,p,slideshow_dir,wink_dir):

        self.c = c
        self.slideshow_dir = slideshow_dir
        self.slideshow_node = p
        self.wink_dir = wink_dir

    @others
#@+node:ekr.20101016043116.5353: *7* utils
#@+node:ekr.20101016043116.6224: *8* finalize & fix
def fix (self,fn):
    return os.path.normcase(fn).replace('\\','/')

def finalize (self,fn):
    return self.fix(g.os_path_finalize_join(self.slideshow_dir,fn))
#@+node:ekr.20101015151959.5301: *8* has_at_no_screenshot_node
def has_at_no_screenshot_node (self,p):

    for p in p.children():
        if self.match(p,'@no-screenshot'):
            return True
    else:
        return False
#@+node:ekr.20101015151959.5302: *8* match
def match (self,p,pattern):

    '''Return True if p.h matches the pattern.'''

    return g.match_word(p.h,0,pattern)
#@+node:ekr.20101014110348.5341: *7* run & helpers
def run (self):

    print('='*20)

    aList = self.get_wink_screenshots()
    if not aList:
        return

    if not self.check(aList):
        return

    # Pass 1: copy files for @slide nodes w/o @no-screenshot nodes.
    self.copy_files(aList)

    # Pass 2: adjust children of @slide nodes.
    self.adjust_slideshow()

    print('meld done')
#@+node:ekr.20101014110348.5344: *8* adjust_slideshow & helper
def adjust_slideshow(self):

    '''Adjust all @slide nodes in the slideshow.'''

    # Traverse the tree as in the screenshot plugin.
    # That is, ignore @ignore trees and nested @slide nodes.
    # This ensures that the slide number, n, is correct.
    p = self.slideshow_node
    after = p.nodeAfterTree()
    p = p.firstChild()
    n = 1
    while p and p != after:
        if self.match(p,'@slide'):
            self.adjust_slide_node(p,n)
            n += 1
            p.moveToNodeAfterTree()
        elif self.match(p,'@ignore'):
            p.moveToNodeAfterTree()
        else:
            p.moveToThreadNext()
#@+node:ekr.20101015151959.5300: *9* adjust_slide_node & helpers
def adjust_slide_node (self,p,slide_number):

    '''Adjust p, an @slide node.'''

    trace = True

    # Delete the first "@url built slide" node.
    self.delete_at_url_built_slide_node(p)

    # Do nothing more if there is an @no-screenshot node.
    if self.has_at_no_screenshot_node(p):
        return

    # Add or update the "@url final output file" node.
    p2 = self.add_at_url_final_output_file(p,slide_number)

    # Add the .. image:: directive.
    self.add_image_directive(p,slide_number)
#@+node:ekr.20101016043116.6226: *10* add_at_url_final_output_file
def add_at_url_final_output_file (self,p,slide_number):

    '''Create or update the "@url final output file" node.'''

    trace = True
    tag ='@url final output file'

    for child in p.children():
        if self.match(child,tag):
            p2 = child ; break
    else:
        if trace: g.es('add %s' % tag)
        p2 = p.insertAsLastChild()
        p2.h = tag

    p2.b = self.finalize(
        'slide-%03d.png' % (slide_number))

    return p2
#@+node:ekr.20101016043116.6223: *10* add_image_directive
def add_image_directive (self,p,slide_number):

    '''Add an image directive in p if it is not there.'''

    s = '.. image:: slide-%03d.png' % (slide_number)

    if p.b.find(s) == -1:
        p.b = p.b.rstrip() + '\n\n%s\n\n' % (s)
#@+node:ekr.20101016043116.6225: *10* delete_at_url_built_slide_node
def delete_at_url_built_slide_node (self,p):

    '''Delete any "@url built slide" node in p's children.'''

    trace = True
    tag = '@url built slide'

    for child in p.children():
        if self.match(child,tag):
            if trace: g.es('del %s in %s' % (tag,p.h))
            child.doDelete()
            break
#@+node:ekr.20101014110348.5342: *8* check & helpers
def check (self,aList):

    '''
    Check that len(aList) matches the number of @slide nodes in the
    slideshow. Don't count @slide nodes containing an @no-screenshot node.
    '''

    p = self.slideshow_node
    n1 = len(aList)
    n2,n3 = self.count_slide_nodes()

    if not self.check_dir(self.wink_dir):
        return False
    if not self.check_dir(self.slideshow_dir):
        return False
    if not self.match(p,'@slideshow'):
        return g.error('not a @slideshow node: %s',p.h)

    if n1 != (n2-n3):
        return g.error(
            '%s wink slides\n'
            '%s @slide nodes\n'
            '%s @no_screenshot nodes' % (
                n1,n2,n3))

    return True
#@+node:ekr.20101016043116.5349: *9* check_dir
def check_dir (self,theDir):

    if not g.os_path_exists(theDir):
        return g.error('not found: %s' % (theDir))

    if not g.os_path_isdir(theDir):
        return g.error('not a directory: %s' % (theDir))

    return True
#@+node:ekr.20101014110348.5347: *9* count_slide_nodes
def count_slide_nodes (self):

    '''Return n1,n2

    n1 is the total number of @slide nodes in the @slideshow tree.
    n2 is number of @slide nodes containing an @no-slideshow child.
    '''

    p = self.slideshow_node
    after = p.nodeAfterTree()
    p = p.firstChild()
    n1,n2 = 0,0
    while p and p != after:
        if self.match(p,'@slide'):
            n1 += 1
            if self.has_at_no_screenshot_node(p):
                n2 += 1
            p.moveToNodeAfterTree()
        elif self.match(p,'@ignore'):
            p.moveToNodeAfterTree()
        else:
            p.moveToThreadNext()

    g.trace(n1,n2)
    return n1,n2
#@+node:ekr.20101014110348.5343: *8* copy_files & helper
def copy_files (self,aList):

    '''Copy files from the wink_dir to slideshow_dir,
    numbering the destination files to reflect "holes"
    created by @no-screenshot nodes.'''

    # Traverse the tree as in the screenshot plugin.
    # That is, ignore @ignore trees and nested @slide nodes.
    # This ensures that the slide number, n, is correct.
    p = self.slideshow_node
    after = p.nodeAfterTree()
    p = p.firstChild()
    wink_n = 0 # Wink screenshot numbers start at 0.
    slide_n = 1 # Slide numbers start at 1.
    while p and p != after:
        if self.match(p,'@slide'):
            if not self.has_at_no_screenshot_node(p):
                self.copy_file(aList,slide_n,wink_n)
                wink_n += 1
            slide_n += 1
            p.moveToNodeAfterTree()
        elif self.match(p,'@ignore'):
            p.moveToNodeAfterTree()
        else:
            p.moveToThreadNext()
#@+node:ekr.20101016043116.5352: *9* copy_file
def copy_file (self,aList,slide_n,wink_n):

    trace = True

    if wink_n >= len(aList):
        return g.trace('can not happen: '
            'len(aList): %s, n: %s' % (
                len(aList),wink_n))

    fn_src = aList[wink_n]
    fn_dst = 'slide-%03d.png' % (slide_n)

    if trace:
        g.trace('%7s -> %s' % (g.shortFileName(fn_src),fn_dst))

    shutil.copyfile(fn_src,fn_dst)
#@+node:ekr.20101014110348.5346: *8* get_wink_screenshots
def get_wink_screenshots (self):

    '''Return the properly sorted list of wink screenshots.'''

    trace = False

    aList = glob.glob(self.wink_dir + '/*.png')

    def key(s):
        path,ext = g.os_path_splitext(s)
        junk,n = g.os_path_split(path)
        n = n.strip()
        if n.isdigit():
            return int(n)
        else:
            g.error('bad wink screenshot: %s' % (s))
            raise KeyError

    aList.sort(key=key) # Essential.

    if trace:
        for z in aList:
            print(z)

    return aList
#@+node:ekr.20101008061729.4538: *5* @@button renumber nodes
'''Renumber @slide nodes under p, an @slideshow node.'''

if g.match_word(p.h,0,'@slideshow'):
    n = 1
    for child in p.children():
        if g.match(child.h,0,'@slide'):
            child.h = '@slide %03d' % n
            n += 1
    c.redraw()
else:
    g.error('not an @slideshow node',p.h)
#@+node:ekr.20101018183640.5360: *4* @@button remove-image-directives
@language python

changed = 0
b = c.undoer.beforeChangeTree(p)

for child in p.children():
    s = child.b
    i = s.find('.. image::')
    if i > -1:
        i,j = g.getLine(s,i)
        child.b = s[:i] + s[j+1:]
        # g.es(child.h)
        changed += 1

if changed:
    g.es('changed %s nodes' % changed)
    c.undoer.afterChangeTree(p,'remove-image-directives',b)

#@+node:ekr.20101018185243.5368: *4* @@button remove-built-slides
@language python

changed = 0
b = c.undoer.beforeChangeTree(p)
for child in p.children():
    for child2 in child.children():
        if g.match_word(child2.h,0,'@url built slide'):
            child2.doDelete()
            changed += 1
            break

if changed:
    g.es('deleted %s nodes' % (changed))
    c.undoer.afterChangeTree(p,'remove-@url-built-slide',b)
    c.redraw()
#@+node:ekr.20101122071746.5020: *4* @@button remove-final-output
@language python

changed = 0
b = c.undoer.beforeChangeTree(p)
for child in p.children():
    for child2 in child.children():
        if g.match_word(child2.h,0,'@url final output file'):
            child2.doDelete()
            changed += 1
            break

if changed:
    g.es('deleted %s nodes' % (changed))
    c.undoer.afterChangeTree(p,'remove-@url-final-output',b)
    c.redraw()
#@+node:ekr.20050407144417: ** @settings
#@+node:ekr.20100908122026.4443: *3* @@enabled_plugins
# Standard plugins
mod_scripting.py
nav_qt.py
plugins_menu.py
quicksearch.py
scrolledmessage.py
# UNL.py

# Other useful plugins
# codewisecompleter.py
# leoremote.py
screenshots.py
#@+node:ekr.20100907092300.4440: *3* Inkscape options
#@+node:ekr.20100907092300.4441: *4* @string inkscape-template = ../docs/inkscape-template.svg
Path to inkscape template file
#@+node:ekr.20100907092300.4442: *4* @string inkscape-bin = "c:\Program Files (x86)\Inkscape\inkscape.exe"
Path to Inkscape executable
#@+node:ekr.20101009114830.4724: *3* File options
#@+node:ekr.20080923182326.1: *4* @@bool create_nonexistent_directories = True
This option applies to directories specified in filenames in all kinds of @file trees, and to filenames specified in the @path directive.

True:  Leo attempts to create directories if they do not exist.
False: Leo never attempts to create directories.
#@+node:ekr.20080412124815.1: *4* @bool fixedWindow = False
#@+node:ekr.20101009114830.4723: *4* @bool put_expansion_bits_in_leo_files = False
@nocolor-node

Formerly, this had to be on because the expansion bits
of @screenshot trees were significant.

Happily, this is no longer true.

True (recommended):
    Write "E" attribute bits in <v> elements.
    Leo outlines will record the expansion state of all nodes.

False:
    (Good for files like unitTest.leo)
    Suppress "E" attribute bits in <v> elements.
    Only the ancestors of the presently selected node will
    be expanded when Leo opens an outline.
#@+node:ekr.20101009114830.4725: *3* Plugins options
#@+node:ekr.20050407144342: *4* @page http plugin




#@+node:ekr.20050407144342.1: *5* @bool http_active = False
#@+node:ekr.20050407144342.2: *5* @int  port = 8080
#@+node:ekr.20050407144342.3: *5* @string rst_http_attributename = rst_http_attribute
#@+node:ekr.20050812123002: *4* @page rst3 options
#@+node:ekr.20050812123002.1: *5* Http options...
#@+node:ekr.20050812123002.2: *6* @bool rst3_clear_http_attributes = False
Deletes p.v.rst2_http_attributename from all nodes after writing.

Deletes p.v.unknownAttributes if it then becomes empty.
#@+node:ekr.20050812123002.3: *6* @string rst3_http_attributename = 'rst_http_attribute'
#@+node:ekr.20050812123002.4: *6* @bool rst3_http_server_support = False
@nocolor

If False, add_node_marker and http_support_main  do nothing.  Otherwise add_node_marker does the following:

1. add_node_marker writes a string using generate_node_marker.

Generates 'http-node-marker-'+str(number), where number is config.node_counter
(incremented each time add_node_marker is called.

2. Enables the following code in :
@color

    if config.tag == 'open2':
        http_map = self.http_map
    else:
        http_map = {}
        config.anchormap = {}
        # maps v nodes to markers.
        config.node_counter = 0
    # [snip] code to write the tree
    if config.rst2_http_server_support:
        self.http_map = http_map
#@+node:ekr.20050812123002.5: *6* @string rst3_node_begin_marker = 'http-node-marker-'
#@+node:ekr.20050812123002.6: *5* @bool rst3_massage_body = False
True: call body_filter to massage text.

Removes @ignore, @nocolor, @wrap directives.
#@+node:ekr.20050812123002.7: *5* @bool rst3_format_headlines = True
Used differently.  See rst2_pure_document.
#@+node:ekr.20050812123002.8: *5* @bool rst3_write_intermediate_file = True
#@+node:sps.20100708213227.44914: *5* @string rst3_write_intermediate_extension = .html.txt
#@+node:ekr.20051202072010: *5* @string rst3_default_path =
#@+node:ekr.20080923181012.1: ** @@rst ../test/new-directory/test.html
@ @rst-options
code_mode=False
generate_rst=True
http_server_support = False
show_organizer_nodes=True
show_headlines=True
show_leo_directives=True
stylesheet_path=..\doc
write_intermediate_file = False
verbose=True
@c

A test of creating directories.

############
html test
############
#@+node:ekr.20101111175617.5037: ** Script: get-plugin-docstrings
'''Creates an outline containing most docstrings from leoPlugins.leo.

Documentation for some docstings are suppressed.'''

@others

controller(c).run()
#@+node:ekr.20101111175617.56915: *3* class controller
class controller:

    def __init__ (self,c):
        self.c = c
        self.trace = False

    @others
#@+node:ekr.20101112045055.13356: *4* allowDir
def allowDir (self,p):

    '''Return True if we should allow scan of directory p.'''

    aList = (
        # Suppressed directories.
        'Examples','Experimental',
        'Dyna plugins by e',
        'Gui plugins','Testing',
    )
    return p.h not in aList and not p.h.startswith('  ')
#@+node:ekr.20101112222250.5322: *4* allowFile
def allowFile (self,p):

    '''Return True if we should allow scan of a file at p.'''

    aList = (
        # Suppresssed files.
        '@file bookmarks.py',       # Replaced by better @url.
        '@file rst3.py',            # Replaced by core rst3 command.
        '@file stickynotes_plus.py', # Experimental version of stickynotes
        '@file testnode.py',        # Replaced by @edit.
        # These all depend on old plugins_manager.py.
        '@file autotrees.py', 
        '@file old_plugin_manager.py',
        '@file leoupdate.py',
        # These are used only by autotrees.py.
        r'@file trees\doc.py',
        r'@file trees\news.py',
        r'@file trees\remote.py',
        r'@file trees\rss.py',
        r'@file trees\test.py',
    )
    return p.h not in aList and p.isAnyAtFileNode() and p.h.endswith('.py')
#@+node:ekr.20101112045055.13355: *4* createDocs
def createDocs (self,output,root):

     for p in root.children():
        if self.allowDir(p):
            if self.trace: print('\n**',p.h)
            child = output.insertAsLastChild()
            child.h = p.h
            for p2 in p.subtree():
                if self.allowFile(p2):
                    h = p2.anyAtFileNodeName()
                    s = self.getDocString(p2)
                    if self.trace: print('%5s %s' % (len(s),h))
                    child2 = child.insertAsLastChild()
                    child2.h = h
                    child2.b = "%s\n\n" % s.strip()
#@+node:ekr.20101112045055.13354: *4* createSummary
def createSummary (self,output,root):

    summary = output.insertAsLastChild()
    summary.h = 'Summary'
    result = []

    for p in root.children():
        if self.allowDir(p):
            for p2 in p.subtree():
                if self.allowFile(p2):
                    h = p2.anyAtFileNodeName()
                    s = self.getDocString(p2)
                    s = self.getFirstParagraph(s).rstrip()
                    if s:
                        if not s.endswith('.'): s = s + '.'
                        result.append('%s\n%s\n\n' % (h,s))

    # Sort by plugin name, ignoring case.
    def lower(s): return s.lower()
    result.sort(key=lower)
    summary.b = ''.join(result)
#@+node:ekr.20101111175617.14683: *4* getDocString
def getDocString(self,p):

    '''Return the docstring of the @<file> node p.'''

    trace = False # p.h.find('@file rClick.py') > -1
    if trace: g.trace('='*20)
    for p2 in p.self_and_subtree():
        s = p2.b
        if trace: g.trace(p2.h)
        for tag in ("'''",'"""'):
            i = s.find(tag)
            if i > -1:
                j = s.find(tag,i+3)
                if j > -1:
                    if trace: g.trace('**found**',p2.h,'\n',s)
                    return s[i+3:j]
    else:
        return ''
#@+node:ekr.20101112045055.13357: *4* getFirstParagraph
def getFirstParagraph (self,s):

    lines =  g.splitLines(s.strip())
    if not lines: return ''

    result = []
    for s in lines:
        if s.strip():
            result.append('   '+s)
        else:
            break

    return ''.join(result)
#@+node:ekr.20101111175617.24328: *4* openPlugins
def openLeoPlugins(self):

    fn = g.os_path_finalize_join(
        g.app.loadDir,'..','plugins','leoPlugins.leo')

    ok,frame = g.openWithFileName(fn,
        old_c=self.c,enableLog=True,
        gui=None,readAtFileNodesFlag=True)

    if ok:
        return frame.c
    else:
        g.error('can not open leoPlugins.leo')
        return None
#@+node:ekr.20101111175617.5787: *4* run
def run(self):

    c = self.c
    new_c = self.openLeoPlugins()
    if not new_c: return

    # Create the top-level output node.
    output = c.p.insertAfter()
    output.h = 'get-docstrings-output'
    output.b = '@language rest\n'

    # Scan the descendants of the Plugins node.
    root = g.findNodeAnywhere(new_c,'Plugins')
    if root:
        if self.trace: print('='*20)
        self.createSummary(output,root)
        self.createDocs(output,root)
        c.frame.bringToFront() # new_c.close()
        c.redraw()
    else:
        g.error('no Plugins node')



@language python

@language python

@language python
#@-all
#@-leo
