Interactive/SlideShowCustom.py

# (C) Copyright 2012 Autodesk, Inc.  All rights reserved.
#
# Use of this software is subject to the terms of the Autodesk license
# agreement provided at the time of installation or download, or which
# otherwise accompanies this software in either electronic or hard copy
# form.

# Produce a series of images name Slide1.png, Slide2.png, etc., exactly the
# way PowerPoint would export as PNGs.  Set kSlideDirectory below to the
# location of the directory that contains all the slides.  If you have 
# slides that do not match the format mentioned above, modify kSlideNames.

kSlideDirectory = "C:/slides"
kSlideNames = "Slide%d.png"

# Switch to a backdrop environment, and use the Display->Show Custom Slideshow
# to show the controls (Display->Hide Custom Slideshow hides them).  The arrows
# (specify your own images and location, see kWhatToShow below) will get you
# to previous and next images as the backdrop.

# Unique ID, image, anchor, and percentage offset from the anchor (0 to 1),
# location in pixels (bottom left is zero) and size.  For the anchors,
# 0 is always the near edge, 1 is the far edge, and pixel location is for the
# opposite (e.g. top or right) corner.  If you don't want to anchor, just
# leave empty.

# The three images specified on each button are for inactive, hover and
# press state.  They can be the same.  If you specify just the file name
# images from support/Images directory will be found.  You can override
# this with your own images, by specifying an absolute path.  If using
# different images, make sure you change the size as appropriate.

kAnchorBL = "bottomLeft"
kAnchorBR = "bottomRight"
kAnchorTL = "topLeft"
kAnchorTR = "topRight"
kWhatToShow = (
    ("left", ("previous.tif", "previousHighlight.tif", "previousPress.tif"),
     (kAnchorBL, 0, 0), (20,20), (16,16)),
    ("right", ("next.tif", "nextHighlight.tif", "nextPress.tif"),
     (kAnchorBR, 0, 0), (20,20), (16,16)),
)


from UserCustomization  import UserCustomBase, CustomInfo
from RTFapi             import NodeRef
from UI                 import ControlState, getID, ImageButton, FindImages
from UiSelector         import OverlayUiNew
import os.path

class MyImageButton(ImageButton):
    def __init__(self, clickCallback, imagesToUse, winsize=(32,32)):
        assert(callable(clickCallback))
        ImageButton.__init__(self)
        self.myClickCallback = clickCallback
        self.myPressOffset = (0, 0)
        self.setImages(imagesToUse)
        self.setSize(winsize)
        self.setDraggable(False)
        self.setVisible(True)


    def detachCallbacks(self):
        self.myClickCallback = None


    def wantsDragData(self, dragData):
        return True


    def doRelease(self, uiEvent):
        ImageButton.doRelease(self, uiEvent)
        atX, atY = uiEvent.getPosition()
        offsetX, offsetY = self.myPressOffset
        x = int(round(atX - offsetX))
        y = int(round(atY - offsetY))
        self.myClickCallback(x, y)


class SlideShowUi(OverlayUiNew):
    kCanvasUiUniqueId = "SlideShowCustom.PreviousNext"

    def __init__(self):
        self.myCurrentSlide = -1
        self.myLastSlide = 0

        self.myWidth = 0
        self.myHeight = 0
        self.mySlideShow = {}
        self.myPosition = {}
        OverlayUiNew.__init__(self)
        self.getRoot().setUniqueId(getID(self.kCanvasUiUniqueId))
        self.doCreate()


    def anchorToAbsolute( self, anchor, percW, percH, loc, sz ):
        x = loc[0]
        y = loc[1]
        if kAnchorBL == anchor:
            x += percW*self.myWidth
            y += percH*self.myHeight
        elif kAnchorBR == anchor:
            x = (1.0-percW)*self.myWidth - sz[0] - x
            y += percH*self.myHeight
        elif kAnchorTL == anchor:
            x += percW*self.myWidth
            y = (1.0-percH)*self.myHeight - sz[1] - y
        elif kAnchorTR == anchor:
            x = (1.0-percW)*self.myWidth - sz[0] - x
            y = (1.0-percH)*self.myHeight - sz[1] - y
        return (x,y)


    def doCreate(self):
        for (name,image,anchor,loc,sz) in kWhatToShow:
            self.myPosition[name] = self.anchorToAbsolute(anchor[0],anchor[1],
                                                         anchor[2],loc,sz)
            self.mySlideShow[name] = \
                MyImageButton( self.__onSlideShowClick,
                               FindImages({
                                   ControlState.kNormal    : image[0],
                                   ControlState.kHighlight : image[1],
                                   ControlState.kPress     : image[2]}),
                               sz )
            self.getControls().append(self.mySlideShow[name])
        for control in self.getControls():
            self.getRoot().insertChild(NodeRef(control.getNode().get()))


    def doCleanup(self):
        for each in self.mySlideShow.values():
            each.detachCallbacks()
            self.getRoot().removeChild(NodeRef(each.getNode().get()))
        self.mySlideShow = {}


    def doLayout(self, position, size):
        self.layoutCommon(position, size)


    def __onSlideShowClick(self, x, y):
        for (name,image,anchor,loc,sz) in kWhatToShow:
            pos = self.myPosition[name]
            if (x > pos[0]) and (x < (pos[0]+sz[0])) and \
               (y > pos[1]) and (y < (pos[1]+sz[1])):
                normX = float(x - pos[0]) / float(sz[0])
                normY = float(y - pos[1]) / float(sz[1])
                self.__onClickSomething(name)
                break

    def __fileFromSlide(self, n):
        return os.path.join(kSlideDirectory,kSlideNames % (n))

    def __onClickSomething(self,what):
        if self.myCurrentSlide < 0:
            self.myCurrentSlide = 0
            cntr = 1
            while cntr > 0:
                f = self.__fileFromSlide(cntr)
                if not os.path.isfile(f):
                    break
                self.myLastSlide = cntr
                cntr += 1
        if self.myLastSlide <= 0:
            return

        if "left" == what:
            self.myCurrentSlide -= 1
        elif "right" == what:
            self.myCurrentSlide += 1
        if self.myCurrentSlide < 1:
            self.myCurrentSlide = self.myLastSlide
        if self.myCurrentSlide > self.myLastSlide:
            self.myCurrentSlide = 1
        self.updateCurrentSlide()


    def updateCurrentSlide(self):
        self.sendMessage( 'ENVIRONMENT_CHANGE_BACKDROP_IMAGE',
                          ((),self.__fileFromSlide(self.myCurrentSlide),(),))


    def __recomputePosition(self):
        for (name,image,anchor,loc,sz) in kWhatToShow:
            self.myPosition[name] = self.anchorToAbsolute(anchor[0],anchor[1],
                                                         anchor[2],loc,sz)


    def __setPosition(self):
        if self.isVisible():
            for (name,image,anchor,loc,sz) in kWhatToShow:
                inc = self.mySlideShow[name]
                inc.setPosition(self.myPosition[name])
                inc.doLayout()


    def __setVisibility(self, visible):
        if visible != self.isVisible():
            self.setVisible(visible)
            self.__setPosition()


    """
    If the layout of the interface was changing or dynamic,
    we would collect dirty status and update it in this
    function if self.isDirty() is True.
    def FRAME_SYNCHRONIZE_END(self, message)
    """

    def CUSTOM_OVERLAY_SET_MODE( self, message):
        (which, visible) = message.data
        if which == self.kCanvasUiUniqueId:
            self.__setVisibility(visible)

    # We do not mind showing this interface together with everything else,
    # so we do not need to listen to the other _UI_SET_MODE messages.

    def APPLICATION_CLOSE_SCENE( self, message ):
        self.__setVisibility(False)

    def APPLICATION_VIEW_POSITION_AND_SIZE_CHANGED( self, message ):
        # If the window resizes, reposition
        (x, y, self.myWidth, self.myHeight, s) = message.data
        self.__recomputePosition()
        self.__setPosition()




class SlideShow( UserCustomBase ):
    def __init__( self ):
        #UserCustomBase.__init__(self)
        self.myUniqueCanvasId = None


    def getInterpreter( self, isInteractive ):
        return None


    def getCustomUi( self ):
        ui = SlideShowUi()
        self.myUniqueCanvasId = ui.kCanvasUiUniqueId
        return (self.myUniqueCanvasId,ui)


    def appendMenuItems( self, id, menu ):
        if 'Story' == id:
            menu.AppendSeparator()
            self.myCanvasShowId = menu.appendItem(_( 'Show Custom Slideshow' ),
                                                    self.__onCanvasShow )
            self.myCanvasHideId = menu.appendItem(_( 'Hide Custom Slideshow' ),
                                                    self.__onCanvasHide )

    def enableMenuStates( self, id, enableStates ):
        if 'Story' == id:
            enableStates[self.myCanvasShowId] = True
            enableStates[self.myCanvasHideId] = True

    def __onCanvasShow( self, event ):
        self.sendMessage( 'CUSTOM_OVERLAY_SET_MODE',
                          ( self.myUniqueCanvasId, True, ) )

    def __onCanvasHide( self, event ):
        self.sendMessage( 'CUSTOM_OVERLAY_SET_MODE',
                          ( self.myUniqueCanvasId, False, ) )


def instantiate():
    return SlideShow()


def info():
    customInfo = CustomInfo()
    customInfo.vendor = 'Autodesk'
    customInfo.version = '1.0'
    customInfo.api = '2013'
    customInfo.shortInfo = "Switch to a backdrop environment."
    customInfo.longInfo = \
"""The controls for switching to a backdrop environment are shown by Story->Show Custom \
Slideshow and hidden by Story->Hide Custom Slideshow. The arrows will get you to previous \
and next images under "C:\slides\" as the backdrop.
"""
    return customInfo