class OptionsPage(BookPage):
    notify = directNotify.newCategory("OptionsPage")

    # Keys are categories and values are x-positions for the text.
    Categories = OrderedDict()
    Categories[AboutCategory] = 0.09
    Categories[GeneralCategory] = 0.07
    Categories[ControlsCategory] = 0.05
    Categories[SoundCategory] = 0.0825
    Categories[DisplayCategory] = 0.07
    Categories[AdvancedDisplayCategory] = 0.05

    def __init__(self, book):
        BookPage.__init__(self, book, "Options")
        self.currentCategory = None
        self.tabs = []

    def load(self):
        BookPage.load(self)
        icons = loader.loadModel('phase_3.5/models/gui/sos_textures.bam')
        self.icon = icons.find('**/switch')
        icons.detachNode()

    def enter(self):
        BookPage.enter(self)
        self.tabScaleFrame = DirectFrame(parent=self.book)
        self.tabScaleFrame.setZ(0.7683)
        self.tabsFrame = DirectFrame(parent=self.tabScaleFrame)

        tabWidth = 0.379136800766
        spacing = 0.075

        totalWidth = 0.0
        bookWidth = 2.0

        for i in xrange(len(self.Categories.keys())):
            if i > 0:
                totalWidth += spacing
            totalWidth += tabWidth
            cl = self.Categories.keys()[i]
            tab = CategoryTab(self, cl.Name, [cl],
                              ((tabWidth + spacing) * i, 0, 0),
                              self.Categories.values()[i])
            self.tabs.append(tab)

        self.tabsFrame.setX(totalWidth / -2.0)
        self.tabScaleFrame.setScale(min(1.0, 1.0 / (totalWidth / bookWidth)))

        self.pickCategory(AboutCategory)

    def closeWindow(self):
        if self.currentCategory:
            self.currentCategory.cleanup()
            self.currentCategory = None

    def pickCategory(self, cat):
        if self.currentCategory:
            self.currentCategory.cleanup()
            self.currentCategory = None
        self.currentCategory = cat(self)
        for tab in self.tabs:
            if tab['extraArgs'][0] is cat:
                tab['state'] = DGG.DISABLED
            else:
                tab['state'] = DGG.NORMAL
        #self.currentCategory.show()

    def exit(self):
        if self.currentCategory:
            self.currentCategory.cleanup()
            self.currentCategory = None
        for tab in self.tabs:
            tab.destroy()
        self.tabsFrame.destroy()
        del self.tabsFrame
        self.tabScaleFrame.destroy()
        del self.tabScaleFrame
        self.tabs = []
        BookPage.exit(self)
Ejemplo n.º 2
0
class GagSelectionGui(DirectFrame, FSM):
    InactivityTime = 5.0

    AmmoZSelect = -0.15
    AmmoZIdle = 0.035

    def __init__(self):
        DirectFrame.__init__(self,
                             parent=aspect2d,
                             pos=(0, 0, 0.93),
                             scale=0.7)
        FSM.__init__(self, 'GagSelectionGui')
        self.setTransparency(TransparencyAttrib.MDual)
        self.tracks = []
        self.currentTrack = 0
        self.currentGag = None
        self.fwdShakeIval = None
        self.revShakeIval = None
        self.newTrackSound = None
        self.keyScrollSound = None
        self.selectSound = None
        self.selectDenySound = None
        self.lastActivityTime = 0.0
        self.activityTask = None
        self.midpoint = 0.0

        self.ammoFrame = DirectFrame(parent=self,
                                     pos=(0, 0, -0.2),
                                     image='phase_14/maps/status_bar.png',
                                     image_scale=(0.461 * 0.7, 0, 0.098),
                                     relief=None)
        self.ammoFrame.hide()
        self.ammoTitle = OnscreenText(parent=self.ammoFrame,
                                      text='SUPPLY',
                                      fg=(0, 0, 0, 0.65),
                                      align=TextNode.ALeft,
                                      pos=(-0.37 * 0.7, -0.015, 0))
        self.ammoText = OnscreenText(parent=self.ammoFrame,
                                     text='',
                                     fg=(1, 1, 1, 1),
                                     shadow=(0, 0, 0, 1),
                                     align=TextNode.ARight,
                                     pos=(0.37 * 0.7, -0.015, 0))

    def update(self):
        plyr = base.localAvatar

        if not base.localAvatar.hasAttacks():
            return

        gagId = -1
        if self.getCurrentOrNextState() == 'Idle':
            gagId = plyr.getEquippedAttack()
        elif self.getCurrentOrNextState() == 'Select' and self.currentGag:
            gagId = self.currentGag.gagId

        if gagId != -1:
            self.ammoFrame.showThrough()
            if plyr.hasAttackId(gagId):
                self.ammoText.setText(
                    '%i/%i' %
                    (plyr.getAttackAmmo(gagId), plyr.getAttackMaxAmmo(gagId)))
            else:
                self.ammoText.setText('')
            col = GagGlobals.TrackColorByName[GagGlobals.getTrackOfGag(gagId)]
            self.ammoFrame['image_color'] = (col[0], col[1], col[2], 1.0)
        else:
            self.ammoFrame.hide()

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def hide(self):
        showAmmo = False
        if not self.ammoFrame.isHidden():
            showAmmo = True
        DirectFrame.hide(self)
        if showAmmo:
            self.ammoFrame.showThrough()

    def enterSelect(self):
        base.localAvatar.disableGagKeys()
        self.ammoFrame.setZ(self.AmmoZSelect)
        self.show()
        self.update()
        self.acceptSelectionClick()
        self.resetTimeout()
        self.activityTask = taskMgr.add(self.__activityTask, "activityTask")

    def acceptSelectionClick(self):
        self.accept('mouse1-up', self.selectCurrentGag)

    def ignoreSelectionClick(self):
        self.ignore('mouse1-up')

    def resetTimeout(self):
        self.lastActivityTime = globalClock.getFrameTime()

    def __activityTask(self, task):
        time = globalClock.getFrameTime()
        if time - self.lastActivityTime >= self.InactivityTime:
            self.request('Idle')
            return task.done
        return task.cont

    def exitSelect(self):
        self.activityTask.remove()
        self.activityTask = None
        self.hide()
        self.ignoreSelectionClick()

    def enterIdle(self):
        self.ammoFrame.setZ(self.AmmoZIdle)
        self.hide()
        self.update()
        if base.localAvatar.avatarMovementEnabled:
            base.localAvatar.enableGagKeys()

    def exitIdle(self):
        pass

    def disable(self):
        self.disableControls()
        self.hide()

    def enable(self):
        self.enableControls()
        self.show()

    def cleanup(self):
        self.request('Off')

        self.disableControls()

        self.newTrackSound = None
        self.keyScrollSound = None
        self.selectSound = None
        self.selectDenySound = None

        if self.fwdShakeIval:
            self.fwdShakeIval.finish()
            self.fwdShakeIval = None
        if self.revShakeIval:
            self.revShakeIval.finish()
            self.revShakeIval = None
        self.currentTrack = None
        self.currentGag = None
        if self.tracks:
            for track in self.tracks:
                track.cleanup()
        self.tracks = None

        self.destroy()

    def __accumulateTracks(self):
        tracks = []
        for gagId in base.localAvatar.attacks.keys():
            trackId = GagGlobals.getTrackOfGag(gagId, getId=True)
            if trackId not in tracks:
                tracks.append(trackId)
        tracks.sort()
        return tracks

    def load(self):
        tracks = self.__accumulateTracks()
        for i in xrange(len(tracks)):
            track = GagTrack(self, tracks[i])
            track.load()
            track.reparentTo(self)
            track.setX(FRAME_OFFSET * i)
            self.tracks.append(track)

        self.midpoint = (len(self.tracks) / 2.0) * -FRAME_OFFSET
        # Center the gui horizontally
        self.setX(self.midpoint)
        self.ammoFrame.setX(-self.midpoint)

        if base.config.GetBool('gsg-want-hlsounds', False):
            self.newTrackSound = base.loadSfx(
                "phase_14/audio/sfx/wpn_hudon.ogg")
            self.keyScrollSound = base.loadSfx(
                'phase_14/audio/sfx/wpn_moveselect.ogg')
            self.selectSound = base.loadSfx(
                'phase_14/audio/sfx/wpn_select.ogg')
            self.selectDenySound = base.loadSfx(
                'phase_14/audio/sfx/wpn_denyselect.ogg')
        else:
            self.newTrackSound = base.loadSfx(
                "phase_3/audio/sfx/GUI_create_toon_back.ogg")
            self.keyScrollSound = base.loadSfx(
                'phase_3/audio/sfx/GUI_rollover.ogg')
            self.selectSound = base.loadSfx(
                'phase_3/audio/sfx/GUI_create_toon_fwd.ogg')
            self.selectDenySound = base.loadSfx(
                'phase_4/audio/sfx/ring_miss.ogg')

        self.fwdShakeIval = Effects.createXBounce(self, 1,
                                                  Vec3(self.midpoint, 0, 0.93),
                                                  0.05, 0.05)
        self.revShakeIval = Effects.createXBounce(self, 1,
                                                  Vec3(self.midpoint, 0, 0.93),
                                                  0.05, -0.05)

        if base.localAvatar.hasAttacks():
            self.updateCurrentTrack(0)

    def enableControls(self):
        self.accept('wheel_up', self.__handleScrollUp)
        self.accept('wheel_down', self.__handleScrollDown)

        for i in xrange(len(self.tracks)):
            self.accept(str(i + 1), self.__handleTrackChoose, [i])

        self.request('Idle')

    def selectCurrentGag(self):
        selected = False

        self.newTrackSound.stop()
        self.keyScrollSound.stop()

        if self.currentGag is not None:
            if base.localAvatar.getEquippedAttack() == self.currentGag.gagId:
                selected = True
            elif (not self.currentGag.locked and self.currentGag.hasAmmo()):
                gagId = self.currentGag.gagId
                base.localAvatar.needsToSwitchToGag = gagId
                if base.localAvatar.gagsTimedOut == False:
                    base.localAvatar.selectGag(gagId)
                    selected = True

        if not selected:
            # Denied!
            self.selectDenySound.play()
            self.resetTimeout()
        else:
            self.selectSound.play()
            self.request('Idle')

    def disableControls(self):
        self.ignore('wheel_up')
        self.ignore('wheel_down')

        for i in xrange(len(self.tracks)):
            self.ignore(str(i + 1))

        self.request('Idle')

    def __maybeDoSelect(self):
        if self.getCurrentOrNextState() == 'Idle':
            self.request('Select')

    def __handleTrackChoose(self, idx):
        if not base.localAvatar.hasAttacks():
            return

        self.__maybeDoSelect()
        self.resetTimeout()

        if self.currentTrack == idx:
            # Scroll through the current track.
            self.tracks[self.currentTrack].selectNextGag()

            self.newTrackSound.stop()
            self.keyScrollSound.play()
        else:
            # Always start from the beginning when using the keys to choose a track.
            self.updateCurrentTrack(idx, 0)
            self.newTrackSound.play()

    def __handleScrollUp(self):
        if not base.localAvatar.hasAttacks():
            return

        self.__maybeDoSelect()
        self.resetTimeout()

        track = self.tracks[self.currentTrack]
        if track.isOnFirstGag():
            self.prevTrack()
        else:
            track.selectPrevGag()

        self.newTrackSound.stop()
        self.keyScrollSound.play()

    def __handleScrollDown(self):
        if not base.localAvatar.hasAttacks():
            return

        self.__maybeDoSelect()
        self.resetTimeout()

        track = self.tracks[self.currentTrack]
        if track.isOnLastGag():
            self.nextTrack()
        else:
            track.selectNextGag()

        self.newTrackSound.stop()
        self.keyScrollSound.play()

    def nextTrack(self):
        newIdx = self.currentTrack + 1
        if newIdx > len(self.tracks) - 1:
            newIdx = 0

        self.updateCurrentTrack(newIdx)

    def prevTrack(self):
        newIdx = self.currentTrack - 1
        if newIdx < 0:
            newIdx = len(self.tracks) - 1

        self.updateCurrentTrack(newIdx)

    def updateCurrentTrack(self, idx, startLoc=None):
        oldTrack = self.tracks[self.currentTrack]
        oldTrack.deselectAll()
        oldTrack.stashContents()

        if idx - self.currentTrack < 0:
            direction = 1
        else:
            direction = 0

        if startLoc is None:
            startLoc = direction

        if direction == 0:
            self.fwdShakeIval.start()
        else:
            self.revShakeIval.start()

        self.currentTrack = idx

        # Resort the tracks
        numTracks = len(self.tracks)
        maxTrack = numTracks - 1
        for i in xrange(len(self.tracks)):
            track = self.tracks[i]

            if i == idx:
                sort = FRAME_FRONT_SORT
            elif i > idx:
                sort = FRAME_SORT_BEGIN + (maxTrack - i) * FRAME_SORT_DISTANCE
            elif i < idx:
                sort = FRAME_SORT_BEGIN + (i * FRAME_SORT_DISTANCE)
            track.setBin('gsg-popup', sort)

            if i == idx:
                track.unstashContents()
                if startLoc == 0:
                    track.selectFirstGag()
                else:
                    track.selectLastGag()
            else:
                track.stashContents()
Ejemplo n.º 3
0
class DropDownMenu(DirectObject):
    ALeft = 0
    ACenter = 1
    ARight = 2
    ENone = 0
    EFade = 1
    ESlide = 2
    EStretch = 3
    PLeft = 0
    PRight = 1
    PBottom = 2
    PTop = 3
    parents = (('a2dBottomLeft', 'a2dLeftCenter', 'a2dTopLeft'),
               ('a2dTopRight', 'a2dRightCenter', 'a2dBottomRight'),
               ('a2dBottomLeft', 'a2dBottomCenter', 'a2dBottomRight'),
               ('a2dTopLeft', 'a2dTopCenter', 'a2dTopRight'))

    def __init__(self,
                 items,
                 parent=None,
                 sidePad=.0,
                 edgePos=PTop,
                 align=ALeft,
                 effect=ENone,
                 buttonThrower=None,
                 font=None,
                 baselineOffset=.0,
                 scale=.05,
                 itemHeight=1.,
                 leftPad=.0,
                 separatorHeight=.5,
                 underscoreThickness=1,
                 BGColor=(0, 0, 0, .7),
                 BGBorderColor=(1, .85, .4, 1),
                 separatorColor=(1, 1, 1, 1),
                 frameColorHover=(1, .85, .4, 1),
                 frameColorPress=(0, 1, 0, 1),
                 textColorReady=(1, 1, 1, 1),
                 textColorHover=(0, 0, 0, 1),
                 textColorPress=(0, 0, 0, 1),
                 textColorDisabled=(.5, .5, .5, 1),
                 draggable=False,
                 onMove=None):
        '''
      sidePad : additional space on the left and right of the text item
      edgePos : menu bar position on the screen,
                  use DropDownMenu.PLeft, PRight, PBottom, or PTop
      align   : menu items alignment on menu bar,
                  use DropDownMenu.ALeft, ACenter, or ARight
      effect  : the drop down appearance effect,
                  use DropDownMenu.ENone, EFade, ESlide, or EStretch
      draggable : menu bar's draggability status
      onMove : a function which will be called after changing edge position

      Read the remaining options documentation in PopupMenu class.
      '''
        self.parent = parent if parent else getattr(
            base, DropDownMenu.parents[edgePos][align])
        self.BT = buttonThrower if buttonThrower else base.buttonThrowers[
            0].node()
        self.menu = self.parent.attachNewNode('dropdownmenu-%s' % id(self))
        self.font = font if font else TextNode.getDefaultFont()
        self.baselineOffset = baselineOffset
        self.scale = scale
        self.itemHeight = itemHeight
        self.sidePad = sidePad
        self.edgePos = edgePos
        self.alignment = align
        self.effect = effect
        self.leftPad = leftPad
        self.underscoreThickness = underscoreThickness
        self.separatorHeight = separatorHeight
        self.BGColor = BGColor
        self.BGBorderColor = BGBorderColor
        self.separatorColor = separatorColor
        self.frameColorHover = frameColorHover
        self.frameColorPress = frameColorPress
        self.textColorReady = textColorReady
        self.textColorHover = textColorHover
        self.textColorPress = textColorPress
        self.textColorDisabled = textColorDisabled
        self.draggable = draggable
        self.onMove = onMove
        self.dropDownMenu = self.whoseDropDownMenu = None

        self.gapFromEdge = gapFromEdge = .008
        texMargin = self.font.getTextureMargin() * self.scale * .25
        b = DirectButton(parent=NodePath(''),
                         text='^|g_',
                         text_font=self.font,
                         scale=self.scale)
        fr = b.node().getFrame()
        b.getParent().removeNode()
        baselineToCenter = (fr[2] + fr[3]) * self.scale
        LH = (fr[3] - fr[2]) * self.itemHeight * self.scale
        baselineToTop = (fr[3] * self.itemHeight * self.scale /
                         LH) / (1. + self.baselineOffset)
        baselineToBot = LH / self.scale - baselineToTop
        self.height = LH + .01
        l, r, b, t = 0, 5, -self.height, 0
        self.menuBG = DirectFrame(parent=self.menu,
                                  frameColor=BGColor,
                                  frameSize=(l, r, b, t),
                                  state=DGG.NORMAL,
                                  suppressMouse=1)
        if self.draggable:
            self.setDraggable(1)
        LSborder = LineSegs()
        LSborder.setThickness(2)
        LSborder.setColor(0, 0, 0, 1)
        LSborder.moveTo(l, 0, b)
        LSborder.drawTo(r, 0, b)
        self.menuBG.attachNewNode(LSborder.create())
        self.itemsParent = self.menu.attachNewNode('menu items parent')

        x = sidePad * self.scale + gapFromEdge
        for t, menuItemsGenerator in items:
            underlinePos = t.find('_')
            t = t.replace('_', '')
            b = DirectButton(
                parent=self.itemsParent,
                text=t,
                text_font=self.font,
                pad=(sidePad, 0),
                scale=self.scale,
                pos=(x, 0, -baselineToTop * self.scale - gapFromEdge),
                text_fg=textColorReady,
                # text color when mouse over
                text2_fg=textColorHover,
                # text color when pressed
                text1_fg=textColorPress,
                # framecolor when pressed
                frameColor=frameColorPress,
                command=self.__createMenu,
                extraArgs=[True, menuItemsGenerator],
                text_align=TextNode.ALeft,
                relief=DGG.FLAT,
                rolloverSound=0,
                clickSound=0)
            b['extraArgs'] += [b.getName()]
            b.stateNodePath[2].setColor(
                *frameColorHover)  # framecolor when mouse over
            b.stateNodePath[0].setColor(0, 0, 0, 0)  # framecolor when ready
            fr = b.node().getFrame()
            b['frameSize'] = (fr[0], fr[1], -baselineToBot, baselineToTop)
            self.accept(DGG.ENTER + b.guiId, self.__createMenu,
                        [False, menuItemsGenerator,
                         b.getName()])
            if underlinePos > -1:
                tn = TextNode('')
                tn.setFont(self.font)
                tn.setText(t[:underlinePos + 1])
                tnp = NodePath(tn.getInternalGeom())
                underlineXend = tnp.getTightBounds()[1][0]
                tnp.removeNode()
                tn.setText(t[underlinePos])
                tnp = NodePath(tn.getInternalGeom())
                b3 = tnp.getTightBounds()
                underlineXstart = underlineXend - (b3[1] - b3[0])[0]
                tnp.removeNode()
                LSunder = LineSegs()
                LSunder.setThickness(underscoreThickness)
                LSunder.moveTo(underlineXstart + texMargin, 0,
                               -.7 * baselineToBot)
                LSunder.drawTo(underlineXend - texMargin, 0,
                               -.7 * baselineToBot)
                underline = b.stateNodePath[0].attachNewNode(LSunder.create())
                underline.setColor(Vec4(*textColorReady), 1)
                underline.copyTo(b.stateNodePath[1],
                                 10).setColor(Vec4(*textColorPress), 1)
                underline.copyTo(b.stateNodePath[2],
                                 10).setColor(Vec4(*textColorHover), 1)
                self.accept('alt-' + t[underlinePos].lower(),
                            self.__createMenu,
                            [True, menuItemsGenerator,
                             b.getName()])
            x += (fr[1] - fr[0]) * self.scale
        self.width = x - 2 * gapFromEdge
        self.align(align)
        self.setEdgePos(edgePos)
        self.minZ = base.a2dBottom + self.height if edgePos == DropDownMenu.PBottom else None
        viewPlaneNode = PlaneNode('cut menu')
        viewPlaneNode.setPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, -LH)))
        self.clipPlane = self.menuBG.attachNewNode(viewPlaneNode)

    def __createMenu(self, clicked, menuItemsGenerator, DBname, crap=None):
        itself = self.dropDownMenu and self.whoseDropDownMenu == DBname
        if not (clicked or self.dropDownMenu) or (not clicked and itself):
            return
        self.__removeMenu()
        if clicked and itself: return
        # removes any context menu
        if clicked:
            self.__removePopupMenu()
        self.dropDownMenu = PopupMenu(
            items=menuItemsGenerator(),
            parent=self.parent,
            buttonThrower=self.BT,
            font=self.font,
            baselineOffset=self.baselineOffset,
            scale=self.scale,
            itemHeight=self.itemHeight,
            leftPad=self.leftPad,
            separatorHeight=self.separatorHeight,
            underscoreThickness=self.underscoreThickness,
            BGColor=self.BGColor,
            BGBorderColor=self.BGBorderColor,
            separatorColor=self.separatorColor,
            frameColorHover=self.frameColorHover,
            frameColorPress=self.frameColorPress,
            textColorReady=self.textColorReady,
            textColorHover=self.textColorHover,
            textColorPress=self.textColorPress,
            textColorDisabled=self.textColorDisabled,
            minZ=self.minZ)
        self.acceptOnce(self.dropDownMenu.BTprefix + 'destroyed', setattr,
                        [self, 'dropDownMenu', None])
        self.whoseDropDownMenu = DBname
        item = self.menu.find('**/%s' % DBname)
        fr = item.node().getFrame()
        #~ if self.edgePos==DropDownMenu.PLeft:
        #~ x=max(fr[1],self.dropDownMenu.menu.getX(item))
        #~ z=fr[2]
        #~ elif self.edgePos==DropDownMenu.PRight:
        #~ x=min(fr[0],self.dropDownMenu.menu.getX(item))
        #~ z=fr[2]-self.dropDownMenu.maxWidth
        #~ elif self.edgePos in (DropDownMenu.PBottom,DropDownMenu.PTop):
        #~ x=fr[1]-self.dropDownMenu.maxWidth if self.alignment==DropDownMenu.ARight else fr[0]
        #~ z=fr[2] if self.edgePos==DropDownMenu.PTop else fr[3]+self.dropDownMenu.height
        if self.edgePos == DropDownMenu.PLeft:
            x = max(fr[1], self.dropDownMenu.menu.getX(item))
            z = fr[3] - (self.height - self.gapFromEdge) / self.scale
        elif self.edgePos == DropDownMenu.PRight:
            x = min(fr[0], self.dropDownMenu.menu.getX(item))
            z = fr[3] - (self.height - self.gapFromEdge
                         ) / self.scale - self.dropDownMenu.maxWidth
        elif self.edgePos in (DropDownMenu.PBottom, DropDownMenu.PTop):
            x = fr[
                1] - self.dropDownMenu.maxWidth if self.alignment == DropDownMenu.ARight else fr[
                    0]
            z = fr[3] - (
                self.height - self.gapFromEdge
            ) / self.scale if self.edgePos == DropDownMenu.PTop else fr[2] + (
                self.height) / self.scale + self.dropDownMenu.height
        self.dropDownMenu.menu.setPos(item, x, 0, z)

        if self.effect == DropDownMenu.EFade:
            self.dropDownMenu.menu.colorScaleInterval(
                .3, Vec4(1), Vec4(1, 1, 1, 0), blendType='easeIn').start()
        elif self.effect == DropDownMenu.ESlide:
            pos = self.dropDownMenu.menu.getPos()
            if self.edgePos == DropDownMenu.PTop:
                startPos = Point3(0, 0, self.dropDownMenu.height * self.scale)
            elif self.edgePos == DropDownMenu.PBottom:
                startPos = Point3(0, 0, -self.dropDownMenu.height * self.scale)
            elif self.edgePos == DropDownMenu.PLeft:
                startPos = Point3(-self.dropDownMenu.maxWidth * self.scale, 0,
                                  0)
            elif self.edgePos == DropDownMenu.PRight:
                startPos = Point3(self.dropDownMenu.maxWidth * self.scale, 0,
                                  0)
            self.dropDownMenu.menu.posInterval(.3,
                                               pos,
                                               pos + startPos,
                                               blendType='easeIn').start()
            self.dropDownMenu.menu.setClipPlane(self.clipPlane)
        elif self.effect == DropDownMenu.EStretch:
            if self.edgePos == DropDownMenu.PTop:
                startHpr = Vec3(0, -90, 0)
            elif self.edgePos == DropDownMenu.PBottom:
                dz = self.dropDownMenu.height * self.scale
                for c in asList(self.dropDownMenu.menu.getChildren()):
                    c.setZ(c.getZ() + dz)
                self.dropDownMenu.menu.setZ(self.dropDownMenu.menu, -dz)
                startHpr = Vec3(0, 90, 0)
            elif self.edgePos == DropDownMenu.PLeft:
                startHpr = Vec3(90, 0, 0)
            elif self.edgePos == DropDownMenu.PRight:
                dx = self.dropDownMenu.maxWidth * self.scale
                for c in asList(self.dropDownMenu.menu.getChildren()):
                    c.setX(c.getX() - dx)
                self.dropDownMenu.menu.setX(self.dropDownMenu.menu, dx)
                startHpr = Vec3(-90, 0, 0)
            self.dropDownMenu.menu.hprInterval(.3,
                                               Vec3(0),
                                               startHpr,
                                               blendType='easeIn').start()

    def __removeMenu(self):
        if self.dropDownMenu:
            self.dropDownMenu.destroy()
            self.dropDownMenu = None

    def __removePopupMenu(self):
        menuEvents = messenger.find('menu-')
        if menuEvents:
            menuEvent = menuEvents.keys()
            activeMenu = menuEvents[menuEvent[0]].values()[0][0].im_self
            activeMenu.destroy(delParents=True)

    def __reverseItems(self):
        tmp = NodePath('')
        self.itemsParent.getChildren().reparentTo(tmp)
        children = asList(tmp.getChildren())
        for c in reversed(children):
            c.reparentTo(self.itemsParent)
        tmp.removeNode()
        x = self.sidePad * self.scale + self.gapFromEdge
        for c in asList(self.itemsParent.getChildren()):
            c.setX(x)
            fr = c.node().getFrame()
            x += (fr[1] - fr[0]) * self.scale

    def __startDrag(self, crap):
        taskMgr.add(self.__drag,
                    'dragging menu bar',
                    extraArgs=[Point2(base.mouseWatcherNode.getMouse())])
        self.__removePopupMenu()
        self.origBTprefix = self.BT.getPrefix()
        self.BT.setPrefix('dragging menu bar')

    def __drag(self, origMpos):
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            if mpos != origMpos:
                x, y = mpos.getX(), mpos.getY(),
                deltas = [x + 1, 1 - x, y + 1, 1 - y]
                closestEdge = deltas.index(min(deltas))
                if closestEdge != self.edgePos:
                    self.setEdgePos(closestEdge)
        return Task.cont

    def __stopDrag(self, crap):
        taskMgr.remove('dragging menu bar')
        self.BT.setPrefix(self.origBTprefix)

    def destroy(self):
        self.__removeMenu()
        self.ignoreAll()
        self.menu.removeNode()

    def isDraggable(self):
        '''
      Returns menu bar's draggable status
      '''
        return self.draggable

    def setDraggable(self, d):
        '''
      Sets menu bar's draggable status
      '''
        self.draggable = d
        if d:
            self.menuBG.bind(DGG.B1PRESS, self.__startDrag)
            self.menuBG.bind(DGG.B1RELEASE, self.__stopDrag)
        else:
            self.menuBG.unbind(DGG.B1PRESS)
            self.menuBG.unbind(DGG.B1RELEASE)

    def align(self, align=None):
        '''
      Aligns menu text on menu bar.
      Use one of DropDownMenu.ALeft, DropDownMenu.ACenter, or DropDownMenu.ARight.
      '''
        self.parent = getattr(
            base, DropDownMenu.parents[self.edgePos]
            [self.alignment if align is None else align])
        self.menu.reparentTo(self.parent)
        if align is not None:
            self.itemsParent.setX(-.5 * self.width * align)
            self.menuBG.setX(-.5 * (5 - self.width) * align)
            self.alignment = align

    def setEdgePos(self, edge):
        '''
      Sticks menu bar to 4 possible screen edges :
          DropDownMenu.PLeft, DropDownMenu.PRight,
          DropDownMenu.PBottom, or DropDownMenu.PTop.
      '''
        lastEdge = self.edgePos
        reverseItems = lastEdge == DropDownMenu.PLeft
        self.edgePos = edge
        self.itemsParent.setZ(0)
        self.menuBG.setSz(1)
        alignment = None
        if self.edgePos == DropDownMenu.PLeft:
            self.menu.setR(-90)
            if self.alignment != DropDownMenu.ACenter:
                alignment = 2 - self.alignment
            self.__reverseItems()
            self.minZ = None
        else:
            if self.edgePos == DropDownMenu.PRight:
                self.menu.setR(90)
                self.minZ = None
            elif self.edgePos == DropDownMenu.PBottom:
                self.menu.setR(0)
                self.menuBG.setSz(-1)
                self.itemsParent.setZ(-self.menuBG.node().getFrame()[2])
                self.minZ = base.a2dBottom + self.height
            elif self.edgePos == DropDownMenu.PTop:
                self.menu.setR(0)
                self.minZ = None
            if reverseItems:
                if self.alignment != DropDownMenu.ACenter:
                    alignment = 2 - self.alignment
                self.__reverseItems()
        self.align(alignment)
        if callable(self.onMove):
            self.onMove()