Interactive/InCanvasWebCustom.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.


# Change the home page and size, then use the Display->Canvas UI to
# show and hide it.  The click away.

kAnchorNone = "none"
kAnchorBL = "bottomLeft"
kAnchorBR = "bottomRight"
kAnchorTL = "topLeft"
kAnchorTR = "topRight"

# Unique ID, page, 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.

kWhatToShow = (
    ("canvas1", "http://www.google.com/", (kAnchorBL, 0, 0), (20,20), (256,400)),
    ("canvas2", "http://www.autodesk.com/", (kAnchorBR, 0, 0), (20,20), (256,400)),
)



from UserCustomization  import UserCustomBase
from RTFapi             import Event, Id, NodeRef, WebContainer
from UI                 import ControlState, getID, ImageButton
from MessageInterpreter import MessageInterpreter
from ActionRegistry     import theActionRegistry
from RunInUiThread      import RunInUiThread
from UiSelector         import OverlayUiNew

kCanvasKeyMappingId = 'CanvasKeys'

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 InCanvasUi(OverlayUiNew):
    kCanvasUiUniqueId = "InCanvasCustom.Example"

    def __init__(self):
        self.myWidth = 0
        self.myHeight = 0
        self.myInCanvas = {}
        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 kAnchorNone == anchor:
            pass
        elif 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 makeBrowser(self):
        wc = WebContainer.instance()
        for (name,url,anchor,loc,sz) in kWhatToShow:
            id = self._id(name)
            br = wc.browser(id)
            if br is None:
                id = wc.createBrowser( id.getFullId(), sz[0], sz[1] )
            br = wc.browser(id)
            if br is not None:
                br.navigate( url )


    def _id(self,name):
        return Id("mem:WEB:%s" % (name))

    def doCreate(self):
        self.makeBrowser()

        for (name,url,anchor,loc,sz) in kWhatToShow:
            id = self._id(name)
            self.myPosition[name] = self.anchorToAbsolute(anchor[0],anchor[1],
                                                         anchor[2],loc,sz)
            self.myInCanvas[name] = \
                MyImageButton( self.__onInCanvasClick,
                               {ControlState.kNormal    : id.getFullId(),
                                ControlState.kHighlight : id.getFullId(),
                                ControlState.kPress     : id.getFullId()},
                               sz)
            self.getControls().append(self.myInCanvas[name])
        for control in self.getControls():
            self.getRoot().insertChild(NodeRef(control.getNode().get()))


    def doRefresh(self):
        wc = WebContainer.instance()
        for (name,url,anchor,loc,sz) in kWhatToShow:
            id = self._id(name)
            br = wc.browser(id)
            if br is not None:
                br.navigate( url )


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


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


    def __onInCanvasClick(self, x, y):
        for (name,url,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])
                id = self._id(name)
                br = WebContainer.instance().browser(id)
                if br is not None:
                    br.mouseDownRel(normX,normY)
                    br.mouseUpRel(normX,normY)
                break


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


    def __setPosition(self):
        if self.isVisible():
            for (name,url,anchor,loc,sz) in kWhatToShow:
                inc = self.myInCanvas[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)
            if not visible:
                self.doRefresh()

    # 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 SET_DISPLAY( self, message ):
        (display,) = message.data
        (x, y, self.myWidth, self.myHeight) = display.getPositionAndSize(0)

    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 InCanvas( UserCustomBase ):
    def __init__( self ):
        self.myInterpreter = LocalInterpreter()
        self.myCanvasShowId = None
        self.myUniqueCanvasId = None
        self.myMenuId = None
        self.myMenu = None
        pass

    def getInterpreter( self, isInteractive ):
        if isInteractive:
            return self.myInterpreter
        return None


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


    def appendMenuItems( self, id, menu ):
        if self.myMenuId is None and ('Display' == id):
            self.myMenuId = id
            self.myMenu = menu
            self.myInterpreter.myMenu = menu
            menu.AppendSeparator()
            self.myCanvasShowId = menu.appendCheckItem( _( 'Canvas UI\tQ' ),
                                                        self.__onCanvasShow )
            self.myContDrawId = menu.appendCheckItem( _( 'Continuous Draw' ),
                                                      self.__onContDraw )
            theActionRegistry.register( Event.BUTTON_q,
                                        (),
                                        True,
                                        Event.BUTTONUP,
                                        'CanvasMode',
                                        'Global' )
            self.myInterpreter.myCanvasShowId = self.myCanvasShowId

    def enableMenuStates( self, id, enableStates ):
        if self.myMenuId == id:
            enableStates[self.myCanvasShowId] = True
            enableStates[self.myContDrawId] = True

    def resetMenuStates(self, id):
        if self.myMenuId == id:
            menuItemStates = {}
            menuItemStates[self.myCanvasShowId] = False
            menuItemStates[self.myContDrawId] = False
            self.myMenu.checkMenuItems(menuItemStates)

    def __onCanvasShow( self, event ):
        isOn = event.IsChecked()
        self.myInterpreter.myCanvasMode = isOn
        self.sendMessage( 'CUSTOM_OVERLAY_SET_MODE',
                          ( self.myUniqueCanvasId, isOn, ) )

    def __onContDraw( self, event ):
        isOn = event.IsChecked()
        self.myInterpreter.setContinuousDraw( isOn )


class LocalInterpreter( MessageInterpreter ):

    def __init__( self ):
        MessageInterpreter.__init__( self )
        self.myDisplay = None
        self.myCanvasMode = False

    def SET_DISPLAY( self, message ):
        ( self.myDisplay, ) = message.data

    def setContinuousDraw( self, state ):
        if self.myDisplay is not None:
            self.myDisplay.setContinuousDraw(state)

    def APPLICATION_CLOSE_SCENE( self, message ):
        self.myDisplay.setContinuousDraw(False)

    def CanvasMode( self, evt ):
        self.myCanvasMode = not self.myCanvasMode
        self.sendMessage( 'CUSTOM_OVERLAY_SET_MODE',
                          ( self.myUniqueCanvasId, self.myCanvasMode, ) )
        if self.myMenu is not None and self.myUniqueCanvasId is not None:
            RunInUiThread( self.myMenu.Check,
                           self.myCanvasShowId,
                           self.myCanvasMode )


def instantiate():
    return InCanvas()