#@+leo-ver=5-thin
#@+node:ktenney.20041211072654.1: * @file at_view.py
#@+<< docstring >>
#@+node:ekr.20041231104454: ** << docstring >>
''' Adds support for \@clip, \@view and \@strip nodes.

- Selecting a headline containing \@clip appends the contents of the clipboard to
  the end of the body pane.

- Double clicking the icon box of a node whose headline contains \@view
  *<path-to-file>* places the contents of the file in the body pane.

- Double clicking the icon box of a node whose headline contains \@strip
  *<path-to-file>* places the contents of the file in the body pane, with all
  sentinels removed.

This plugin also accumulates the effect of all \@path nodes.

'''
#@-<< docstring >>

#@@language python
#@@tabwidth -4
#@@pagewidth 80

__version__ = "0.8"
#@+<< version history >>
#@+node:ktenney.20041211072654.3: ** << version history >>
#@+at
# 
# 0.1 KT 2004/12/07 begin converting @button to plugin
# 
# 0.2 EKR style changes:
#     - Uses g.trace to simplify all traces.
#     - Removed comments originating from style guide.
#     - Defined __version__ only in root node.
# 0.3 EKR:
#     - Used g.importExtension to import path and win32clipboard.
#     - Added extensive comments to module's doc string.
#     - Added comments to class View node.
#     - Commented out several traces.
#     - Handle @verbatim sentinels in strip()
#     - Fix bug in strip: set path = currentPath.abspath()
# 0.4 EKR:
#     - Handle case where self.c has been destroyed in idle handler.
# 0.5 EKR:
#     - Corrected and expanded doc string.
# 0.6 EKR:
#     - Added better error message if can't load extensions.
# 0.7 EKR:
#     - Simplified code, fixed bugs and improved error messages.
# 0.8 EKR:
#     - Added init method.
#@-<< version history >>
#@+<< imports >>
#@+node:ktenney.20041211072654.4: ** << imports >>
import leo.core.leoGlobals as g

path           = g.importExtension('path',          pluginName=__name__,verbose=True)
win32clipboard = g.importExtension('win32clipboard',pluginName=__name__,verbose=True)
#@-<< imports >>

#@+others
#@+node:ekr.20111104210837.9693: ** init
def init():

    ok = path and win32clipboard
        # Ok for unit testing.
        
    if ok:
        g.registerHandler("after-create-leo-frame",onCreate)
    else:
        if not g.app.unitTesting:
            s = 'at_view plugin not loaded: win32Clipboard not present.'
            g.es_print(s)

    return ok
#@+node:ktenney.20041211072654.6: ** onCreate
def onCreate(tag, keywords):

    c = keywords.get("c")
    if not c: return
    myView = View(c)

    # Register the handlers...
    g.registerHandler("icondclick2", myView.icondclick2)
    g.registerHandler("idle", myView.idle)
    g.plugin_signon(__name__)
#@+node:ktenney.20041211072654.7: ** class View
class View:

    '''A class to support @view, @strip and @clip nodes.'''

    #@+others
    #@+node:ktenney.20041211072654.8: *3* __init__
    def __init__ (self,c):

        self.c = c
        # g.trace('View')
    #@+node:ktenney.20041211072654.9: *3* icondclick2
    def icondclick2 (self, tag, keywords):

        self.current = self.c.p
        hs = self.current.h

        g.trace(hs)

        if hs.startswith('@view'):
            self.view()

        if hs.startswith('@strip'):
            self.strip()
    #@+node:ktenney.20041211203715: *3* idle
    def idle(self, tag, keywords):

        try:
            self.current = self.c.p
        except AttributeError:
            # c has been destroyed.
            return

        s = self.current.h
        if s.startswith("@clip"):
            self.clip()
    #@+node:ktenney.20041211072654.10: *3* view
    def view(self):

        '''Place the contents of a file in the body pane

        the file is either in the current headstring, 
        or found by ascending the tree
        '''

        # get a path object for this position
        currentPath = self.getCurrentPath()

        # g.trace(currentPath.exists(),currentPath)

        if currentPath.exists():
            g.es('currentPath: %s' % currentPath.abspath())
            if currentPath.isfile():
                self.processFile(currentPath, self.current)

            if currentPath.isdir():
                self.processDirectory(currentPath, self.current)
        else:
            g.es('path does not exist: %s' % (str(currentPath)),color='blue')
    #@+node:ktenney.20041212102137: *3* clip
    def clip(self):

        '''Watch the clipboard, and copy new items to the body.'''

        if not win32clipboard:
            return

        c = self.c
        divider = '\n' + ('_-' * 34) + '\n'
        win32clipboard.OpenClipboard()
        clipboard = ""
        if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_TEXT):
            clipboard = win32clipboard.GetClipboardData()
        else:
            banner = '*' * 8
            clipboard = banner + 'Image data was copied to the clipboard' + banner

        win32clipboard.CloseClipboard()

        body = self.current.b.split(divider)
        if not body[0] == clipboard:
            g.es('clipboard now holds %s' % clipboard)
            body.insert(0, clipboard)
            c.setBodyText(self.current,divider.join(body))
    #@+node:ktenney.20041211072654.15: *3* strip
    def strip(self):

        '''Display a file with all sentinel lines removed'''

        # get a path object for this position
        c = self.c
        currentPath = self.getCurrentPath()

        # g.trace(currentPath.exists(),currentPath)

        if currentPath.exists():
            path = currentPath.abspath()
            s = 'currentPath: %s' % path
            g.es_print(s)
            filelines = path.lines()
            # Add an @ignore directive.
            lines = ['@ignore\n']
            verbatim = False
            for line in filelines:
                if verbatim:
                    lines.append(line)
                    verbatim = False
                elif line.strip().startswith('#@verbatim'):
                    verbatim = True
                elif not line.strip().startswith('#@'):
                    lines.append(line)
            c.setBodyText(self.current,''.join(lines))
        else:
            g.es('path does not exist: %s' % (str(currentPath)),color='blue')
    #@+node:ktenney.20041211072654.11: *3* getCurrentPath
    def getCurrentPath(self):

        """ traverse the current tree and build a path
            using all @path statements found
        """
        pathFragments = []

        # we are currently in a @view node; get the file or directory name
        pathFragments.append(self.getPathFragment(self.current))

        for p in self.current.parents():
            pathFragments.append(self.getPathFragment(p))

        if pathFragments:
            currentPath = path.path(pathFragments.pop())
            while pathFragments:
                # pop takes the last appended, which is at the top of the tree
                # build a path from the fragments
                currentPath = currentPath / path.path(pathFragments.pop())

        return currentPath.normpath()
    #@+node:ktenney.20041211072654.12: *3* getPathFragment
    def getPathFragment (self,p):

        """
        Return the path fragment if this node is a @path or @view or any @file node.
        """

        head = p.h

        for s in ('@path','@view','@strip','@file','@thin','@nosent','@asis'):
            if head.startswith(s):
                fragment = head [head.find(' '):].strip()
                # g.trace(repr(fragment))
                return fragment

        return ''
    #@+node:ktenney.20041211072654.13: *3* processFile
    def processFile(self, path, node):

        """parameters are a path object and a node.
           the path is a file, place it's contents into the node
        """

        g.trace(node)

        self.c.setBodyText(node,''.join(path.lines()))
    #@+node:ktenney.20041211072654.14: *3* processDirectory
    def processDirectory(self, path, node):

        """
        create child nodes for each member of the directory

        @path is a path object for a directory
        @node is the node to work with
        """

        c = self.c

        # delete all nodes before creating, to avoid duplicates
        while node.firstChild():
            node.firstChild().doDelete(node)

        for file in path.files():
            child = node.insertAsLastChild()
            c.setHeadString(child,'@view %s' % file.name)

        for file in path.dirs():
            child = node.insertAsLastChild()
            c.setHeadString(child,'@view %s' % file.name)
    #@-others
#@-others
#@-leo
