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

#@+all
#@+node:ekr.20070325123558: ** @chapters
#@+node:ekr.20050404094627: ** Local buttons
#@+node:ekr.20111017085134.16158: *3*  Slideshow Buttons
#@+node:ekr.20111017085134.16159: *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.20111017085134.16160: *5* @screenshot
#@+node:ekr.20111017085134.16161: *6* To Do List
My to-do list.
#@+node:ekr.20111017085134.16162: *7* Urgent
1. Make Leo tutorials.  The world is waiting.
2. Pay phone bill or the world will never know.
#@+node:ekr.20111017085134.16163: *7* Important
#@+node:ekr.20111017085134.16164: *7* Soon
#@+node:ekr.20111017085134.16165: *7* Whenever
#@+node:ekr.20111017085134.16166: *6* Diary
#@+node:ekr.20111017085134.16167: *7* 2009
@language rest

This is my diary.
#@+node:ekr.20111017085134.16168: *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.20111017085134.16169: *8* Aug 2009
#@+node:ekr.20111017085134.16170: *8* Sep 2009
#@+node:ekr.20111017085134.16171: *8* Oct 2009
#@+node:ekr.20111017085134.16172: *8* Nov 2009
#@+node:ekr.20111017085134.16173: *8* Dec 2009
#@+node:ekr.20111017085134.16174: *7* 2010
#@+node:ekr.20111017085134.16175: *5* @select Urgent
#@+node:ekr.20111017085134.16176: *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.20111017085134.16177: *5* @@button make-slide @key=Alt-8
m = g.loadOnePlugin('screenshots')
m.make_slide_command(event={'c':c})
#@+node:ekr.20111017085134.16178: *5* @@button make-slide-show @key=Alt-8
m = g.loadOnePlugin('screenshots')
m.make_slide_show_command(event={'c':c})
#@+node:ekr.20111017085134.16179: *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.20111017085134.16180: *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.20111017085134.16181: *7* utils
#@+node:ekr.20111017085134.16182: *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.20111017085134.16183: *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.20111017085134.16184: *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.20111017085134.16185: *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.20111017085134.16186: *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.20111017085134.16187: *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.20111017085134.16188: *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.20111017085134.16189: *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.20111017085134.16190: *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.20111017085134.16191: *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.20111017085134.16192: *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.20111017085134.16193: *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.20111017085134.16194: *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.20111017085134.16195: *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.20111017085134.16196: *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.20111017085134.16197: *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.20111017085134.16198: *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.20111017085134.16199: *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.20111017085134.16200: *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: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.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
