예제 #1
0
    def createInterface(self):
        WxPandaShell.createInterface(self)

        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.libraryUI = LibraryUI(self.leftBarUpNB, -1, self.editor)
        self.leftBarUpNB.AddPage(self.libraryUI, 'Library')
        self.pandaObjUI = PandaObjUI(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.pandaObjUI, 'Panda Objects')
        self.storyObjUI = StoryObjUI(self.leftBarUpNB, -1, self.editor)
        self.leftBarUpNB.AddPage(self.storyObjUI, 'Story Objects')

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')
        self.leftBarDownPane1 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane1, 'Scene List')
        self.soundUI = SoundUI(self.leftBarDownNB, -1, self.editor)
        self.leftBarDownNB.AddPage(self.soundUI, 'Sound List')

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()
        self.baseFrame.SplitVertically(self.viewFrame, self.rightFrame, 500)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane,
                                                 self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.scenesUI = ScenesUI(self.leftBarDownPane1, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)
예제 #2
0
    def createInterface(self):
        WxPandaShell.createInterface(self)

        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.leftBarUpPane0 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane0, 'Object Palette')
        self.leftBarUpPane1 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane1, 'Proto Palette')

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()

        self.objectPaletteUI = ObjectPaletteUI(self.leftBarUpPane0,
                                               self.editor)
        self.protoPaletteUI = ProtoPaletteUI(self.leftBarUpPane1, self.editor)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane,
                                                 self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)
예제 #3
0
    def createInterface(self):
        WxPandaShell.createInterface(self)
        
        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.libraryUI = LibraryUI(self.leftBarUpNB,-1, self.editor)
        self.leftBarUpNB.AddPage(self.libraryUI, 'Library')
        self.pandaObjUI = PandaObjUI(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.pandaObjUI, 'Panda Objects')
        self.storyObjUI = StoryObjUI(self.leftBarUpNB, -1, self.editor)
        self.leftBarUpNB.AddPage(self.storyObjUI, 'Story Objects')
        

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')
        self.leftBarDownPane1 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane1, 'Scene List')
        self.soundUI = SoundUI(self.leftBarDownNB, -1, self.editor)
        self.leftBarDownNB.AddPage(self.soundUI, 'Sound List')
        

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()
        self.baseFrame.SplitVertically(self.viewFrame, self.rightFrame, 500)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane, self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.scenesUI = ScenesUI(self.leftBarDownPane1, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)
    def createInterface(self):
        WxPandaShell.createInterface(self)
        
        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.leftBarUpPane0 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane0, 'Object Palette')
        self.leftBarUpPane1 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane1, 'Proto Palette')

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()

        self.objectPaletteUI = ObjectPaletteUI(self.leftBarUpPane0, self.editor)
        self.protoPaletteUI = ProtoPaletteUI(self.leftBarUpPane1, self.editor)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane, self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)
예제 #5
0
class LevelEditorUI(WxPandaShell):
    """ Class for Panda3D LevelEditor """
    def __init__(self, editor):
        self.MENU_TEXTS.update({
            ID_NEW: ("New", "LE-NewScene"),
            ID_OPEN: ("Open...", "LE-OpenScene"),
            ID_SAVE: ("Save", "LE-SaveScene"),
            ID_SAVE_AS: ("Save As...", None),
            ID_IMPORT: ("Import Asset...", "LE-Import"),
            ID_IMPORT_EXTERNAL_LIB: ("Import External Library...", None),
            ID_EXPORT: ("Export Preview...", None),
            #ID_BVW_EXPORT : ("Export Standalone...", None),
            ID_GAME_EXPORT: ("Export World...", None),
            ID_GAME_EXPORT_RUN: ("Export World and Run...", None),
            ID_EXPORT_LIBRARY: ("Export Library...", None),
            ID_BROWSE_LIB: ("Browse External Library...", None),
            ID_BROWSE_DEFAULT_LIB: ("Browse Default Library...", None),
            ID_MERGE: ("Merge...", None),
            wx.ID_EXIT: ("Quit", "LE-Quit"),
            ID_DUPLICATE: ("Duplicate", "LE-Duplicate"),
            ID_UNDO: ("Undo", "LE-Undo"),
            ID_REDO: ("Redo", "LE-Redo"),
            ID_DROP_TO_GROUND: ("Drop Selected To Ground", "LE-DropToGround"),
            ID_SHOW_GRID: ("Show Grid", None),
            ID_GRID_SIZE: ("Grid Size...", None),
            ID_SHOW_COLLIDERS: ("Show Colliders", None),
            ID_HOT_KEYS: ("Hot Keys...", None),
            ID_PARENT_TO_SELECTED: ("Parent To Selected", None),
            ID_UNIQUE_NAMES: ("Prepend Username to Names", None),
            ID_PREFERENCES: ("Editor Preferences...", None),
            ID_SCENE_STATS: ("Scene Stats...", None),
            ID_SELECTED_OBJECT_STATS: ("Selected Object Stats...", None),
            ID_TRANSLATE_MODE: ("Translate", "LE-translateMode"),
            ID_ROTATE_MODE: ("Rotate", "LE-rotateMode"),
            ID_SCALE_MODE: ("Scale", "LE-scaleMode"),
            ID_LOCAL_MODE: ("Local Space", "LE-localMode"),
            ID_WORLD_MODE: ("World Space", "LE-worldMode"),
            ID_SHOW_JOURNAL: ("Show Journal", None),
            ID_CONVO_EDITOR: ("Conversation Editor", None),
            ID_INVENTORY: ("Show Inventory Map", None),
        })

        self.editor = editor
        WxPandaShell.__init__(self, fStartDirect=True)
        self.contextMenu = ViewportMenu()
        self.bindKeyEvents(True)

        #prevent craziness with the splitter windows
        for x in (self.baseFrame, self.mainFrame, self.rightFrame,
                  self.leftFrame):
            x.Bind(wx.EVT_SPLITTER_DCLICK, self.onDoubleClick)
            x.SetMinimumPaneSize(50)
            x.SetBorderSize(10)

        self.viewFrame.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING,
                            self.onViewFrameChange)

        self.SetSize(wx.Size(1280, 720))
        self.Maximize()

        self.leftBarUpPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        self.leftBarDownPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        self.rightBarUpPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        self.rightBarDownPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)

        self.mainFrame.SetSashPosition(250)
        self.baseFrame.SetSashPosition(self.baseFrame.GetSize()[0] - 250)
        self.rightFrame.SetSashPosition(self.rightFrame.GetSize()[1] - 250)
        self.rightFrame.SetSashGravity(1)

        taskMgr.add(self.mouseTask, 'mouseTask')
        self.inPandaWindow = False
        self.leftMouseWasDown = False
        self.middleMouseWasDown = False
        self.rightMouseWasDown = False

    #Overridden from wxAppShell
    def quit(self, event=None):
        if self.onDestroy(event):
            # to close Panda
            try:
                base
            except NameError:
                sys.exit()

            base.userExit()

    #overridden from wxAppShell
    def _WxAppShell__createAboutBox(self):
        aboutString = "Panda3D Level Editor\n\n" +\
                      "Version %.2f"%Util.VERSION_NUMBER + "\n\n"+\
                      "Developed by\n\n"+\
                      "Gyedo Jeon\n"+\
                      "Dan Pike\n"+\
                      "Craig Wells\n"+\
                      "Kent Vasko\n"+\
                      "Tatyana Koutepova\n"+\
                      "Andrew Gartner\n"+\
                      "Ayse Zeynep Tasar\n"+\
                      "Jue Wang\n"+\
                      "Qiaosi Chen\n"+\
                      "Thomas Luong\n"+\
                      "Anton Strenger\n"+\
                      "Russell Mester"

        self.about = wx.MessageDialog(None, aboutString,
                                      "About Panda3D Level Editor",
                                      wx.OK | wx.ICON_INFORMATION)

    def onDoubleClick(self, evt=None):
        evt.Veto()

    # detects when the mouse enters and leaves the panda window so we don't get caught thinking
    # the mouse or some key is held down when it isn't
    def mouseTask(self, task):

        mouseState = wx.GetMouseState()
        mousePoint = wx.Point(mouseState.GetX(), mouseState.GetY())
        viewports = (self.topView, self.leftView, self.frontView,
                     self.perspView)

        inPandaWindow = False

        for x in viewports:
            if x.IsShownOnScreen():
                if x.GetScreenRect().Inside(mousePoint):
                    inPandaWindow = True

        if inPandaWindow and not self.inPandaWindow:
            self.onEnterPandaWindow()
        elif not inPandaWindow and self.inPandaWindow:
            self.onLeavePandaWindow()

        self.inPandaWindow = inPandaWindow

        return task.cont

    def onEnterPandaWindow(self):
        base.direct.fAlt = int(wx.GetKeyState(wx.WXK_ALT))
        base.direct.fControl = int(wx.GetKeyState(wx.WXK_CONTROL))
        base.direct.fShift = int(wx.GetKeyState(wx.WXK_SHIFT))

        modifiers = DIRECT_NO_MOD
        ms = wx.GetMouseState()
        if ms.AltDown():
            modifiers |= DIRECT_ALT_MOD
        if ms.ShiftDown():
            modifiers |= DIRECT_SHIFT_MOD
        if ms.ControlDown():
            modifiers |= DIRECT_CONTROL_MOD
        if ms.LeftDown() and self.leftMouseWasDown:
            messenger.send('DIRECT-mouse1', sentArgs=[modifiers])
        if ms.MiddleDown() and self.middleMouseWasDown:
            messenger.send('DIRECT-mouse2', sentArgs=[modifiers])
        if ms.RightDown() and self.rightMouseWasDown:
            messenger.send('DIRECT-mouse3', sentArgs=[modifiers])

    def onLeavePandaWindow(self):
        base.direct.fAlt = int(wx.GetKeyState(wx.WXK_ALT))
        base.direct.fControl = int(wx.GetKeyState(wx.WXK_CONTROL))
        base.direct.fShift = int(wx.GetKeyState(wx.WXK_SHIFT))
        if base.direct.fMouse1:
            self.leftMouseWasDown = True
            messenger.send('DIRECT-mouse1Up')
        else:
            self.leftMouseWasDown = False
        if base.direct.fMouse2:
            messenger.send('DIRECT-mouse2Up')
            self.middleMouseWasDown = True
        else:
            self.middleMouseWasDown = False
        if base.direct.fMouse3:
            messenger.send('DIRECT-mouse3Up')
            self.rightMouseWasDown = True
        else:
            self.rightMouseWasDown = False

    def onViewFrameChange(self, evt=None):
        if evt.GetSashPosition()[0] < 50:
            evt.Veto()
        elif evt.GetSashPosition()[1] < 50:
            evt.Veto()
        elif self.viewFrame.GetSize()[0] - evt.GetSashPosition()[0] < 50:
            evt.Veto()
        elif self.viewFrame.GetSize()[1] - evt.GetSashPosition()[1] < 50:
            evt.Veto()

    #binds or unbinds hotkeys to free them up for use in the ui
    def bindKeyEvents(self, toBind=True):
        if toBind:
            self.wxApp.Bind(wx.EVT_CHAR, self.onKeyEvent)
            self.wxApp.Bind(wx.EVT_KEY_DOWN, self.onKeyDownEvent)
            self.wxApp.Bind(wx.EVT_KEY_UP, self.onKeyUpEvent)
        else:
            self.wxApp.Unbind(wx.EVT_CHAR)
            self.wxApp.Unbind(wx.EVT_KEY_DOWN)
            self.wxApp.Unbind(wx.EVT_KEY_UP)

    def createMenu(self):
        menuItem = self.menuFile.Insert(0, ID_NEW, self.MENU_TEXTS[ID_NEW][0])
        self.Bind(wx.EVT_MENU, self.onNew, menuItem)

        menuItem = self.menuFile.Insert(1, ID_OPEN,
                                        self.MENU_TEXTS[ID_OPEN][0])
        self.Bind(wx.EVT_MENU, self.onOpen, menuItem)

        menuItem = self.menuFile.Insert(2, ID_SAVE,
                                        self.MENU_TEXTS[ID_SAVE][0])
        self.Bind(wx.EVT_MENU, self.onSave, menuItem)

        menuItem = self.menuFile.Insert(3, ID_SAVE_AS,
                                        self.MENU_TEXTS[ID_SAVE_AS][0])
        self.Bind(wx.EVT_MENU, self.onSaveAs, menuItem)

        menuItem = self.menuFile.Insert(4, ID_MERGE,
                                        self.MENU_TEXTS[ID_MERGE][0])
        self.Bind(wx.EVT_MENU, self.onMerge, menuItem)

        self.menuFile.InsertSeparator(5)

        menuItem = self.menuFile.Insert(6, ID_IMPORT,
                                        self.MENU_TEXTS[ID_IMPORT][0])
        self.Bind(wx.EVT_MENU, self.onImport, menuItem)

        menuItem = self.menuFile.Insert(7, ID_BROWSE_LIB,
                                        self.MENU_TEXTS[ID_BROWSE_LIB][0])
        self.Bind(wx.EVT_MENU, self.onBrowseLib, menuItem)

        menuItem = self.menuFile.Insert(
            8, ID_BROWSE_DEFAULT_LIB,
            self.MENU_TEXTS[ID_BROWSE_DEFAULT_LIB][0])
        self.Bind(wx.EVT_MENU, self.onBrowseDefaultLib, menuItem)

        menuItem = self.menuFile.Insert(
            9, ID_IMPORT_EXTERNAL_LIB,
            self.MENU_TEXTS[ID_IMPORT_EXTERNAL_LIB][0])
        self.Bind(wx.EVT_MENU, self.onImportLib, menuItem)

        menuItem = self.menuFile.Insert(10, ID_EXPORT_LIBRARY,
                                        self.MENU_TEXTS[ID_EXPORT_LIBRARY][0])
        self.Bind(wx.EVT_MENU, self.onExportLib, menuItem)

        self.menuFile.InsertSeparator(11)

        menuItem = self.menuFile.Insert(12, ID_EXPORT,
                                        self.MENU_TEXTS[ID_EXPORT][0])
        self.Bind(wx.EVT_MENU, self.onExport, menuItem)

        #menuItem = self.menuFile.Insert(13, ID_BVW_EXPORT, self.MENU_TEXTS[ID_BVW_EXPORT][0])
        #self.Bind(wx.EVT_MENU, self.onBVWExport, menuItem)

        menuItem = self.menuFile.Insert(13, ID_GAME_EXPORT,
                                        self.MENU_TEXTS[ID_GAME_EXPORT][0])
        self.Bind(wx.EVT_MENU, self.onGameExport, menuItem)

        menuItem = self.menuFile.Insert(14, ID_GAME_EXPORT_RUN,
                                        self.MENU_TEXTS[ID_GAME_EXPORT_RUN][0])
        self.Bind(wx.EVT_MENU, self.onGameExportRun, menuItem)

        self.menuFile.InsertSeparator(15)

        self.menuEdit = wx.Menu()
        self.menuBar.Insert(1, self.menuEdit, "&Edit")

        menuItem = self.menuEdit.Append(ID_UNDO, self.MENU_TEXTS[ID_UNDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.undo, menuItem)

        menuItem = self.menuEdit.Append(ID_REDO, self.MENU_TEXTS[ID_REDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.redo, menuItem)

        menuItem = self.menuEdit.Append(ID_DUPLICATE,
                                        self.MENU_TEXTS[ID_DUPLICATE][0])
        self.Bind(wx.EVT_MENU, self.onDuplicate, menuItem)

        menuItem = self.menuEdit.Append(ID_DROP_TO_GROUND,
                                        self.MENU_TEXTS[ID_DROP_TO_GROUND][0])
        self.Bind(wx.EVT_MENU, self.editor.onDropToGround, menuItem)

        self.menuManip = wx.Menu()
        self.menuBar.Insert(2, self.menuManip, "&Manip")

        self.translateMenuItem = self.menuManip.AppendRadioItem(
            ID_TRANSLATE_MODE, self.MENU_TEXTS[ID_TRANSLATE_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.translateMode,
                  self.translateMenuItem)

        self.rotateMenuItem = self.menuManip.AppendRadioItem(
            ID_ROTATE_MODE, self.MENU_TEXTS[ID_ROTATE_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.rotateMode, self.rotateMenuItem)

        self.scaleMenuItem = self.menuManip.AppendRadioItem(
            ID_SCALE_MODE, self.MENU_TEXTS[ID_SCALE_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.scaleMode, self.scaleMenuItem)

        self.menuManip.AppendSeparator()

        self.localMenuItem = self.menuManip.AppendRadioItem(
            ID_LOCAL_MODE, self.MENU_TEXTS[ID_LOCAL_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.localMode, self.localMenuItem)

        self.worldMenuItem = self.menuManip.AppendRadioItem(
            ID_WORLD_MODE, self.MENU_TEXTS[ID_WORLD_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.worldMode, self.worldMenuItem)

        self.menuOptions = wx.Menu()
        self.menuBar.Insert(3, self.menuOptions, "&Options")

        self.showGridMenuItem = self.menuOptions.Append(
            ID_SHOW_GRID, self.MENU_TEXTS[ID_SHOW_GRID][0], kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.toggleGrid, self.showGridMenuItem)

        self.gridSizeMenuItem = self.menuOptions.Append(
            ID_GRID_SIZE, self.MENU_TEXTS[ID_GRID_SIZE][0])
        self.Bind(wx.EVT_MENU, self.onGridSize, self.gridSizeMenuItem)

        self.showCollidersMenuItem = self.menuOptions.Append(
            ID_SHOW_COLLIDERS,
            self.MENU_TEXTS[ID_SHOW_COLLIDERS][0],
            kind=wx.ITEM_CHECK)
        self.showCollidersMenuItem.Check(True)
        self.Bind(wx.EVT_MENU, self.onShowColliders,
                  self.showCollidersMenuItem)

        #self.parentToSelectedMenuItem = self.menuOptions.Append(ID_PARENT_TO_SELECTED, self.MENU_TEXTS[ID_PARENT_TO_SELECTED][0], kind = wx.ITEM_CHECK)
        self.preferencesItem = self.menuOptions.Append(
            ID_PREFERENCES, self.MENU_TEXTS[ID_PREFERENCES][0])
        self.Bind(wx.EVT_MENU, self.onPreferences, self.preferencesItem)
        self.hotKeysMenuItem = self.menuOptions.Append(
            ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0])
        self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem)

        self.sceneStatsMenuItem = self.menuOptions.Append(
            ID_SCENE_STATS, self.MENU_TEXTS[ID_SCENE_STATS][0])
        self.Bind(wx.EVT_MENU, self.onSceneStats, self.sceneStatsMenuItem)

        self.objectStatsMenuItem = self.menuOptions.Append(
            ID_SELECTED_OBJECT_STATS,
            self.MENU_TEXTS[ID_SELECTED_OBJECT_STATS][0])
        self.Bind(wx.EVT_MENU, self.onSelectedObjectStats,
                  self.objectStatsMenuItem)

        WxPandaShell.createMenu(self)

        self.menuGame = wx.Menu()
        self.menuBar.Append(self.menuGame, "&Game")
        self.showJournalItem = self.menuGame.Append(
            ID_SHOW_JOURNAL, self.MENU_TEXTS[ID_SHOW_JOURNAL][0])
        self.Bind(wx.EVT_MENU, self.onShowJournal, self.showJournalItem)
        self.conversationEditorItem = self.menuGame.Append(
            ID_CONVO_EDITOR, self.MENU_TEXTS[ID_CONVO_EDITOR][0])
        self.Bind(wx.EVT_MENU, self.onConversationEditor,
                  self.conversationEditorItem)
        self.inventoryItem = self.menuGame.Append(
            ID_INVENTORY, self.MENU_TEXTS[ID_INVENTORY][0])
        self.Bind(wx.EVT_MENU, self.onInventory, self.inventoryItem)

        #self.menuGame.AppendRadioItem()

    def updateMenu(self):
        hotKeyDict = {}
        for hotKey in base.direct.hotKeyMap.keys():
            desc = base.direct.hotKeyMap[hotKey]
            hotKeyDict[desc[1]] = hotKey

        for id in self.MENU_TEXTS.keys():
            desc = self.MENU_TEXTS[id]
            if desc[1]:
                menuItem = self.menuBar.FindItemById(id)
                hotKey = hotKeyDict.get(desc[1]).replace('control', 'ctrl')
                if hotKey:
                    menuItem.SetText(desc[0] + "\t%s" % hotKey)

    def createInterface(self):
        WxPandaShell.createInterface(self)

        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.libraryUI = LibraryUI(self.leftBarUpNB, -1, self.editor)
        self.leftBarUpNB.AddPage(self.libraryUI, 'Library')
        self.pandaObjUI = PandaObjUI(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.pandaObjUI, 'Panda Objects')
        self.storyObjUI = StoryObjUI(self.leftBarUpNB, -1, self.editor)
        self.leftBarUpNB.AddPage(self.storyObjUI, 'Story Objects')

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')
        self.leftBarDownPane1 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane1, 'Scene List')
        self.soundUI = SoundUI(self.leftBarDownNB, -1, self.editor)
        self.leftBarDownNB.AddPage(self.soundUI, 'Sound List')

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()
        self.baseFrame.SplitVertically(self.viewFrame, self.rightFrame, 500)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane,
                                                 self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.scenesUI = ScenesUI(self.leftBarDownPane1, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)

    def onRightDown(self, evt=None):
        """Invoked when the viewport is right-clicked."""
        if evt == None:
            mpos = wx.GetMouseState()
            mpos = self.ScreenToClient((mpos.x, mpos.y))
        else:
            mpos = evt.GetPosition()

        base.direct.fMouse3 = 0
        #self.PopupMenu(self.contextMenu, mpos)

    def onKeyDownEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 1
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 1
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 1
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down')
        else:
            evt.Skip()

    def onKeyUpEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 0
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 0
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 0
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up-up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down-up')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left-up')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down-up')
        else:
            evt.Skip()

    def onKeyEvent(self, evt):
        input = ''
        if evt.GetKeyCode() in range(97, 123):  # for keys from a to z
            if evt.GetModifiers(
            ) == 4:  # when shift is pressed while caps lock is on
                input = 'shift-%s' % chr(evt.GetKeyCode())
            else:
                input = chr(evt.GetKeyCode())
        elif evt.GetKeyCode() in range(65, 91):
            if evt.GetModifiers() == 4:  # when shift is pressed
                input = 'shift-%s' % chr(evt.GetKeyCode() + 32)
            else:
                input = chr(evt.GetKeyCode() + 32)
        elif evt.GetKeyCode() in range(
                1, 27):  # for keys from a to z with control
            if evt.GetModifiers() == 2:
                input = 'control-%s' % chr(evt.GetKeyCode() + 96)
        elif evt.GetKeyCode() == wx.WXK_DELETE:
            input = 'delete'
        elif evt.GetKeyCode() == wx.WXK_ESCAPE:
            input = 'escape'
        else:
            if evt.GetModifiers() == 4:
                input = 'shift-%s' % chr(evt.GetKeyCode())
            elif evt.GetModifiers() == 2:
                input = 'control-%s' % chr(evt.GetKeyCode())
            elif evt.GetKeyCode() < 256:
                input = chr(evt.GetKeyCode())
        if input in base.direct.hotKeyMap.keys():
            keyDesc = base.direct.hotKeyMap[input]
            messenger.send(keyDesc[1])

    def reset(self):
        self.sceneGraphUI.reset()
        self.scenesUI.reset()
        self.layerEditorUI.reset()

    def onNew(self, evt=None):
        self.editor.reset()
        self.editor.newProj()

    def onOpen(self, evt=None):
        dialog = wx.FileDialog(None, "Choose a project file", os.getcwd(), "",
                               "*.proj", wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.load(Filename.fromOsSpecific(dialog.GetPath()))
            self.libraryUI.update()
            self.storyObjUI.update()
            self.soundUI.update()
        dialog.Destroy()

    def onSave(self, evt=None):
        if not self.editor.saved:
            self.onSaveAs(evt)
        else:
            self.editor.save()

    def onSaveAs(self, evt):
        while True:
            dialog = SaveProjectUI(self, -1, "Save Project")
            if dialog.ShowModal() == wx.ID_OK:
                baseDir = dialog.dir
                projName = Util.toFilename(dialog.projName)
                if projName == '' or projName != dialog.projName:
                    dlg = wx.MessageDialog(self,
                                           "Invalid Project Name",
                                           style=wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                elif baseDir == None:
                    dlg = wx.MessageDialog(self,
                                           "Please Choose a save location",
                                           style=wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                else:
                    dir = baseDir + '/' + projName
                    if os.path.exists(dir.toOsSpecific()):
                        dlg = wx.MessageDialog(self, "A folder with that name already exists in the"\
                                               + " directory you have chosen.  Please choose another name.", style = wx.OK)
                        dlg.ShowModal()
                        dlg.Destroy()
                    else:
                        self.editor.saveAs(dir, projName)
                        break
            else:
                break

    def onImport(self, evt=None):
        importUI = ImportUI(self, -1, "Import")
        importUI.Show()

    def onImportLib(self, evt=None):
        dlg = wx.FileDialog(self,
                            "Choose a library file to import.",
                            wildcard="*.lbr",
                            style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:

            self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
            vfs = VirtualFileSystem.getGlobalPtr()
            filename = Filename.fromOsSpecific(dlg.GetPath())
            otherName = filename.getBasenameWoExtension().strip()
            ## #ZJC - 07/29/2011: Code deemed unnecessary
            ## base.le.prefixImportMerge.append(otherName) # ZJC - 07/27/2011: When a library is imported, add prefix to the list

            mountPt = "./externalLib_" + otherName
            vfs.mount(filename, mountPt, VirtualFileSystem.MFReadOnly)
            otherLib = Library.Library(Filename(mountPt))

            self.editor.lib.mergeWith(otherLib,
                                      otherName=otherName,
                                      saveAfter=True)
            vfs.unmountPoint(mountPt)

            self.libraryUI.update()
            self.storyObjUI.update()
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

    def onBrowseLib(self, evt=None):
        dlg = wx.FileDialog(self,
                            "Choose a library file to browse.",
                            wildcard="*.lbr",
                            style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:

            self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
            vfs = VirtualFileSystem.getGlobalPtr()
            filename = Filename.fromOsSpecific(dlg.GetPath())
            otherName = filename.getBasenameWoExtension().strip()
            mountPt = "./externalLib_" + otherName
            vfs.mount(filename, mountPt, VirtualFileSystem.MFReadOnly)
            otherLib = Library.Library(Filename(mountPt))
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

            libBrowser = LibraryBrowserUI(self, otherLib, otherName)

            libBrowser.ShowModal()

            vfs.unmountPoint(mountPt)

            self.libraryUI.update()
            self.storyObjUI.update()

    def onBrowseDefaultLib(self, evt=None):
        self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
        vfs = VirtualFileSystem.getGlobalPtr()
        filename = Filename('models/Orelia.lbr')  #'models/default.lbr')
        otherName = filename.getBasenameWoExtension().strip()
        mountPt = "./externalLib_" + otherName
        vfs.mount(filename, mountPt, VirtualFileSystem.MFReadOnly)
        otherLib = Library.Library(Filename(mountPt))
        self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

        libBrowser = LibraryBrowserUI(self, otherLib, otherName)

        libBrowser.ShowModal()

        vfs.unmountPoint(mountPt)

        self.libraryUI.update()
        self.storyObjUI.update()

    def onExportLib(self, evt=None):
        if self.editor.saved:
            dlg = wx.FileDialog(self, "Choose a filename for your exported library.", defaultDir = self.editor.projDir.toOsSpecific(),\
                defaultFile = self.editor.currentProj.name.strip() + ".lbr", wildcard="*.lbr", style = wx.FD_SAVE|wx.OVERWRITE_PROMPT)

            if dlg.ShowModal() == wx.ID_OK:
                f = Filename.fromOsSpecific(dlg.GetPath())
                if f.getDirname() != self.editor.projDir.getFullpath():
                    msg = wx.MessageDialog(
                        self,
                        "Must save to your current project directory.",
                        style=wx.OK | wx.ICON_ERROR)
                    msg.ShowModal()
                    return
                self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
                self.editor.lib.exportToMultifile(
                    Filename.fromOsSpecific(dlg.GetPath()))
                self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
            dlg.Destroy()
        else:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Library Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()

    def onExport(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
        else:
            #            dlg = wx.FileDialog(self, "Choose a python file to export to.", \
            #            defaultDir= self.editor.currentProj.dir.toOsSpecific(),\
            #            defaultFile = self.editor.currentProj.name + '.py', wildcard="*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
            #
            #            if dlg.ShowModal() == wx.ID_OK:
            #                self.editor.standaloneExporter.export(dlg.GetPath())
            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.export(opts)
                self.editor.save()

    def onBVWExport(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)

            dlg.ShowModal()
            dlg.Destroy()
        else:
            #            dlg = wx.FileDialog(self, "Choose a python file to export to.", \
            #            defaultDir= self.editor.currentProj.dir.toOsSpecific(),\
            #            defaultFile = self.editor.currentProj.name + '.py', wildcard="*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
            #
            #            if dlg.ShowModal() == wx.ID_OK:
            #                self.editor.standaloneExporter.exportBVW(dlg.GetPath())
            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.exportBVW(opts)

    def onGameExport(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
        else:
            #            dialog = wx.MessageDialog(self, "Cool stuff goes here", "Export as Game", wx.OK|wx.ICON_EXCLAMATION)
            #            dialog.ShowModal()
            #            dialog.Destroy()

            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.exportGame(opts)
                self.editor.save()

    def onGameExportRun(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
        else:
            #            dialog = wx.MessageDialog(self, "Cool stuff goes here", "Export as Game", wx.OK|wx.ICON_EXCLAMATION)
            #            dialog.ShowModal()
            #            dialog.Destroy()

            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.exportGame(opts)
                self.editor.save()

                filename = Filename("runWorld.bat")
                filename.setDirname(self.editor.projDir.getFullpath())
                #print filename
                cwd = os.getcwd()
                os.chdir(self.editor.projDir.toOsSpecific())
                subprocess.Popen(filename.toOsSpecific())
                os.chdir(cwd)

    def onMerge(self, evt):
        dlg = MergeProjectUI(self)

        dlg.ShowModal()

        dlg.Destroy()

    def onSceneStats(self, evt=None):
        f = StringIO()
        temp = sys.stdout
        sys.stdout = f
        render.analyze()

        sys.stdout = temp

        dlg = wx.MessageDialog(self, f.getvalue(), caption="Scene Statistics")
        dlg.ShowModal()

    def onSelectedObjectStats(self, evt=None):
        np = base.direct.selected.last

        f = StringIO()
        temp = sys.stdout
        sys.stdout = f
        np.analyze()

        sys.stdout = temp

        dlg = wx.MessageDialog(self, f.getvalue(), caption="Object Statistics")
        dlg.ShowModal()

    def onDuplicate(self, evt=None):
        action = ActionDuplicateSelected(self.editor)
        self.editor.actionMgr.push(action)
        action()
        #self.editor.objectMgr.duplicateSelected()

    def toggleGrid(self, evt):
        if self.showGridMenuItem.IsChecked():
            for grid in [
                    self.perspView.grid, self.topView.grid,
                    self.frontView.grid, self.leftView.grid
            ]:
                if grid.isHidden():
                    grid.show()
        else:
            for grid in [
                    self.perspView.grid, self.topView.grid,
                    self.frontView.grid, self.leftView.grid
            ]:
                if not grid.isHidden():
                    grid.hide()

    def onShowJournal(self, evt):
        journalDialog = JournalDialog(self, -1, "Show Journal", self.editor)
        journalDialog.Show()

    def onConversationEditor(self, evt):
        editor = ConversationEditor(self, self.editor)
        #editor.Show()

    def onInventory(self, evt):
        inventoryDialog = LEInventoryUI(self, -1, "Inventory Map", self.editor)
        inventoryDialog.Show()
        #inventoryDialog.ShowModal()

    def onGridSize(self, evt):
        gridSizeUI = GridSizeUI(self, -1, 'Change Grid Size',
                                self.perspView.grid.gridSize,
                                self.perspView.grid.gridSpacing)
        gridSizeUI.ShowModal()
        gridSizeUI.Destroy()

    def onShowColliders(self, evt):
        if self.showCollidersMenuItem.IsChecked():
            self.editor.objectMgr.showColliders = True
            for c in self.editor.objectMgr.colliders:
                c.show()
        else:
            self.editor.objectMgr.showColliders = False

            for c in self.editor.objectMgr.colliders:
                c.hide()

    def onDestroy(self, evt):
        dlg = wx.MessageDialog(self,
                               "Are you sure you want to exit?",
                               style=wx.YES_NO)
        if dlg.ShowModal() == wx.ID_YES:
            self.editor.saveSettings()
            self.editor.reset()
            if self.editor.tempDir and os.path.exists(self.editor.tempDir):
                try:
                    shutil.rmtree(self.editor.tempDir)
                except:
                    pass
            dlg.Destroy()
            return True
        else:
            dlg.Destroy()
            return False

    def updateGrids(self, newSize, newSpacing):
        self.perspView.grid.gridSize = newSize
        self.perspView.grid.gridSpacing = newSpacing
        self.perspView.grid.updateGrid()

        self.topView.grid.gridSize = newSize
        self.topView.grid.gridSpacing = newSpacing
        self.topView.grid.updateGrid()

        self.frontView.grid.gridSize = newSize
        self.frontView.grid.gridSpacing = newSpacing
        self.frontView.grid.updateGrid()

        self.leftView.grid.gridSize = newSize
        self.leftView.grid.gridSpacing = newSpacing
        self.leftView.grid.updateGrid()

    def onHotKeys(self, evt):
        hotKeyUI = HotKeyUI(self, -1, 'Hot Key List')
        hotKeyUI.ShowModal()
        hotKeyUI.Destroy()

    def onPreferences(self, evt):
        ##Show a dialog with the few editor prefs we have
        dlg = PrefsDialog(self)
        dlg.Show()

    def buildContextMenu(self, nodePath):
        for menuItem in self.contextMenu.GetMenuItems():
            self.contextMenu.RemoveItem(menuItem)

        #self.contextMenu.addItem('Replace With Selected Asset', call=self.editor.objectMgr.replaceObjectWithAsset)
        self.contextMenu.addItem('Duplicate', call=self.onDuplicate)
class LevelEditorUIBase(WxPandaShell):
    """ Class for Panda3D LevelEditor """ 
    def __init__(self, editor):
        self.MENU_TEXTS.update({
            ID_NEW : ("&New", "LE-NewScene"),
            ID_OPEN : ("&Open", "LE-OpenScene"),
            ID_SAVE : ("&Save", "LE-SaveScene"),
            ID_SAVE_AS : ("Save &As", None),
            ID_EXPORT_TO_MAYA : ("Export to Maya", None),
            wx.ID_EXIT : ("&Quit", "LE-Quit"),
            ID_DUPLICATE : ("&Duplicate", "LE-Duplicate"),
            ID_MAKE_LIVE : ("Make &Live", "LE-MakeLive"),
            ID_UNDO : ("&Undo", "LE-Undo"),
            ID_REDO : ("&Redo", "LE-Redo"),
            ID_SHOW_GRID : ("&Show Grid", None),
            ID_GRID_SIZE : ("&Grid Size", None),
            ID_GRID_SNAP : ("Grid S&nap", None),
            ID_SHOW_PANDA_OBJECT : ("Show &Panda Objects", None),
            ID_HOT_KEYS : ("&Hot Keys", None),
            ID_PARENT_TO_SELECTED : ("&Parent To Selected", None),
            ID_CREATE_CURVE : ("&Create Curve", None),
            ID_EDIT_CURVE : ("&Edit Curve", None),
            ID_CURVE_ANIM : ("&Curve Animation", None),
            ID_ANIM : ("&Edit Animation", None),
            ID_GRAPH : ("&Graph Editor", None)
            })

        self.editor = editor
        WxPandaShell.__init__(self, fStartDirect=True)        
        self.contextMenu = ViewportMenu()
        self.bindKeyEvents(True)

    def bindKeyEvents(self, toBind=True):
        if toBind:
            self.wxApp.Bind(wx.EVT_CHAR, self.onKeyEvent)
            self.wxApp.Bind(wx.EVT_KEY_DOWN, self.onKeyDownEvent)
            self.wxApp.Bind(wx.EVT_KEY_UP, self.onKeyUpEvent)
        else:
            self.wxApp.Unbind(wx.EVT_CHAR)
            self.wxApp.Unbind(wx.EVT_KEY_DOWN)
            self.wxApp.Unbind(wx.EVT_KEY_UP)

    def createMenu(self):
        menuItem = self.menuFile.Insert(0, ID_NEW, self.MENU_TEXTS[ID_NEW][0])
        self.Bind(wx.EVT_MENU, self.onNew, menuItem)
        
        menuItem = self.menuFile.Insert(1, ID_OPEN, self.MENU_TEXTS[ID_OPEN][0])
        self.Bind(wx.EVT_MENU, self.onOpen, menuItem)

        menuItem = self.menuFile.Insert(2, ID_SAVE, self.MENU_TEXTS[ID_SAVE][0])
        self.Bind(wx.EVT_MENU, self.onSave, menuItem)

        menuItem = self.menuFile.Insert(3, ID_SAVE_AS, self.MENU_TEXTS[ID_SAVE_AS][0])
        self.Bind(wx.EVT_MENU, self.onSaveAs, menuItem)

        menuItem = self.menuFile.Insert(4, ID_EXPORT_TO_MAYA, self.MENU_TEXTS[ID_EXPORT_TO_MAYA][0])
        self.Bind(wx.EVT_MENU, self.onExportToMaya, menuItem)

        self.menuEdit = wx.Menu()
        self.menuBar.Insert(1, self.menuEdit, "&Edit")

        menuItem = self.menuEdit.Append(ID_DUPLICATE, self.MENU_TEXTS[ID_DUPLICATE][0])
        self.Bind(wx.EVT_MENU, self.onDuplicate, menuItem)

        menuItem = self.menuEdit.Append(ID_MAKE_LIVE, self.MENU_TEXTS[ID_MAKE_LIVE][0])
        self.Bind(wx.EVT_MENU, self.onMakeLive, menuItem)

        menuItem = self.menuEdit.Append(ID_UNDO, self.MENU_TEXTS[ID_UNDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.undo, menuItem)

        menuItem = self.menuEdit.Append(ID_REDO, self.MENU_TEXTS[ID_REDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.redo, menuItem)

        self.menuOptions = wx.Menu()
        self.menuBar.Insert(2, self.menuOptions, "&Options")

        self.showGridMenuItem = self.menuOptions.Append(ID_SHOW_GRID, self.MENU_TEXTS[ID_SHOW_GRID][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.toggleGrid, self.showGridMenuItem)

        self.gridSizeMenuItem = self.menuOptions.Append(ID_GRID_SIZE, self.MENU_TEXTS[ID_GRID_SIZE][0])
        self.Bind(wx.EVT_MENU, self.onGridSize, self.gridSizeMenuItem)

        self.gridSnapMenuItem = self.menuOptions.Append(ID_GRID_SNAP, self.MENU_TEXTS[ID_GRID_SNAP][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.toggleGridSnap, self.gridSnapMenuItem)

        self.showPandaObjectsMenuItem = self.menuOptions.Append(ID_SHOW_PANDA_OBJECT, self.MENU_TEXTS[ID_SHOW_PANDA_OBJECT][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onShowPandaObjects, self.showPandaObjectsMenuItem)

        self.parentToSelectedMenuItem = self.menuOptions.Append(ID_PARENT_TO_SELECTED, self.MENU_TEXTS[ID_PARENT_TO_SELECTED][0], kind = wx.ITEM_CHECK)

        self.hotKeysMenuItem = self.menuOptions.Append(ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0])
        self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem)
        
        self.menuCurve = wx.Menu()
        self.menuBar.Insert(3, self.menuCurve, "&CurveMode")
        
        self.createCurveMenuItem = self.menuCurve.Append(ID_CREATE_CURVE, self.MENU_TEXTS[ID_CREATE_CURVE][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onCreateCurve, self.createCurveMenuItem)
        
        self.editCurveMenuItem = self.menuCurve.Append(ID_EDIT_CURVE, self.MENU_TEXTS[ID_EDIT_CURVE][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onEditCurve, self.editCurveMenuItem)
        
        self.curveAnimMenuItem = self.menuCurve.Append(ID_CURVE_ANIM, self.MENU_TEXTS[ID_CURVE_ANIM][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onCurveAnim, self.curveAnimMenuItem)
        
        self.menuAnim = wx.Menu()
        self.menuBar.Insert(4, self.menuAnim, "&AnimationMode")
        
        self.editAnimMenuItem = self.menuAnim.Append(ID_ANIM, self.MENU_TEXTS[ID_ANIM][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onAnimation, self.editAnimMenuItem)
        
        self.graphEditorMenuItem = self.menuAnim.Append(ID_GRAPH, self.MENU_TEXTS[ID_GRAPH][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onGraphEditor, self.graphEditorMenuItem)
        
        WxPandaShell.createMenu(self)
    
    def onGraphEditor(self,e):
        if base.direct.selected.last == None:
            dlg = wx.MessageDialog(None, 'Please select a object first.', 'NOTICE', wx.OK )
            dlg.ShowModal()
            dlg.Destroy()
            self.graphEditorMenuItem.Check(False)
        else:
            currentObj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
            self.graphEditorUI = GraphEditorUI(self, self.editor, currentObj)
            self.graphEditorUI.Show()
            self.graphEditorMenuItem.Check(True)
    
    def onAnimation(self,e):
        if self.editor.mode != self.editor.ANIM_MODE:
            self.animUI = AnimControlUI(self, self.editor)
            self.animUI.Show()
            self.editor.mode = self.editor.ANIM_MODE
        if self.editor.mode == self.editor.ANIM_MODE:
            self.editAnimMenuItem.Check(True)
        
    def onCurveAnim(self,e):
        self.curveAnimUI = CurveAnimUI(self, self.editor)
        self.curveAnimUI.Show()
        self.curveAnimMenuItem.Check(True)
        
    def onCreateCurve(self,e):
        """Function to invoke curve creating, need to check previous mode"""
        if self.editor.mode == self.editor.CREATE_CURVE_MODE:
            self.createCurveMenuItem.Check(False)
            self.editor.curveEditor.onBaseMode() 
        else:
            if self.editor.mode == self.editor.EDIT_CURVE_MODE:
                self.editor.curveEditor.onBaseMode()
                self.editCurveMenuItem.Check(False)
                self.createCurveMenuItem.Check(True)
                self.onCreateCurve(None)
            else:
                self.currentView = self.getCurrentView()
                if self.currentView == None:
                    dlg = wx.MessageDialog(None, 'Please select a viewport first.Do not support curve creation under four viewports.', 'NOTICE', wx.OK )
                    dlg.ShowModal()
                    dlg.Destroy()
                    self.createCurveMenuItem.Check(False)
                else:
                    self.editor.mode = self.editor.CREATE_CURVE_MODE
                    self.editor.updateStatusReadout('Please press ENTER to end the curve creation.')
                    degreeUI = CurveDegreeUI(self, -1, 'Curve Degree')
                    degreeUI.ShowModal()
                    degreeUI.Destroy()
                    base.direct.manipulationControl.disableManipulation()
                    self.editCurveMenuItem.Check(False)
    
    def onEditCurve(self,e):
        """Function to invoke curve editing and translate global information to local information. Need to check previous mode"""
        if self.editor.mode == self.editor.EDIT_CURVE_MODE:
            self.editCurveMenuItem.Check(False)
            self.editor.curveEditor.onBaseMode() 
        else:
            if self.editor.mode == self.editor.CREATE_CURVE_MODE:
                self.editor.curveEditor.onBaseMode()
                self.editCurveMenuItem.Check(True)
                self.createCurveMenuItem.Check(False)
                self.onEditCurve(None)
            else:
                if base.direct.selected.last == None:
                    dlg = wx.MessageDialog(None, 'Please select a curve first.', 'NOTICE', wx.OK )
                    dlg.ShowModal()
                    dlg.Destroy()
                    self.editCurveMenuItem.Check(False)
                if base.direct.selected.last != None :
                    base.direct.manipulationControl.enableManipulation()
                    self.createCurveMenuItem.Check(False)
                    self.curveObj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
                    if self.curveObj[OG.OBJ_DEF].name == '__Curve__':
                        self.editor.mode = self.editor.EDIT_CURVE_MODE
                        self.editor.updateStatusReadout('Please press ENTER to end the curve editing.')
                        self.editor.curveEditor.currentRope = self.curveObj[OG.OBJ_NP]
                        self.editor.curveEditor.curveControl = self.curveObj[OG.OBJ_PROP]['curveInfo']
                        self.editor.curveEditor.degree = self.curveObj[OG.OBJ_PROP]['Degree']
                        for item in self.editor.curveEditor.curveControl:
                            item[1].show()
                            self.editor.curveEditor.curve.append((None, item[1].getPos()))
                    else:
                        dlg = wx.MessageDialog(None, 'Please select a curve first.', 'NOTICE', wx.OK )
                        dlg.ShowModal()
                        dlg.Destroy()
                        self.editCurveMenuItem.Check(False)
                
    def updateMenu(self):
        hotKeyDict = {}
        for hotKey in base.direct.hotKeyMap.keys():
            desc = base.direct.hotKeyMap[hotKey]
            hotKeyDict[desc[1]] = hotKey
            
        for id in self.MENU_TEXTS.keys():
            desc = self.MENU_TEXTS[id]
            if desc[1]:
                menuItem = self.menuBar.FindItemById(id)
                hotKey = hotKeyDict.get(desc[1])
                if hotKey:
                    menuItem.SetText(desc[0] + "\t%s"%hotKey)

    def createInterface(self):
        WxPandaShell.createInterface(self)
        
        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.leftBarUpPane0 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane0, 'Object Palette')
        self.leftBarUpPane1 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane1, 'Proto Palette')

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()

        self.objectPaletteUI = ObjectPaletteUI(self.leftBarUpPane0, self.editor)
        self.protoPaletteUI = ProtoPaletteUI(self.leftBarUpPane1, self.editor)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane, self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)

    def onRightDown(self, evt=None):
        """Invoked when the viewport is right-clicked."""
        if evt == None:
            mpos = wx.GetMouseState()
            mpos = self.ScreenToClient((mpos.x, mpos.y))
        else:
            mpos = evt.GetPosition()

        base.direct.fMouse3 = 0
        self.PopupMenu(self.contextMenu, mpos)

    def onKeyDownEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 1
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 1
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 1
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down')
        else:
            evt.Skip()

    def onKeyUpEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 0
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 0
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 0
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up-up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down-up')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left-up')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down-up')
        else:
            evt.Skip()
        
    def onKeyEvent(self, evt):
        input = ''
        if evt.GetKeyCode() in range(97, 123): # for keys from a to z
            if evt.GetModifiers() == 4: # when shift is pressed while caps lock is on
                input = 'shift-%s'%chr(evt.GetKeyCode())
            else:
                input = chr(evt.GetKeyCode())
        elif evt.GetKeyCode() in range(65, 91):
            if evt.GetModifiers() == 4: # when shift is pressed
                input = 'shift-%s'%chr(evt.GetKeyCode() + 32)
            else:
                input = chr(evt.GetKeyCode() + 32)
        elif evt.GetKeyCode() in range(1, 27): # for keys from a to z with control
            input = 'control-%s'%chr(evt.GetKeyCode()+96)
        elif evt.GetKeyCode() == wx.WXK_DELETE:
            input = 'delete'
        elif evt.GetKeyCode() == wx.WXK_ESCAPE:
            input = 'escape'
        else:
            if evt.GetModifiers() == 4:
                input = 'shift-%s'%chr(evt.GetKeyCode())
            elif evt.GetModifiers() == 2:
                input = 'control-%s'%chr(evt.GetKeyCode())
            elif evt.GetKeyCode() < 256:
                input = chr(evt.GetKeyCode())
        if input in base.direct.hotKeyMap.keys():
            keyDesc = base.direct.hotKeyMap[input]
            messenger.send(keyDesc[1])

    def reset(self):
        self.sceneGraphUI.reset()
        self.layerEditorUI.reset()

    def onNew(self, evt=None):
        self.editor.reset()

    def onOpen(self, evt=None):
        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.load(dialog.GetPath())
            self.editor.setTitleWithFilename(dialog.GetPath())
        dialog.Destroy()

    def onSave(self, evt=None):
        if self.editor.currentFile is None or\
           not self.editor.currentFile.endswith('.py'):
            return self.onSaveAs(evt)
        else:
            self.editor.save()

    def onSaveAs(self, evt):
        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.SAVE)
        result = True
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.saveAs(dialog.GetPath())
            self.editor.setTitleWithFilename(dialog.GetPath())
        else:
            result = False
        dialog.Destroy()
        return result

    def onExportToMaya(self, evt):
        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", wx.SAVE)
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.exportToMaya(dialog.GetPath())
        dialog.Destroy()

    def onDuplicate(self, evt):
        self.editor.objectMgr.duplicateSelected()

    def onMakeLive(self, evt):
        self.editor.objectMgr.makeSelectedLive()

    def toggleGrid(self, evt):
        if self.showGridMenuItem.IsChecked():
            for grid in [self.perspView.grid, self.topView.grid, self.frontView.grid, self.leftView.grid]:
                if grid.isHidden():
                    grid.show()
        else:
            for grid in [self.perspView.grid, self.topView.grid, self.frontView.grid, self.leftView.grid]:
                if not grid.isHidden():
                    grid.hide()
                
    def toggleGridSnap(self, evt):
        if self.gridSnapMenuItem.IsChecked():
            base.direct.manipulationControl.fGridSnap = 1
            for grid in [self.perspView.grid, self.topView.grid, self.frontView.grid, self.leftView.grid]:
                grid.fXyzSnap = 1

        else:
            base.direct.manipulationControl.fGridSnap = 0            
            for grid in [self.perspView.grid, self.topView.grid, self.frontView.grid, self.leftView.grid]:
                grid.fXyzSnap = 0
            
    def onGridSize(self, evt):
        gridSizeUI = GridSizeUI(self, -1, 'Change Grid Size', self.perspView.grid.gridSize, self.perspView.grid.gridSpacing)
        gridSizeUI.ShowModal()
        gridSizeUI.Destroy()
        
    def onShowPandaObjects(self, evt):
        self.sceneGraphUI.showPandaObjectChildren()

    def onDestroy(self, evt):
        self.editor.protoPalette.saveToFile()
        self.editor.saveSettings()
        self.editor.reset()

    def updateGrids(self, newSize, newSpacing):
        self.perspView.grid.gridSize = newSize
        self.perspView.grid.gridSpacing = newSpacing
        self.perspView.grid.updateGrid()

        self.topView.grid.gridSize = newSize
        self.topView.grid.gridSpacing = newSpacing
        self.topView.grid.updateGrid()

        self.frontView.grid.gridSize = newSize
        self.frontView.grid.gridSpacing = newSpacing
        self.frontView.grid.updateGrid()

        self.leftView.grid.gridSize = newSize
        self.leftView.grid.gridSpacing = newSpacing
        self.leftView.grid.updateGrid()        

    def onHotKeys(self, evt):
        hotKeyUI = HotKeyUI(self, -1, 'Hot Key List')
        hotKeyUI.ShowModal()
        hotKeyUI.Destroy()

    def buildContextMenu(self, nodePath):
        for menuItem in self.contextMenu.GetMenuItems():
            self.contextMenu.RemoveItem(menuItem)

        self.contextMenu.addItem('Replace This', call=lambda\
                                 p0=None, p1=False:self.replaceObject(p0, p1))

        self.contextMenu.addItem('Replace All', call=lambda\
                                 p0=None, p1=True:self.replaceObject(p0, p1))
        self.contextMenu.AppendSeparator()

    def replaceObject(self, evt, all=False):
        currObj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
        if currObj is None:
            print 'No valid object is selected for replacement'
            return

        targetType = self.editor.ui.objectPaletteUI.getSelected()
        if targetType is None:
            print 'No valid target type is selected for replacement'
            return

        if all:
            typeName = currObj[OG.OBJ_DEF].name
            objs = self.editor.objectMgr.findObjectsByTypeName(typeName)
            for obj in objs:
                self.editor.objectMgr.replaceObjectWithTypeName(obj, targetType)
        else:
            self.editor.objectMgr.replaceObjectWithTypeName(currObj, targetType)
예제 #7
0
 def createInterface(self):
     LevelEditorUIBase.createInterface(self)
     self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
예제 #8
0
class LevelEditorUI(WxPandaShell):
    """ Class for Panda3D LevelEditor """ 
    def __init__(self, editor):
        self.MENU_TEXTS.update({
            ID_NEW : ("New", "LE-NewScene"),
            ID_OPEN : ("Open...", "LE-OpenScene"),
            ID_SAVE : ("Save", "LE-SaveScene"),
            ID_SAVE_AS : ("Save As...", None),
            ID_IMPORT : ("Import Asset...", "LE-Import"),
            ID_IMPORT_EXTERNAL_LIB : ("Import External Library...", None),
            ID_EXPORT : ("Export Preview...", None),
            #ID_BVW_EXPORT : ("Export Standalone...", None),
            ID_GAME_EXPORT : ("Export World...", None),
            ID_GAME_EXPORT_RUN:("Export World and Run...", None),
            ID_EXPORT_LIBRARY : ("Export Library...", None),
            ID_BROWSE_LIB : ("Browse External Library...", None),
            ID_BROWSE_DEFAULT_LIB : ("Browse Default Library...", None),
            ID_MERGE : ("Merge...", None),
            wx.ID_EXIT : ("Quit", "LE-Quit"),
            ID_DUPLICATE : ("Duplicate", "LE-Duplicate"),
            ID_UNDO : ("Undo", "LE-Undo"),
            ID_REDO : ("Redo", "LE-Redo"),
            ID_DROP_TO_GROUND : ("Drop Selected To Ground", "LE-DropToGround"),
            ID_SHOW_GRID : ("Show Grid", None),
            ID_GRID_SIZE : ("Grid Size...", None),
            ID_SHOW_COLLIDERS : ("Show Colliders", None),
            ID_HOT_KEYS : ("Hot Keys...", None),
            ID_PARENT_TO_SELECTED : ("Parent To Selected", None),
            ID_UNIQUE_NAMES : ("Prepend Username to Names", None),
            ID_PREFERENCES : ("Editor Preferences...", None),
            ID_SCENE_STATS : ("Scene Stats...", None),
            ID_SELECTED_OBJECT_STATS : ("Selected Object Stats...", None),
            ID_TRANSLATE_MODE : ("Translate", "LE-translateMode"),
            ID_ROTATE_MODE : ("Rotate", "LE-rotateMode"),
            ID_SCALE_MODE : ("Scale", "LE-scaleMode"),
            ID_LOCAL_MODE : ("Local Space", "LE-localMode"),
            ID_WORLD_MODE : ("World Space", "LE-worldMode"),
            ID_SHOW_JOURNAL :("Show Journal", None),
            ID_CONVO_EDITOR :("Conversation Editor", None),
            ID_INVENTORY :("Show Inventory Map", None),
            })

        self.editor = editor
        WxPandaShell.__init__(self, fStartDirect=True)        
        self.contextMenu = ViewportMenu()
        self.bindKeyEvents(True)
        
        #prevent craziness with the splitter windows
        for x in (self.baseFrame, self.mainFrame, self.rightFrame, self.leftFrame):
            x.Bind(wx.EVT_SPLITTER_DCLICK, self.onDoubleClick)
            x.SetMinimumPaneSize(50)
            x.SetBorderSize(10)
                  
        self.viewFrame.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.onViewFrameChange)
        
        self.SetSize(wx.Size(1280,720))
        self.Maximize()
        
        
        self.leftBarUpPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        self.leftBarDownPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        self.rightBarUpPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        self.rightBarDownPane.SetWindowStyleFlag(wx.wx.SUNKEN_BORDER)
        
        self.mainFrame.SetSashPosition(250)
        self.baseFrame.SetSashPosition(self.baseFrame.GetSize()[0] - 250)
        self.rightFrame.SetSashPosition(self.rightFrame.GetSize()[1] -250)
        self.rightFrame.SetSashGravity(1)
        
        taskMgr.add(self.mouseTask, 'mouseTask')
        self.inPandaWindow = False
        self.leftMouseWasDown = False
        self.middleMouseWasDown = False
        self.rightMouseWasDown = False
                  
    #Overridden from wxAppShell
    def quit(self, event=None):
        if self.onDestroy(event):
            # to close Panda
            try:
                base
            except NameError:
                sys.exit()

            base.userExit()
   
    #overridden from wxAppShell
    def _WxAppShell__createAboutBox(self):
        aboutString = "Panda3D Level Editor\n\n" +\
                      "Version %.2f"%Util.VERSION_NUMBER + "\n\n"+\
                      "Developed by\n\n"+\
                      "Gyedo Jeon\n"+\
                      "Dan Pike\n"+\
                      "Craig Wells\n"+\
                      "Kent Vasko\n"+\
                      "Tatyana Koutepova\n"+\
                      "Andrew Gartner\n"+\
                      "Ayse Zeynep Tasar\n"+\
                      "Jue Wang\n"+\
                      "Qiaosi Chen\n"+\
                      "Thomas Luong\n"+\
                      "Anton Strenger\n"+\
                      "Russell Mester"
                      
        self.about = wx.MessageDialog(None, aboutString, "About Panda3D Level Editor",
                     wx.OK | wx.ICON_INFORMATION)
   
    def onDoubleClick(self, evt=None):
        evt.Veto()
    
    # detects when the mouse enters and leaves the panda window so we don't get caught thinking
    # the mouse or some key is held down when it isn't
    def mouseTask(self, task):
    
        mouseState = wx.GetMouseState()
        mousePoint = wx.Point(mouseState.GetX(), mouseState.GetY())
        viewports = (self.topView, self.leftView, self.frontView, self.perspView)
        
        inPandaWindow = False
               
        for x in viewports:
            if x.IsShownOnScreen():
                if x.GetScreenRect().Inside(mousePoint):
                    inPandaWindow = True

        if inPandaWindow and not self.inPandaWindow:
            self.onEnterPandaWindow()
        elif not inPandaWindow and self.inPandaWindow:
            self.onLeavePandaWindow()
                    
        self.inPandaWindow = inPandaWindow
        
        return task.cont
        
    def onEnterPandaWindow(self):
        base.direct.fAlt = int(wx.GetKeyState(wx.WXK_ALT))
        base.direct.fControl = int(wx.GetKeyState(wx.WXK_CONTROL))
        base.direct.fShift = int(wx.GetKeyState(wx.WXK_SHIFT))

        modifiers = DIRECT_NO_MOD
        ms = wx.GetMouseState()
        if ms.AltDown():
            modifiers |= DIRECT_ALT_MOD
        if ms.ShiftDown():
           modifiers |= DIRECT_SHIFT_MOD
        if ms.ControlDown():
            modifiers |= DIRECT_CONTROL_MOD
        if ms.LeftDown() and self.leftMouseWasDown:
            messenger.send('DIRECT-mouse1', sentArgs=[modifiers])
        if ms.MiddleDown() and self.middleMouseWasDown:
            messenger.send('DIRECT-mouse2', sentArgs=[modifiers])
        if ms.RightDown() and self.rightMouseWasDown:
            messenger.send('DIRECT-mouse3', sentArgs=[modifiers])
        
        
    def onLeavePandaWindow(self):
        base.direct.fAlt = int(wx.GetKeyState(wx.WXK_ALT))
        base.direct.fControl = int(wx.GetKeyState(wx.WXK_CONTROL))
        base.direct.fShift = int(wx.GetKeyState(wx.WXK_SHIFT))
        if base.direct.fMouse1:
            self.leftMouseWasDown = True
            messenger.send('DIRECT-mouse1Up')
        else:
            self.leftMouseWasDown = False
        if base.direct.fMouse2:
            messenger.send('DIRECT-mouse2Up')
            self.middleMouseWasDown = True
        else:
            self.middleMouseWasDown = False
        if base.direct.fMouse3:
            messenger.send('DIRECT-mouse3Up')
            self.rightMouseWasDown = True
        else:
            self.rightMouseWasDown = False
        
        
    def onViewFrameChange(self, evt=None):
        if evt.GetSashPosition()[0] < 50:
            evt.Veto()
        elif evt.GetSashPosition()[1] < 50:
            evt.Veto()
        elif self.viewFrame.GetSize()[0] - evt.GetSashPosition()[0] < 50:
            evt.Veto()
        elif self.viewFrame.GetSize()[1] - evt.GetSashPosition()[1] <50:
            evt.Veto()
    
    #binds or unbinds hotkeys to free them up for use in the ui
    def bindKeyEvents(self, toBind=True):
        if toBind:
            self.wxApp.Bind(wx.EVT_CHAR, self.onKeyEvent)
            self.wxApp.Bind(wx.EVT_KEY_DOWN, self.onKeyDownEvent)
            self.wxApp.Bind(wx.EVT_KEY_UP, self.onKeyUpEvent)
        else:
            self.wxApp.Unbind(wx.EVT_CHAR)
            self.wxApp.Unbind(wx.EVT_KEY_DOWN)
            self.wxApp.Unbind(wx.EVT_KEY_UP)

    def createMenu(self):
        menuItem = self.menuFile.Insert(0, ID_NEW, self.MENU_TEXTS[ID_NEW][0])
        self.Bind(wx.EVT_MENU, self.onNew, menuItem)
        
        menuItem = self.menuFile.Insert(1, ID_OPEN, self.MENU_TEXTS[ID_OPEN][0])
        self.Bind(wx.EVT_MENU, self.onOpen, menuItem)

        menuItem = self.menuFile.Insert(2, ID_SAVE, self.MENU_TEXTS[ID_SAVE][0])
        self.Bind(wx.EVT_MENU, self.onSave, menuItem)

        menuItem = self.menuFile.Insert(3, ID_SAVE_AS, self.MENU_TEXTS[ID_SAVE_AS][0])
        self.Bind(wx.EVT_MENU, self.onSaveAs, menuItem)
        
        menuItem = self.menuFile.Insert(4, ID_MERGE, self.MENU_TEXTS[ID_MERGE][0])
        self.Bind(wx.EVT_MENU, self.onMerge, menuItem)
        
        self.menuFile.InsertSeparator(5)
        
        menuItem = self.menuFile.Insert(6, ID_IMPORT, self.MENU_TEXTS[ID_IMPORT][0])
        self.Bind(wx.EVT_MENU, self.onImport, menuItem)
        
        menuItem = self.menuFile.Insert(7, ID_BROWSE_LIB, self.MENU_TEXTS[ID_BROWSE_LIB][0])
        self.Bind(wx.EVT_MENU, self.onBrowseLib, menuItem)
        
        menuItem = self.menuFile.Insert(8, ID_BROWSE_DEFAULT_LIB, self.MENU_TEXTS[ID_BROWSE_DEFAULT_LIB][0])
        self.Bind(wx.EVT_MENU, self.onBrowseDefaultLib, menuItem)
        
        menuItem = self.menuFile.Insert(9, ID_IMPORT_EXTERNAL_LIB, self.MENU_TEXTS[ID_IMPORT_EXTERNAL_LIB][0])
        self.Bind(wx.EVT_MENU, self.onImportLib, menuItem)
        
        menuItem = self.menuFile.Insert(10, ID_EXPORT_LIBRARY, self.MENU_TEXTS[ID_EXPORT_LIBRARY][0])
        self.Bind(wx.EVT_MENU, self.onExportLib, menuItem)
        
        self.menuFile.InsertSeparator(11)
        
        menuItem = self.menuFile.Insert(12, ID_EXPORT, self.MENU_TEXTS[ID_EXPORT][0])
        self.Bind(wx.EVT_MENU, self.onExport, menuItem)
        
        #menuItem = self.menuFile.Insert(13, ID_BVW_EXPORT, self.MENU_TEXTS[ID_BVW_EXPORT][0])
        #self.Bind(wx.EVT_MENU, self.onBVWExport, menuItem)
        
        menuItem = self.menuFile.Insert(13, ID_GAME_EXPORT, self.MENU_TEXTS[ID_GAME_EXPORT][0])
        self.Bind(wx.EVT_MENU, self.onGameExport, menuItem)
        
        menuItem = self.menuFile.Insert(14, ID_GAME_EXPORT_RUN, self.MENU_TEXTS[ID_GAME_EXPORT_RUN][0])
        self.Bind(wx.EVT_MENU, self.onGameExportRun, menuItem)
        
        self.menuFile.InsertSeparator(15)
        
        self.menuEdit = wx.Menu()
        self.menuBar.Insert(1, self.menuEdit, "&Edit")

        menuItem = self.menuEdit.Append(ID_UNDO, self.MENU_TEXTS[ID_UNDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.undo, menuItem)

        menuItem = self.menuEdit.Append(ID_REDO, self.MENU_TEXTS[ID_REDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.redo, menuItem)
        
        menuItem = self.menuEdit.Append(ID_DUPLICATE, self.MENU_TEXTS[ID_DUPLICATE][0])
        self.Bind(wx.EVT_MENU, self.onDuplicate, menuItem)
        
        menuItem = self.menuEdit.Append(ID_DROP_TO_GROUND, self.MENU_TEXTS[ID_DROP_TO_GROUND][0])
        self.Bind(wx.EVT_MENU, self.editor.onDropToGround, menuItem)

        self.menuManip = wx.Menu()
        self.menuBar.Insert(2, self.menuManip, "&Manip")
        
        self.translateMenuItem = self.menuManip.AppendRadioItem(ID_TRANSLATE_MODE, self.MENU_TEXTS[ID_TRANSLATE_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.translateMode, self.translateMenuItem)
        
        self.rotateMenuItem = self.menuManip.AppendRadioItem(ID_ROTATE_MODE, self.MENU_TEXTS[ID_ROTATE_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.rotateMode, self.rotateMenuItem)        
        
        self.scaleMenuItem = self.menuManip.AppendRadioItem(ID_SCALE_MODE, self.MENU_TEXTS[ID_SCALE_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.scaleMode, self.scaleMenuItem)
        
        self.menuManip.AppendSeparator()
        
        self.localMenuItem = self.menuManip.AppendRadioItem(ID_LOCAL_MODE, self.MENU_TEXTS[ID_LOCAL_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.localMode, self.localMenuItem)
        
        self.worldMenuItem = self.menuManip.AppendRadioItem(ID_WORLD_MODE, self.MENU_TEXTS[ID_WORLD_MODE][0])
        self.Bind(wx.EVT_MENU, self.editor.worldMode, self.worldMenuItem)
        
        self.menuOptions = wx.Menu()
        self.menuBar.Insert(3, self.menuOptions, "&Options")

        self.showGridMenuItem = self.menuOptions.Append(ID_SHOW_GRID, self.MENU_TEXTS[ID_SHOW_GRID][0], kind = wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.toggleGrid, self.showGridMenuItem)

        self.gridSizeMenuItem = self.menuOptions.Append(ID_GRID_SIZE, self.MENU_TEXTS[ID_GRID_SIZE][0])
        self.Bind(wx.EVT_MENU, self.onGridSize, self.gridSizeMenuItem)
        
        self.showCollidersMenuItem = self.menuOptions.Append(ID_SHOW_COLLIDERS, self.MENU_TEXTS[ID_SHOW_COLLIDERS][0], kind = wx.ITEM_CHECK)
        self.showCollidersMenuItem.Check(True)
        self.Bind(wx.EVT_MENU, self.onShowColliders, self.showCollidersMenuItem)

        #self.parentToSelectedMenuItem = self.menuOptions.Append(ID_PARENT_TO_SELECTED, self.MENU_TEXTS[ID_PARENT_TO_SELECTED][0], kind = wx.ITEM_CHECK)
        self.preferencesItem = self.menuOptions.Append(ID_PREFERENCES, self.MENU_TEXTS[ID_PREFERENCES][0])
        self.Bind(wx.EVT_MENU, self.onPreferences, self.preferencesItem)
        self.hotKeysMenuItem = self.menuOptions.Append(ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0])
        self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem)
        
        self.sceneStatsMenuItem = self.menuOptions.Append(ID_SCENE_STATS, self.MENU_TEXTS[ID_SCENE_STATS][0])
        self.Bind(wx.EVT_MENU, self.onSceneStats, self.sceneStatsMenuItem)

        self.objectStatsMenuItem = self.menuOptions.Append(ID_SELECTED_OBJECT_STATS, self.MENU_TEXTS[ID_SELECTED_OBJECT_STATS][0])
        self.Bind(wx.EVT_MENU, self.onSelectedObjectStats, self.objectStatsMenuItem)
        
        WxPandaShell.createMenu(self)
        
        self.menuGame = wx.Menu()
        self.menuBar.Append(self.menuGame, "&Game")
        self.showJournalItem = self.menuGame.Append(ID_SHOW_JOURNAL, self.MENU_TEXTS[ID_SHOW_JOURNAL][0])
        self.Bind(wx.EVT_MENU, self.onShowJournal, self.showJournalItem)
        self.conversationEditorItem = self.menuGame.Append(ID_CONVO_EDITOR, self.MENU_TEXTS[ID_CONVO_EDITOR][0])
        self.Bind(wx.EVT_MENU, self.onConversationEditor, self.conversationEditorItem)
        self.inventoryItem = self.menuGame.Append(ID_INVENTORY, self.MENU_TEXTS[ID_INVENTORY][0])
        self.Bind(wx.EVT_MENU, self.onInventory, self.inventoryItem)
        
        #self.menuGame.AppendRadioItem()
        
        
    def updateMenu(self):
        hotKeyDict = {}
        for hotKey in base.direct.hotKeyMap.keys():
            desc = base.direct.hotKeyMap[hotKey]
            hotKeyDict[desc[1]] = hotKey
            
        for id in self.MENU_TEXTS.keys():
            desc = self.MENU_TEXTS[id]
            if desc[1]:
                menuItem = self.menuBar.FindItemById(id)
                hotKey = hotKeyDict.get(desc[1]).replace('control', 'ctrl')
                if hotKey:
                    menuItem.SetText(desc[0] + "\t%s"%hotKey)
    
    def createInterface(self):
        WxPandaShell.createInterface(self)
        
        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.libraryUI = LibraryUI(self.leftBarUpNB,-1, self.editor)
        self.leftBarUpNB.AddPage(self.libraryUI, 'Library')
        self.pandaObjUI = PandaObjUI(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.pandaObjUI, 'Panda Objects')
        self.storyObjUI = StoryObjUI(self.leftBarUpNB, -1, self.editor)
        self.leftBarUpNB.AddPage(self.storyObjUI, 'Story Objects')
        

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')
        self.leftBarDownPane1 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane1, 'Scene List')
        self.soundUI = SoundUI(self.leftBarDownNB, -1, self.editor)
        self.leftBarDownNB.AddPage(self.soundUI, 'Sound List')
        

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()
        self.baseFrame.SplitVertically(self.viewFrame, self.rightFrame, 500)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane, self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.scenesUI = ScenesUI(self.leftBarDownPane1, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)
    
    def onRightDown(self, evt=None):
        """Invoked when the viewport is right-clicked."""
        if evt == None:
            mpos = wx.GetMouseState()
            mpos = self.ScreenToClient((mpos.x, mpos.y))
        else:
            mpos = evt.GetPosition()

        base.direct.fMouse3 = 0
        #self.PopupMenu(self.contextMenu, mpos)

    def onKeyDownEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 1
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 1
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 1
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down')
        else:
            evt.Skip()

    def onKeyUpEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 0
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 0
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 0
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up-up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down-up')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left-up')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down-up')
        else:
            evt.Skip()
        
    def onKeyEvent(self, evt):
        input = ''
        if evt.GetKeyCode() in range(97, 123): # for keys from a to z
            if evt.GetModifiers() == 4: # when shift is pressed while caps lock is on
                input = 'shift-%s'%chr(evt.GetKeyCode())
            else:
                input = chr(evt.GetKeyCode())
        elif evt.GetKeyCode() in range(65, 91):
            if evt.GetModifiers() == 4: # when shift is pressed
                input = 'shift-%s'%chr(evt.GetKeyCode() + 32)
            else:
                input = chr(evt.GetKeyCode() + 32)
        elif evt.GetKeyCode() in range(1, 27): # for keys from a to z with control
            if evt.GetModifiers() == 2:
                input = 'control-%s'%chr(evt.GetKeyCode()+96)
        elif evt.GetKeyCode() == wx.WXK_DELETE:
            input = 'delete'
        elif evt.GetKeyCode() == wx.WXK_ESCAPE:
            input = 'escape'
        else:
            if evt.GetModifiers() == 4:
                input = 'shift-%s'%chr(evt.GetKeyCode())
            elif evt.GetModifiers() == 2:
                input = 'control-%s'%chr(evt.GetKeyCode())
            elif evt.GetKeyCode() < 256:
                input = chr(evt.GetKeyCode())
        if input in base.direct.hotKeyMap.keys():
            keyDesc = base.direct.hotKeyMap[input]
            messenger.send(keyDesc[1])

    def reset(self):
        self.sceneGraphUI.reset()
        self.scenesUI.reset()
        self.layerEditorUI.reset()

    def onNew(self, evt=None):
        self.editor.reset()
        self.editor.newProj()

    def onOpen(self, evt=None):
        dialog = wx.FileDialog(None, "Choose a project file", os.getcwd(), "", "*.proj", wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.load(Filename.fromOsSpecific(dialog.GetPath()))
            self.libraryUI.update()
            self.storyObjUI.update()
            self.soundUI.update()
        dialog.Destroy()

    def onSave(self, evt=None):
        if not self.editor.saved:
            self.onSaveAs(evt)
        else:
            self.editor.save()

    def onSaveAs(self, evt):
        while True:
            dialog = SaveProjectUI(self, -1, "Save Project")
            if dialog.ShowModal() == wx.ID_OK:
                baseDir = dialog.dir
                projName = Util.toFilename(dialog.projName)
                if projName == '' or projName != dialog.projName:
                   dlg = wx.MessageDialog(self, "Invalid Project Name", style = wx.OK)
                   dlg.ShowModal()
                   dlg.Destroy()
                elif baseDir == None:
                    dlg = wx.MessageDialog(self, "Please Choose a save location", style = wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                else: 
                    dir = baseDir + '/' + projName
                    if os.path.exists(dir.toOsSpecific()):
                        dlg = wx.MessageDialog(self, "A folder with that name already exists in the"\
                                               + " directory you have chosen.  Please choose another name.", style = wx.OK)
                        dlg.ShowModal()
                        dlg.Destroy()
                    else:
                        self.editor.saveAs(dir, projName)
                        break
            else:
                break
            
    def onImport(self, evt=None):
        importUI = ImportUI(self, -1, "Import")      
        importUI.Show()
        
    def onImportLib(self, evt=None):
        dlg = wx.FileDialog(self, "Choose a library file to import.", wildcard = "*.lbr", style = wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
                    
            self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
            vfs = VirtualFileSystem.getGlobalPtr()
            filename = Filename.fromOsSpecific(dlg.GetPath())
            otherName = filename.getBasenameWoExtension().strip()
            ## #ZJC - 07/29/2011: Code deemed unnecessary
            ## base.le.prefixImportMerge.append(otherName) # ZJC - 07/27/2011: When a library is imported, add prefix to the list
            
            mountPt = "./externalLib_" + otherName
            vfs.mount(filename, mountPt, VirtualFileSystem.MFReadOnly)
            otherLib = Library.Library(Filename(mountPt))
            
            self.editor.lib.mergeWith(otherLib, otherName=otherName, saveAfter=True)
            vfs.unmountPoint(mountPt)
            
            self.libraryUI.update()
            self.storyObjUI.update()
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
            
    def onBrowseLib(self, evt=None):        
        dlg = wx.FileDialog(self, "Choose a library file to browse.", wildcard = "*.lbr", style = wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
                    
            self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
            vfs = VirtualFileSystem.getGlobalPtr()
            filename = Filename.fromOsSpecific(dlg.GetPath())
            otherName = filename.getBasenameWoExtension().strip()
            mountPt = "./externalLib_" + otherName
            vfs.mount(filename, mountPt, VirtualFileSystem.MFReadOnly)
            otherLib = Library.Library(Filename(mountPt))
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
            
            libBrowser = LibraryBrowserUI(self, otherLib, otherName)
            
            libBrowser.ShowModal()
            
            vfs.unmountPoint(mountPt)
        
            self.libraryUI.update()
            self.storyObjUI.update()
    
    def onBrowseDefaultLib(self, evt=None):
            self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
            vfs = VirtualFileSystem.getGlobalPtr()
            filename = Filename('models/Orelia.lbr')#'models/default.lbr')
            otherName = filename.getBasenameWoExtension().strip()
            mountPt = "./externalLib_" + otherName
            vfs.mount(filename, mountPt, VirtualFileSystem.MFReadOnly)
            otherLib = Library.Library(Filename(mountPt))
            self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
            
            libBrowser = LibraryBrowserUI(self, otherLib, otherName)
            
            libBrowser.ShowModal()
            
            vfs.unmountPoint(mountPt)
        
            self.libraryUI.update()
            self.storyObjUI.update()
        
    def onExportLib(self, evt=None):
        if self.editor.saved:
            dlg = wx.FileDialog(self, "Choose a filename for your exported library.", defaultDir = self.editor.projDir.toOsSpecific(),\
                defaultFile = self.editor.currentProj.name.strip() + ".lbr", wildcard="*.lbr", style = wx.FD_SAVE|wx.OVERWRITE_PROMPT)
                
            if dlg.ShowModal() == wx.ID_OK:
                f = Filename.fromOsSpecific(dlg.GetPath())
                if f.getDirname() != self.editor.projDir.getFullpath():
                    msg = wx.MessageDialog(self, "Must save to your current project directory.", style= wx.OK|wx.ICON_ERROR)
                    msg.ShowModal()
                    return
                self.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
                self.editor.lib.exportToMultifile(Filename.fromOsSpecific(dlg.GetPath()))
                self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
            dlg.Destroy()
        else:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Library Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
    
    def onExport(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
        else:
#            dlg = wx.FileDialog(self, "Choose a python file to export to.", \
#            defaultDir= self.editor.currentProj.dir.toOsSpecific(),\
#            defaultFile = self.editor.currentProj.name + '.py', wildcard="*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
#            
#            if dlg.ShowModal() == wx.ID_OK:
#                self.editor.standaloneExporter.export(dlg.GetPath())
            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.export(opts)
                self.editor.save()
    def onBVWExport(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            
            dlg.ShowModal()
            dlg.Destroy()
        else:
#            dlg = wx.FileDialog(self, "Choose a python file to export to.", \
#            defaultDir= self.editor.currentProj.dir.toOsSpecific(),\
#            defaultFile = self.editor.currentProj.name + '.py', wildcard="*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
#            
#            if dlg.ShowModal() == wx.ID_OK:
#                self.editor.standaloneExporter.exportBVW(dlg.GetPath())
            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.exportBVW(opts)
    
    def onGameExport(self, evt):
        if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
        else:
#            dialog = wx.MessageDialog(self, "Cool stuff goes here", "Export as Game", wx.OK|wx.ICON_EXCLAMATION)
#            dialog.ShowModal()
#            dialog.Destroy()
            
            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.exportGame(opts)
                self.editor.save()
                        
    def onGameExportRun(self, evt):
       if not self.editor.saved:
            dlg = wx.MessageDialog(self, "Please save the project first.", "Cannot Export Before Saving",\
            style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
       else:
#            dialog = wx.MessageDialog(self, "Cool stuff goes here", "Export as Game", wx.OK|wx.ICON_EXCLAMATION)
#            dialog.ShowModal()
#            dialog.Destroy()
            
            dlg = ExportOptionsDialog(self)
            opts = dlg.ShowModal()
            if opts['path'] != '':
                self.editor.standaloneExporter.exportGame(opts)
                self.editor.save() 
            
                filename = Filename("runWorld.bat")
                filename.setDirname(self.editor.projDir.getFullpath())
                #print filename
                cwd = os.getcwd()
                os.chdir(self.editor.projDir.toOsSpecific())
                subprocess.Popen(filename.toOsSpecific())
                os.chdir(cwd)
        
    def onMerge(self, evt):
        dlg = MergeProjectUI(self)
        
        dlg.ShowModal()
        
        dlg.Destroy()
    
    def onSceneStats(self, evt=None):
        f = StringIO()
        temp = sys.stdout
        sys.stdout = f
        render.analyze()
        
        sys.stdout = temp
        
        dlg = wx.MessageDialog(self, f.getvalue(), caption="Scene Statistics")
        dlg.ShowModal()
    
    def onSelectedObjectStats(self, evt=None):
        np = base.direct.selected.last
        
        f = StringIO()
        temp = sys.stdout
        sys.stdout = f
        np.analyze()
        
        sys.stdout = temp
        
        dlg = wx.MessageDialog(self, f.getvalue(), caption="Object Statistics")
        dlg.ShowModal()
    
    def onDuplicate(self, evt=None):
        action = ActionDuplicateSelected(self.editor)
        self.editor.actionMgr.push(action)
        action()
        #self.editor.objectMgr.duplicateSelected()

    def toggleGrid(self, evt):
        if self.showGridMenuItem.IsChecked():
            for grid in [self.perspView.grid, self.topView.grid, self.frontView.grid, self.leftView.grid]:
                if grid.isHidden():
                    grid.show()
        else:
            for grid in [self.perspView.grid, self.topView.grid, self.frontView.grid, self.leftView.grid]:
                if not grid.isHidden():
                    grid.hide()
                    
    def onShowJournal(self, evt):
        journalDialog = JournalDialog(self, -1, "Show Journal", self.editor)      
        journalDialog.Show()
        
    def onConversationEditor(self,evt):
        editor = ConversationEditor(self,self.editor)
        #editor.Show()
    
    def onInventory(self, evt):
        inventoryDialog = LEInventoryUI(self,-1,"Inventory Map", self.editor)
        inventoryDialog.Show()
        #inventoryDialog.ShowModal()
            
    def onGridSize(self, evt):
        gridSizeUI = GridSizeUI(self, -1, 'Change Grid Size', self.perspView.grid.gridSize, self.perspView.grid.gridSpacing)
        gridSizeUI.ShowModal()
        gridSizeUI.Destroy()
    
    def onShowColliders(self, evt):
        if self.showCollidersMenuItem.IsChecked():
            self.editor.objectMgr.showColliders = True
            for c in self.editor.objectMgr.colliders:
                c.show()
        else:
            self.editor.objectMgr.showColliders = False

            for c in self.editor.objectMgr.colliders:
                c.hide()


    def onDestroy(self, evt):
        dlg = wx.MessageDialog(self, "Are you sure you want to exit?", style = wx.YES_NO)
        if dlg.ShowModal() == wx.ID_YES:
            self.editor.saveSettings()
            self.editor.reset()
            if self.editor.tempDir and os.path.exists(self.editor.tempDir):
                try:
                    shutil.rmtree(self.editor.tempDir)
                except:
                    pass
            dlg.Destroy()
            return True
        else:       
            dlg.Destroy()
            return False

    def updateGrids(self, newSize, newSpacing):
        self.perspView.grid.gridSize = newSize
        self.perspView.grid.gridSpacing = newSpacing
        self.perspView.grid.updateGrid()

        self.topView.grid.gridSize = newSize
        self.topView.grid.gridSpacing = newSpacing
        self.topView.grid.updateGrid()

        self.frontView.grid.gridSize = newSize
        self.frontView.grid.gridSpacing = newSpacing
        self.frontView.grid.updateGrid()

        self.leftView.grid.gridSize = newSize
        self.leftView.grid.gridSpacing = newSpacing
        self.leftView.grid.updateGrid()        

    def onHotKeys(self, evt):
        hotKeyUI = HotKeyUI(self, -1, 'Hot Key List')
        hotKeyUI.ShowModal()
        hotKeyUI.Destroy()
    
    def onPreferences(self, evt):
        ##Show a dialog with the few editor prefs we have
        dlg = PrefsDialog(self)
        dlg.Show()
        
    def buildContextMenu(self, nodePath):
        for menuItem in self.contextMenu.GetMenuItems():
            self.contextMenu.RemoveItem(menuItem)

        #self.contextMenu.addItem('Replace With Selected Asset', call=self.editor.objectMgr.replaceObjectWithAsset)
        self.contextMenu.addItem('Duplicate', call = self.onDuplicate)
예제 #9
0
class LevelEditorUIBase(WxPandaShell):
    """ Class for Panda3D LevelEditor """
    def __init__(self, editor):
        self.MENU_TEXTS.update({
            ID_NEW: ("&New", "LE-NewScene"),
            ID_OPEN: ("&Open", "LE-OpenScene"),
            ID_SAVE: ("&Save", "LE-SaveScene"),
            ID_SAVE_AS: ("Save &As", None),
            ID_EXPORT_TO_MAYA: ("Export to Maya", None),
            wx.ID_EXIT: ("&Quit", "LE-Quit"),
            ID_DUPLICATE: ("&Duplicate", "LE-Duplicate"),
            ID_MAKE_LIVE: ("Make &Live", "LE-MakeLive"),
            ID_UNDO: ("&Undo", "LE-Undo"),
            ID_REDO: ("&Redo", "LE-Redo"),
            ID_SHOW_GRID: ("&Show Grid", None),
            ID_GRID_SIZE: ("&Grid Size", None),
            ID_GRID_SNAP: ("Grid S&nap", None),
            ID_SHOW_PANDA_OBJECT: ("Show &Panda Objects", None),
            ID_HOT_KEYS: ("&Hot Keys", None),
            ID_PARENT_TO_SELECTED: ("&Parent To Selected", None),
            ID_CREATE_CURVE: ("&Create Curve", None),
            ID_EDIT_CURVE: ("&Edit Curve", None),
            ID_CURVE_ANIM: ("&Curve Animation", None),
            ID_ANIM: ("&Edit Animation", None),
            ID_GRAPH: ("&Graph Editor", None)
        })

        self.editor = editor
        WxPandaShell.__init__(self, fStartDirect=True)
        self.contextMenu = ViewportMenu()
        self.bindKeyEvents(True)

    def bindKeyEvents(self, toBind=True):
        if toBind:
            self.wxApp.Bind(wx.EVT_CHAR, self.onKeyEvent)
            self.wxApp.Bind(wx.EVT_KEY_DOWN, self.onKeyDownEvent)
            self.wxApp.Bind(wx.EVT_KEY_UP, self.onKeyUpEvent)
        else:
            self.wxApp.Unbind(wx.EVT_CHAR)
            self.wxApp.Unbind(wx.EVT_KEY_DOWN)
            self.wxApp.Unbind(wx.EVT_KEY_UP)

    def createMenu(self):
        menuItem = self.menuFile.Insert(0, ID_NEW, self.MENU_TEXTS[ID_NEW][0])
        self.Bind(wx.EVT_MENU, self.onNew, menuItem)

        menuItem = self.menuFile.Insert(1, ID_OPEN,
                                        self.MENU_TEXTS[ID_OPEN][0])
        self.Bind(wx.EVT_MENU, self.onOpen, menuItem)

        menuItem = self.menuFile.Insert(2, ID_SAVE,
                                        self.MENU_TEXTS[ID_SAVE][0])
        self.Bind(wx.EVT_MENU, self.onSave, menuItem)

        menuItem = self.menuFile.Insert(3, ID_SAVE_AS,
                                        self.MENU_TEXTS[ID_SAVE_AS][0])
        self.Bind(wx.EVT_MENU, self.onSaveAs, menuItem)

        menuItem = self.menuFile.Insert(4, ID_EXPORT_TO_MAYA,
                                        self.MENU_TEXTS[ID_EXPORT_TO_MAYA][0])
        self.Bind(wx.EVT_MENU, self.onExportToMaya, menuItem)

        self.menuEdit = wx.Menu()
        self.menuBar.Insert(1, self.menuEdit, "&Edit")

        menuItem = self.menuEdit.Append(ID_DUPLICATE,
                                        self.MENU_TEXTS[ID_DUPLICATE][0])
        self.Bind(wx.EVT_MENU, self.onDuplicate, menuItem)

        menuItem = self.menuEdit.Append(ID_MAKE_LIVE,
                                        self.MENU_TEXTS[ID_MAKE_LIVE][0])
        self.Bind(wx.EVT_MENU, self.onMakeLive, menuItem)

        menuItem = self.menuEdit.Append(ID_UNDO, self.MENU_TEXTS[ID_UNDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.undo, menuItem)

        menuItem = self.menuEdit.Append(ID_REDO, self.MENU_TEXTS[ID_REDO][0])
        self.Bind(wx.EVT_MENU, self.editor.actionMgr.redo, menuItem)

        self.menuOptions = wx.Menu()
        self.menuBar.Insert(2, self.menuOptions, "&Options")

        self.showGridMenuItem = self.menuOptions.Append(
            ID_SHOW_GRID, self.MENU_TEXTS[ID_SHOW_GRID][0], kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.toggleGrid, self.showGridMenuItem)

        self.gridSizeMenuItem = self.menuOptions.Append(
            ID_GRID_SIZE, self.MENU_TEXTS[ID_GRID_SIZE][0])
        self.Bind(wx.EVT_MENU, self.onGridSize, self.gridSizeMenuItem)

        self.gridSnapMenuItem = self.menuOptions.Append(
            ID_GRID_SNAP, self.MENU_TEXTS[ID_GRID_SNAP][0], kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.toggleGridSnap, self.gridSnapMenuItem)

        self.showPandaObjectsMenuItem = self.menuOptions.Append(
            ID_SHOW_PANDA_OBJECT,
            self.MENU_TEXTS[ID_SHOW_PANDA_OBJECT][0],
            kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onShowPandaObjects,
                  self.showPandaObjectsMenuItem)

        self.parentToSelectedMenuItem = self.menuOptions.Append(
            ID_PARENT_TO_SELECTED,
            self.MENU_TEXTS[ID_PARENT_TO_SELECTED][0],
            kind=wx.ITEM_CHECK)

        self.hotKeysMenuItem = self.menuOptions.Append(
            ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0])
        self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem)

        self.menuCurve = wx.Menu()
        self.menuBar.Insert(3, self.menuCurve, "&CurveMode")

        self.createCurveMenuItem = self.menuCurve.Append(
            ID_CREATE_CURVE,
            self.MENU_TEXTS[ID_CREATE_CURVE][0],
            kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onCreateCurve, self.createCurveMenuItem)

        self.editCurveMenuItem = self.menuCurve.Append(
            ID_EDIT_CURVE,
            self.MENU_TEXTS[ID_EDIT_CURVE][0],
            kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onEditCurve, self.editCurveMenuItem)

        self.curveAnimMenuItem = self.menuCurve.Append(
            ID_CURVE_ANIM,
            self.MENU_TEXTS[ID_CURVE_ANIM][0],
            kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onCurveAnim, self.curveAnimMenuItem)

        self.menuAnim = wx.Menu()
        self.menuBar.Insert(4, self.menuAnim, "&AnimationMode")

        self.editAnimMenuItem = self.menuAnim.Append(
            ID_ANIM, self.MENU_TEXTS[ID_ANIM][0], kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onAnimation, self.editAnimMenuItem)

        self.graphEditorMenuItem = self.menuAnim.Append(
            ID_GRAPH, self.MENU_TEXTS[ID_GRAPH][0], kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.onGraphEditor, self.graphEditorMenuItem)

        WxPandaShell.createMenu(self)

    def onGraphEditor(self, e):
        if base.direct.selected.last == None:
            dlg = wx.MessageDialog(None, 'Please select a object first.',
                                   'NOTICE', wx.OK)
            dlg.ShowModal()
            dlg.Destroy()
            self.graphEditorMenuItem.Check(False)
        else:
            currentObj = self.editor.objectMgr.findObjectByNodePath(
                base.direct.selected.last)
            self.graphEditorUI = GraphEditorUI(self, self.editor, currentObj)
            self.graphEditorUI.Show()
            self.graphEditorMenuItem.Check(True)

    def onAnimation(self, e):
        if self.editor.mode != self.editor.ANIM_MODE:
            self.animUI = AnimControlUI(self, self.editor)
            self.animUI.Show()
            self.editor.mode = self.editor.ANIM_MODE
        if self.editor.mode == self.editor.ANIM_MODE:
            self.editAnimMenuItem.Check(True)

    def onCurveAnim(self, e):
        self.curveAnimUI = CurveAnimUI(self, self.editor)
        self.curveAnimUI.Show()
        self.curveAnimMenuItem.Check(True)

    def onCreateCurve(self, e):
        """Function to invoke curve creating, need to check previous mode"""
        if self.editor.mode == self.editor.CREATE_CURVE_MODE:
            self.createCurveMenuItem.Check(False)
            self.editor.curveEditor.onBaseMode()
        else:
            if self.editor.mode == self.editor.EDIT_CURVE_MODE:
                self.editor.curveEditor.onBaseMode()
                self.editCurveMenuItem.Check(False)
                self.createCurveMenuItem.Check(True)
                self.onCreateCurve(None)
            else:
                self.currentView = self.getCurrentView()
                if self.currentView == None:
                    dlg = wx.MessageDialog(
                        None,
                        'Please select a viewport first.Do not support curve creation under four viewports.',
                        'NOTICE', wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                    self.createCurveMenuItem.Check(False)
                else:
                    self.editor.mode = self.editor.CREATE_CURVE_MODE
                    self.editor.updateStatusReadout(
                        'Please press ENTER to end the curve creation.')
                    degreeUI = CurveDegreeUI(self, -1, 'Curve Degree')
                    degreeUI.ShowModal()
                    degreeUI.Destroy()
                    base.direct.manipulationControl.disableManipulation()
                    self.editCurveMenuItem.Check(False)

    def onEditCurve(self, e):
        """Function to invoke curve editing and translate global information to local information. Need to check previous mode"""
        if self.editor.mode == self.editor.EDIT_CURVE_MODE:
            self.editCurveMenuItem.Check(False)
            self.editor.curveEditor.onBaseMode()
        else:
            if self.editor.mode == self.editor.CREATE_CURVE_MODE:
                self.editor.curveEditor.onBaseMode()
                self.editCurveMenuItem.Check(True)
                self.createCurveMenuItem.Check(False)
                self.onEditCurve(None)
            else:
                if base.direct.selected.last == None:
                    dlg = wx.MessageDialog(None,
                                           'Please select a curve first.',
                                           'NOTICE', wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                    self.editCurveMenuItem.Check(False)
                if base.direct.selected.last != None:
                    base.direct.manipulationControl.enableManipulation()
                    self.createCurveMenuItem.Check(False)
                    self.curveObj = self.editor.objectMgr.findObjectByNodePath(
                        base.direct.selected.last)
                    if self.curveObj[OG.OBJ_DEF].name == '__Curve__':
                        self.editor.mode = self.editor.EDIT_CURVE_MODE
                        self.editor.updateStatusReadout(
                            'Please press ENTER to end the curve editing.')
                        self.editor.curveEditor.currentRope = self.curveObj[
                            OG.OBJ_NP]
                        self.editor.curveEditor.curveControl = self.curveObj[
                            OG.OBJ_PROP]['curveInfo']
                        self.editor.curveEditor.degree = self.curveObj[
                            OG.OBJ_PROP]['Degree']
                        for item in self.editor.curveEditor.curveControl:
                            item[1].show()
                            self.editor.curveEditor.curve.append(
                                (None, item[1].getPos()))
                    else:
                        dlg = wx.MessageDialog(None,
                                               'Please select a curve first.',
                                               'NOTICE', wx.OK)
                        dlg.ShowModal()
                        dlg.Destroy()
                        self.editCurveMenuItem.Check(False)

    def updateMenu(self):
        hotKeyDict = {}
        for hotKey in base.direct.hotKeyMap.keys():
            desc = base.direct.hotKeyMap[hotKey]
            hotKeyDict[desc[1]] = hotKey

        for id in self.MENU_TEXTS.keys():
            desc = self.MENU_TEXTS[id]
            if desc[1]:
                menuItem = self.menuBar.FindItemById(id)
                hotKey = hotKeyDict.get(desc[1])
                if hotKey:
                    menuItem.SetText(desc[0] + "\t%s" % hotKey)

    def createInterface(self):
        WxPandaShell.createInterface(self)

        self.leftBarUpNB = wx.Notebook(self.leftBarUpPane, style=wx.NB_BOTTOM)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarUpNB, 1, wx.EXPAND)
        self.leftBarUpPane.SetSizer(sizer)
        self.leftBarUpPane0 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane0, 'Object Palette')
        self.leftBarUpPane1 = wx.Panel(self.leftBarUpNB, -1)
        self.leftBarUpNB.AddPage(self.leftBarUpPane1, 'Proto Palette')

        self.leftBarDownNB = wx.Notebook(self.leftBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.leftBarDownNB, 1, wx.EXPAND)
        self.leftBarDownPane.SetSizer(sizer)
        self.leftBarDownPane0 = wx.Panel(self.leftBarDownNB, -1)
        self.leftBarDownNB.AddPage(self.leftBarDownPane0, 'Scene Graph')

        self.rightBarDownNB = wx.Notebook(self.rightBarDownPane)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.rightBarDownNB, 1, wx.EXPAND)
        self.rightBarDownPane.SetSizer(sizer)
        self.rightBarDownPane0 = wx.Panel(self.rightBarDownNB, -1)
        self.rightBarDownNB.AddPage(self.rightBarDownPane0, 'Layers')

        self.topView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.topView))
        self.frontView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.frontView))
        self.leftView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.leftView))
        self.perspView.SetDropTarget(
            PandaTextDropTarget(self.editor, self.perspView))

        self.rightBarDownPane.Layout()
        self.Layout()

        self.objectPaletteUI = ObjectPaletteUI(self.leftBarUpPane0,
                                               self.editor)
        self.protoPaletteUI = ProtoPaletteUI(self.leftBarUpPane1, self.editor)
        self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane,
                                                 self.editor)
        self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane0, self.editor)
        self.layerEditorUI = LayerEditorUI(self.rightBarDownPane0, self.editor)

        self.showGridMenuItem.Check(True)

    def onRightDown(self, evt=None):
        """Invoked when the viewport is right-clicked."""
        if evt == None:
            mpos = wx.GetMouseState()
            mpos = self.ScreenToClient((mpos.x, mpos.y))
        else:
            mpos = evt.GetPosition()

        base.direct.fMouse3 = 0
        self.PopupMenu(self.contextMenu, mpos)

    def onKeyDownEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 1
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 1
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 1
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down')
        else:
            evt.Skip()

    def onKeyUpEvent(self, evt):
        if evt.GetKeyCode() == wx.WXK_ALT:
            base.direct.fAlt = 0
        elif evt.GetKeyCode() == wx.WXK_CONTROL:
            base.direct.fControl = 0
        elif evt.GetKeyCode() == wx.WXK_SHIFT:
            base.direct.fShift = 0
        elif evt.GetKeyCode() == wx.WXK_UP:
            messenger.send('arrow_up-up')
        elif evt.GetKeyCode() == wx.WXK_DOWN:
            messenger.send('arrow_down-up')
        elif evt.GetKeyCode() == wx.WXK_LEFT:
            messenger.send('arrow_left-up')
        elif evt.GetKeyCode() == wx.WXK_RIGHT:
            messenger.send('arrow_right-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEUP:
            messenger.send('page_up-up')
        elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
            messenger.send('page_down-up')
        else:
            evt.Skip()

    def onKeyEvent(self, evt):
        input = ''
        if evt.GetKeyCode() in range(97, 123):  # for keys from a to z
            if evt.GetModifiers(
            ) == 4:  # when shift is pressed while caps lock is on
                input = 'shift-%s' % chr(evt.GetKeyCode())
            else:
                input = chr(evt.GetKeyCode())
        elif evt.GetKeyCode() in range(65, 91):
            if evt.GetModifiers() == 4:  # when shift is pressed
                input = 'shift-%s' % chr(evt.GetKeyCode() + 32)
            else:
                input = chr(evt.GetKeyCode() + 32)
        elif evt.GetKeyCode() in range(
                1, 27):  # for keys from a to z with control
            input = 'control-%s' % chr(evt.GetKeyCode() + 96)
        elif evt.GetKeyCode() == wx.WXK_DELETE:
            input = 'delete'
        elif evt.GetKeyCode() == wx.WXK_ESCAPE:
            input = 'escape'
        else:
            if evt.GetModifiers() == 4:
                input = 'shift-%s' % chr(evt.GetKeyCode())
            elif evt.GetModifiers() == 2:
                input = 'control-%s' % chr(evt.GetKeyCode())
            elif evt.GetKeyCode() < 256:
                input = chr(evt.GetKeyCode())
        if input in base.direct.hotKeyMap.keys():
            keyDesc = base.direct.hotKeyMap[input]
            messenger.send(keyDesc[1])

    def reset(self):
        self.sceneGraphUI.reset()
        self.layerEditorUI.reset()

    def onNew(self, evt=None):
        self.editor.reset()

    def onOpen(self, evt=None):
        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py",
                               wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.load(dialog.GetPath())
            self.editor.setTitleWithFilename(dialog.GetPath())
        dialog.Destroy()

    def onSave(self, evt=None):
        if self.editor.currentFile is None or\
           not self.editor.currentFile.endswith('.py'):
            return self.onSaveAs(evt)
        else:
            self.editor.save()

    def onSaveAs(self, evt):
        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py",
                               wx.SAVE)
        result = True
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.saveAs(dialog.GetPath())
            self.editor.setTitleWithFilename(dialog.GetPath())
        else:
            result = False
        dialog.Destroy()
        return result

    def onExportToMaya(self, evt):
        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb",
                               wx.SAVE)
        if dialog.ShowModal() == wx.ID_OK:
            self.editor.exportToMaya(dialog.GetPath())
        dialog.Destroy()

    def onDuplicate(self, evt):
        self.editor.objectMgr.duplicateSelected()

    def onMakeLive(self, evt):
        self.editor.objectMgr.makeSelectedLive()

    def toggleGrid(self, evt):
        if self.showGridMenuItem.IsChecked():
            for grid in [
                    self.perspView.grid, self.topView.grid,
                    self.frontView.grid, self.leftView.grid
            ]:
                if grid.isHidden():
                    grid.show()
        else:
            for grid in [
                    self.perspView.grid, self.topView.grid,
                    self.frontView.grid, self.leftView.grid
            ]:
                if not grid.isHidden():
                    grid.hide()

    def toggleGridSnap(self, evt):
        if self.gridSnapMenuItem.IsChecked():
            base.direct.manipulationControl.fGridSnap = 1
            for grid in [
                    self.perspView.grid, self.topView.grid,
                    self.frontView.grid, self.leftView.grid
            ]:
                grid.fXyzSnap = 1

        else:
            base.direct.manipulationControl.fGridSnap = 0
            for grid in [
                    self.perspView.grid, self.topView.grid,
                    self.frontView.grid, self.leftView.grid
            ]:
                grid.fXyzSnap = 0

    def onGridSize(self, evt):
        gridSizeUI = GridSizeUI(self, -1, 'Change Grid Size',
                                self.perspView.grid.gridSize,
                                self.perspView.grid.gridSpacing)
        gridSizeUI.ShowModal()
        gridSizeUI.Destroy()

    def onShowPandaObjects(self, evt):
        self.sceneGraphUI.showPandaObjectChildren()

    def onDestroy(self, evt):
        self.editor.protoPalette.saveToFile()
        self.editor.saveSettings()
        self.editor.reset()

    def updateGrids(self, newSize, newSpacing):
        self.perspView.grid.gridSize = newSize
        self.perspView.grid.gridSpacing = newSpacing
        self.perspView.grid.updateGrid()

        self.topView.grid.gridSize = newSize
        self.topView.grid.gridSpacing = newSpacing
        self.topView.grid.updateGrid()

        self.frontView.grid.gridSize = newSize
        self.frontView.grid.gridSpacing = newSpacing
        self.frontView.grid.updateGrid()

        self.leftView.grid.gridSize = newSize
        self.leftView.grid.gridSpacing = newSpacing
        self.leftView.grid.updateGrid()

    def onHotKeys(self, evt):
        hotKeyUI = HotKeyUI(self, -1, 'Hot Key List')
        hotKeyUI.ShowModal()
        hotKeyUI.Destroy()

    def buildContextMenu(self, nodePath):
        for menuItem in self.contextMenu.GetMenuItems():
            self.contextMenu.RemoveItem(menuItem)

        self.contextMenu.addItem('Replace This', call=lambda\
                                 p0=None, p1=False:self.replaceObject(p0, p1))

        self.contextMenu.addItem('Replace All', call=lambda\
                                 p0=None, p1=True:self.replaceObject(p0, p1))
        self.contextMenu.AppendSeparator()

    def replaceObject(self, evt, all=False):
        currObj = self.editor.objectMgr.findObjectByNodePath(
            base.direct.selected.last)
        if currObj is None:
            print 'No valid object is selected for replacement'
            return

        targetType = self.editor.ui.objectPaletteUI.getSelected()
        if targetType is None:
            print 'No valid target type is selected for replacement'
            return

        if all:
            typeName = currObj[OG.OBJ_DEF].name
            objs = self.editor.objectMgr.findObjectsByTypeName(typeName)
            for obj in objs:
                self.editor.objectMgr.replaceObjectWithTypeName(
                    obj, targetType)
        else:
            self.editor.objectMgr.replaceObjectWithTypeName(
                currObj, targetType)