Esempio n. 1
0
    def __init__(self, editorSession, dialog):
        super(FindReplaceNBT, self).__init__()
        self.editorSession = editorSession
        self.widget = load_ui("find_replace_nbt.ui")
        self.dialog = dialog

        self.resultsWidget = load_ui("find_replace_nbt_results.ui")
        self.resultsDockWidget = MCEDockWidget("NBT Search", objectName="nbtSearch")
        self.resultsDockWidget.setWidget(self.resultsWidget)
        self.resultsDockWidget.hide()

        self.resultsModel = NBTResultsModel()
        self.resultsWidget.resultsView.setModel(self.resultsModel)
        self.resultsWidget.resultsView.clicked.connect(self.resultsViewIndexClicked)

        # --- Buttons ---
        self.widget.findButton.clicked.connect(self.find)

        self.resultsWidget.stopButton.clicked.connect(self.stop)
        self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)

        self.resultsWidget.replaceSelectedButton.clicked.connect(self.replaceSelected)
        self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)

        self.widget.searchNameCheckbox.toggled.connect(self.searchForToggled)
        self.widget.searchValueCheckbox.toggled.connect(self.searchForToggled)
        self.findTimer = None
        self.finder = None

        # --- Search for... ---
        self.widget.nameField.setText(nbtReplaceSettings.nameField.value(""))
        self.widget.searchNameCheckbox.setChecked(len(self.widget.nameField.text()) > 0)
        self.widget.nameField.textChanged.connect(self.nameFieldChanged)

        self.widget.valueField.setText(nbtReplaceSettings.valueField.value(""))
        self.widget.searchValueCheckbox.setChecked(len(self.widget.valueField.text()) > 0)
        self.widget.valueField.textChanged.connect(self.valueFieldChanged)

        # --- Search in... ---
        self.widget.searchEntitiesCheckbox.setChecked(nbtReplaceSettings.searchEntitiesChecked.value())
        self.widget.searchEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchEntitiesChecked.setValue)

        self.widget.entityIDField.setText(nbtReplaceSettings.entityIDField.value())
        self.widget.entityIDField.textChanged.connect(self.entityIDFieldChanged)

        self.widget.searchTileEntitiesCheckbox.setChecked(nbtReplaceSettings.searchTileEntitiesChecked.value())
        self.widget.searchTileEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchTileEntitiesChecked.setValue)

        self.widget.tileEntityIDField.setText(nbtReplaceSettings.tileEntityIDField.value())
        self.widget.tileEntityIDField.textChanged.connect(self.tileEntityIDFieldChanged)

        # --- Replace with... ---
        self.widget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
        self.resultsWidget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
        self.widget.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)

        self.widget.replaceNameCheckbox.setChecked(len(self.widget.replaceNameField.text()))
        self.resultsWidget.replaceNameCheckbox.setChecked(len(self.widget.replaceNameField.text()))

        self.widget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
        self.resultsWidget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
        self.widget.replaceValueField.textChanged.connect(self.replaceValueFieldChanged)

        self.widget.replaceValueCheckbox.setChecked(len(self.widget.replaceValueField.text()))
        self.resultsWidget.replaceValueCheckbox.setChecked(len(self.widget.replaceValueField.text()))

        self.widget.replaceValueTagTypeComboBox.setCurrentIndex(nbtReplaceSettings.replaceValueTagType.value())
        self.widget.replaceValueTagTypeComboBox.currentIndexChanged[int].connect(self.valueTagTypeChanged)
Esempio n. 2
0
    def __init__(self, editorSession, dialog):
        super(FindReplaceNBT, self).__init__()
        self.editorSession = editorSession
        self.setupUi(self)
        self.dialog = dialog

        self.resultsWidget = FindReplaceNBTResults()
        self.resultsWidget.setupUi(self.resultsWidget)

        self.resultsDockWidget = MCEDockWidget("NBT Search",
                                               objectName="nbtSearch")
        self.resultsDockWidget.setWidget(self.resultsWidget)
        self.resultsDockWidget.hide()

        self.resultsModel = NBTResultsModel()
        self.resultsWidget.resultsView.setModel(self.resultsModel)
        self.resultsWidget.resultsView.clicked.connect(
            self.resultsViewIndexClicked)

        # --- Buttons ---
        self.findButton.clicked.connect(self.find)

        self.resultsWidget.stopButton.clicked.connect(self.stop)
        self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)

        self.resultsWidget.replaceSelectedButton.clicked.connect(
            self.replaceSelected)
        self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)

        self.resultsWidget.removeSelectedButton.clicked.connect(
            self.removeSelected)
        self.resultsWidget.removeAllButton.clicked.connect(self.removeAll)

        self.resultsWidget.replaceSelectedButton.setEnabled(
            not self.editorSession.readonly)
        self.resultsWidget.replaceAllButton.setEnabled(
            not self.editorSession.readonly)

        self.resultsWidget.removeSelectedButton.setEnabled(
            not self.editorSession.readonly)
        self.resultsWidget.removeAllButton.setEnabled(
            not self.editorSession.readonly)

        self.searchNameCheckbox.toggled.connect(self.searchForToggled)
        self.searchValueCheckbox.toggled.connect(self.searchForToggled)
        self.findTimer = None
        self.finder = None

        # --- Search for... ---
        self.nameField.setText(nbtReplaceSettings.nameField.value(""))
        self.searchNameCheckbox.setChecked(len(self.nameField.text()) > 0)
        self.nameField.textChanged.connect(self.nameFieldChanged)

        self.valueField.setText(nbtReplaceSettings.valueField.value(""))
        self.searchValueCheckbox.setChecked(len(self.valueField.text()) > 0)
        self.valueField.textChanged.connect(self.valueFieldChanged)

        # --- Search in... ---
        self.searchEntitiesCheckbox.setChecked(
            nbtReplaceSettings.searchEntitiesChecked.value())
        self.searchEntitiesCheckbox.toggled.connect(
            nbtReplaceSettings.searchEntitiesChecked.setValue)

        self.entityIDField.setText(nbtReplaceSettings.entityIDField.value())
        self.entityIDField.textChanged.connect(self.entityIDFieldChanged)

        self.searchTileEntitiesCheckbox.setChecked(
            nbtReplaceSettings.searchTileEntitiesChecked.value())
        self.searchTileEntitiesCheckbox.toggled.connect(
            nbtReplaceSettings.searchTileEntitiesChecked.setValue)

        self.tileEntityIDField.setText(
            nbtReplaceSettings.tileEntityIDField.value())
        self.tileEntityIDField.textChanged.connect(
            self.tileEntityIDFieldChanged)

        self.searchPlayersCheckbox.setChecked(
            nbtReplaceSettings.searchPlayersChecked.value())
        self.searchPlayersCheckbox.toggled.connect(
            nbtReplaceSettings.searchPlayersChecked.setValue)

        self.searchChunksCheckbox.setChecked(
            nbtReplaceSettings.searchChunksChecked.value())
        self.searchChunksCheckbox.toggled.connect(
            nbtReplaceSettings.searchChunksChecked.setValue)

        # --- Replace with... ---
        self.replaceNameField.setText(
            nbtReplaceSettings.replaceNameField.value())
        self.resultsWidget.replaceNameField.setText(
            nbtReplaceSettings.replaceNameField.value())

        self.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)
        self.resultsWidget.replaceNameField.textChanged.connect(
            self.replaceNameFieldChanged)

        self.replaceNameCheckbox.setChecked(len(self.replaceNameField.text()))
        self.resultsWidget.replaceNameCheckbox.setChecked(
            len(self.replaceNameField.text()))

        self.replaceNameCheckbox.toggled.connect(self.replaceNameToggled)
        self.resultsWidget.replaceNameCheckbox.toggled.connect(
            self.replaceNameToggled)

        self.replaceValueField.setText(
            nbtReplaceSettings.replaceValueField.value())
        self.resultsWidget.replaceValueField.setText(
            nbtReplaceSettings.replaceValueField.value())

        self.replaceValueField.textChanged.connect(
            self.replaceValueFieldChanged)
        self.resultsWidget.replaceValueField.textChanged.connect(
            self.replaceValueFieldChanged)

        self.replaceValueCheckbox.setChecked(len(
            self.replaceValueField.text()))
        self.resultsWidget.replaceValueCheckbox.setChecked(
            len(self.replaceValueField.text()))

        self.replaceValueCheckbox.toggled.connect(self.replaceValueToggled)
        self.resultsWidget.replaceValueCheckbox.toggled.connect(
            self.replaceValueToggled)

        self.replaceValueTagTypeComboBox.setCurrentIndex(
            nbtReplaceSettings.replaceValueTagType.value())
        self.replaceValueTagTypeComboBox.currentIndexChanged[int].connect(
            self.valueTagTypeChanged)
Esempio n. 3
0
class FindReplaceNBT(QtCore.QObject):
    def __init__(self, editorSession, dialog):
        super(FindReplaceNBT, self).__init__()
        self.editorSession = editorSession
        self.widget = load_ui("find_replace_nbt.ui")
        self.dialog = dialog

        self.resultsWidget = load_ui("find_replace_nbt_results.ui")
        self.resultsDockWidget = MCEDockWidget("NBT Search", objectName="nbtSearch")
        self.resultsDockWidget.setWidget(self.resultsWidget)
        self.resultsDockWidget.hide()

        self.resultsModel = NBTResultsModel()
        self.resultsWidget.resultsView.setModel(self.resultsModel)
        self.resultsWidget.resultsView.clicked.connect(self.resultsViewIndexClicked)

        # --- Buttons ---
        self.widget.findButton.clicked.connect(self.find)

        self.resultsWidget.stopButton.clicked.connect(self.stop)
        self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)

        self.resultsWidget.replaceSelectedButton.clicked.connect(self.replaceSelected)
        self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)

        self.widget.searchNameCheckbox.toggled.connect(self.searchForToggled)
        self.widget.searchValueCheckbox.toggled.connect(self.searchForToggled)
        self.findTimer = None
        self.finder = None

        # --- Search for... ---
        self.widget.nameField.setText(nbtReplaceSettings.nameField.value(""))
        self.widget.searchNameCheckbox.setChecked(len(self.widget.nameField.text()) > 0)
        self.widget.nameField.textChanged.connect(self.nameFieldChanged)

        self.widget.valueField.setText(nbtReplaceSettings.valueField.value(""))
        self.widget.searchValueCheckbox.setChecked(len(self.widget.valueField.text()) > 0)
        self.widget.valueField.textChanged.connect(self.valueFieldChanged)

        # --- Search in... ---
        self.widget.searchEntitiesCheckbox.setChecked(nbtReplaceSettings.searchEntitiesChecked.value())
        self.widget.searchEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchEntitiesChecked.setValue)

        self.widget.entityIDField.setText(nbtReplaceSettings.entityIDField.value())
        self.widget.entityIDField.textChanged.connect(self.entityIDFieldChanged)

        self.widget.searchTileEntitiesCheckbox.setChecked(nbtReplaceSettings.searchTileEntitiesChecked.value())
        self.widget.searchTileEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchTileEntitiesChecked.setValue)

        self.widget.tileEntityIDField.setText(nbtReplaceSettings.tileEntityIDField.value())
        self.widget.tileEntityIDField.textChanged.connect(self.tileEntityIDFieldChanged)

        # --- Replace with... ---
        self.widget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
        self.resultsWidget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
        self.widget.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)

        self.widget.replaceNameCheckbox.setChecked(len(self.widget.replaceNameField.text()))
        self.resultsWidget.replaceNameCheckbox.setChecked(len(self.widget.replaceNameField.text()))

        self.widget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
        self.resultsWidget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
        self.widget.replaceValueField.textChanged.connect(self.replaceValueFieldChanged)

        self.widget.replaceValueCheckbox.setChecked(len(self.widget.replaceValueField.text()))
        self.resultsWidget.replaceValueCheckbox.setChecked(len(self.widget.replaceValueField.text()))

        self.widget.replaceValueTagTypeComboBox.setCurrentIndex(nbtReplaceSettings.replaceValueTagType.value())
        self.widget.replaceValueTagTypeComboBox.currentIndexChanged[int].connect(self.valueTagTypeChanged)

    def dialogOpened(self):
        currentSelection = self.editorSession.currentSelection
        self.widget.inSelectionCheckbox.setChecked(currentSelection is not None and currentSelection.volume > 0)

    def searchForToggled(self):
        #canSearch = self.widget.searchNameCheckbox.isChecked() or self.widget.searchValueCheckbox.isChecked()
        #self.widget.findButton.setEnabled(canSearch)
        pass

    def nameFieldChanged(self, value):
        nbtReplaceSettings.nameField.setValue(value)
        self.widget.searchNameCheckbox.setChecked(len(value) > 0)

    def valueFieldChanged(self, value):
        nbtReplaceSettings.valueField.setValue(value)
        self.widget.searchValueCheckbox.setChecked(len(value) > 0)

    def entityIDFieldChanged(self, value):
        nbtReplaceSettings.entityIDField.setValue(value)
        if len(value):
            self.widget.searchEntitiesCheckbox.setChecked(True)

    def tileEntityIDFieldChanged(self, value):
        nbtReplaceSettings.tileEntityIDField.setValue(value)
        if len(value):
            self.widget.searchTileEntitiesCheckbox.setChecked(True)

    def replaceNameFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceNameField.value():
            nbtReplaceSettings.replaceNameField.setValue(value)

            self.widget.replaceNameCheckbox.setChecked(len(value) > 0)
            self.widget.replaceNameField.setText(value)

            self.resultsWidget.replaceNameCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceNameField.setText(value)

    def replaceValueFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceValueField.value():
            nbtReplaceSettings.replaceValueField.setValue(value)

            self.widget.replaceValueCheckbox.setChecked(len(value) > 0)
            self.widget.replaceValueField.setText(value)

            self.resultsWidget.replaceValueCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceValueField.setText(value)

    def valueTagTypeChanged(self, index):
        if index != nbtReplaceSettings.replaceValueTagType.value():
            nbtReplaceSettings.replaceValueTagType.setValue(index)
            self.widget.replaceValueTagTypeComboBox.setCurrentIndex(index)
            self.resultsWidget.replaceValueTagTypeComboBox.setCurrentIndex(index)

    def resultsViewIndexClicked(self, modelIndex):
        row = modelIndex.row()
        result = self.resultsModel.results[row]
        if result.resultType == result.EntityResult:
            entity = result.getEntity(self.editorSession.currentDimension)
            if entity is not None:
                self.editorSession.zoomAndInspectEntity(entity)  # xxxxxxx!!!
            else:
                log.error("Entity not found for result %s", str(result))
        if result.resultType == result.TileEntityResult:
            self.editorSession.zoomAndInspectBlock(result.position)

    def find(self):
        searchNames = self.widget.searchNameCheckbox.isChecked()
        targetName = self.widget.nameField.text()
        searchValues = self.widget.searchValueCheckbox.isChecked()
        targetValue = self.widget.valueField.text()

        searchEntities = self.widget.searchEntitiesCheckbox.isChecked()
        targetEntityIDs = self.widget.entityIDField.text()
        if len(targetEntityIDs):
            targetEntityIDs = targetEntityIDs.split(';')

        searchTileEntities = self.widget.searchTileEntitiesCheckbox.isChecked()
        targetTileEntityIDs = self.widget.tileEntityIDField.text()
        if len(targetTileEntityIDs):
            targetTileEntityIDs = targetTileEntityIDs.split(';')

        if not any((searchNames, searchValues, searchEntities, searchTileEntities)):
            # Nothing to find
            return

        dim = self.editorSession.currentDimension
        inSelection = self.widget.inSelectionCheckbox.isChecked()
        if inSelection:
            selection = self.editorSession.currentSelection
            if selection is None:
                return
        else:
            selection = dim.bounds

        def _matchTag(name_or_index, tag):
            if searchValues and not tag.isCompound() and not tag.isList():
                if tag.tagID == nbt.ID_STRING and targetValue in tag.value:
                    return True
                elif targetValue == tag.value:
                    return True
            if searchNames and isinstance(name_or_index, basestring) and targetName in name_or_index:
                return True
            return False

        def _findTag(name_or_index, tag, path):
            if _matchTag(name_or_index, tag):
                if tag.isCompound():
                    value = "Compound"  # describeCompound
                elif tag.isList():
                    value = "List"  # describeList
                else:
                    value = str(tag.value)

                return str(name_or_index), path, value

        def _findEntitiesInChunk(chunk):
            for entity in chunk.Entities:
                if entity.Position not in selection:
                    continue
                if len(targetEntityIDs) and entity.id not in targetEntityIDs:
                    continue

                try:
                    uuid = entity.UUID
                except KeyError:
                    uuid = None  # Don't want to use find/replace on entities without UUIDs

                if not searchNames and not searchValues:
                    # Finding entities only
                    self.resultsModel.addEntry(tagName="id",
                                               value=entity.id,
                                               id=entity.id,
                                               path=[],
                                               position=entity.Position,
                                               uuid=uuid,
                                               resultType=NBTResultsEntry.EntityResult)
                    continue

                tag = entity.raw_tag()

                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(tagName=name,
                                                   value=value,
                                                   id=entity.id,
                                                   path=path,
                                                   position=entity.Position,
                                                   uuid=uuid,
                                                   resultType=NBTResultsEntry.EntityResult)

        def _findTileEntitiesInChunk(chunk):
            for tileEntity in chunk.TileEntities:
                if tileEntity.Position not in selection:
                    continue
                if len(targetTileEntityIDs) and tileEntity.id not in targetTileEntityIDs:
                    continue

                if not searchNames and not searchValues:
                    # Finding tile entities only
                    self.resultsModel.addEntry(tagName="id",
                                               value=tileEntity.id,
                                               id=tileEntity.id,
                                               path=[],
                                               position=tileEntity.Position,
                                               uuid=None,
                                               resultType=NBTResultsEntry.TileEntityResult)
                    continue

                tag = tileEntity.raw_tag()
                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(tagName=name,
                                                   value=value,
                                                   id=tileEntity.id,
                                                   path=path,
                                                   position=tileEntity.Position,
                                                   uuid=None,
                                                   resultType=NBTResultsEntry.TileEntityResult)

        def _find():
            self.resultsDockWidget.show()
            self.resultsModel.clear()
            self.dialog.accept()
            self.resultsWidget.findAgainButton.setEnabled(False)

            self.resultsWidget.progressBar.setMaximum(selection.chunkCount-1)
            for i, cPos in enumerate(selection.chunkPositions()):
                if dim.containsChunk(*cPos):
                    chunk = dim.getChunk(*cPos)
                    if searchEntities:
                        _findEntitiesInChunk(chunk)
                    if searchTileEntities:
                        _findTileEntitiesInChunk(chunk)

                    yield
                self.resultsWidget.progressBar.setValue(i)

            self.stop()

        finder = _find()

        def find():
            try:
                finder.next()
            except StopIteration:
                pass

        self.findTimer = QtCore.QTimer(timeout=find, interval=1.0)
        self.findTimer.start()
        self.resultsWidget.stopButton.setEnabled(True)

    def stop(self):
        if self.findTimer:
            self.findTimer.stop()
        self.widget.findButton.setEnabled(True)
        self.resultsWidget.stopButton.setEnabled(False)
        self.resultsWidget.findAgainButton.setEnabled(True)

    def replaceEntries(self, entries):
        shouldReplaceName = self.widget.replaceNameCheckbox.isChecked()
        newName = self.widget.replaceNameField.text()
        shouldReplaceValue = self.widget.replaceValueCheckbox.isChecked()
        newValue = self.widget.replaceValueField.text()
        # newTagType = self.widget.replaceTagTypeComboBox.currentIndex()

        def _replaceInTag(result, tag):
            for component in result.path:
                tag = tag[component]

            if shouldReplaceName:
                subtag = tag.pop(result.tagName)
                tag[newName] = subtag
                result.setTagName(newName)

            if shouldReplaceValue:
                subtag = tag[result.tagName]
                # xxx newTagType
                if subtag.tagID in (nbt.ID_BYTE, nbt.ID_SHORT, nbt.ID_INT, nbt.ID_LONG):
                    try:
                        value = int(newValue)
                    except ValueError:
                        log.warn("Could not assign value %s to tag %s (could not convert to int)", newValue, subtag)
                        return
                elif subtag.tagID in (nbt.ID_FLOAT, nbt.ID_DOUBLE):
                    try:
                        value = float(newValue)
                    except ValueError:
                        log.warn("Could not assign value %s to tag %s (could not convert to float)", newValue, subtag)
                        return
                else:
                    value = newValue
                subtag.value = value

        def _replace():
            for result in entries:
                if result.resultType == result.TileEntityResult:
                    tileEntity = self.editorSession.currentDimension.getTileEntity(result.position)
                    if tileEntity:
                        tag = tileEntity.raw_tag()
                        _replaceInTag(result, tag)
                        tileEntity.dirty()

                if result.resultType == result.EntityResult:
                    entity = result.getEntity(self.editorSession.currentDimension)  # xxx put dimension in result!!!!
                    if entity:
                        tag = entity.raw_tag()
                        _replaceInTag(result, tag)
                        entity.dirty()

                # if result.resultType == result.ItemResult:  # xxx
                yield

        command = NBTReplaceCommand(self.editorSession, "Replace NBT data")  # xxx replace details
        with command.begin():
            replacer = _replace()
            showProgress("Replacing NBT data...", replacer)

        self.editorSession.pushCommand(command)

    def replaceAll(self):
        self.replaceEntries(self.resultsModel.results)

    def replaceSelected(self):
        pass
Esempio n. 4
0
    def __init__(self, argv):
        super(MCEditApp, self).__init__(argv)
        MCEditApp.app = self

        self.ensureSingle()

        self.commandLineWorlds = []
        self.parseArgs(argv)

        log.warn("UserFilesDirectory: %s", getUserFilesDirectory())

        # --- Necessities ---

        translator = QtCore.QTranslator()
        translator.load(resourcePath('mcedit2/i18n/en_US.ts'))
        self.installTranslator(translator)

        log.info("Loaded translator.")

        self.setOrganizationName("MCEdit")
        self.setOrganizationDomain("mcedit.net")
        self.setApplicationName("MCEdit")
        self.setWindowIcon(
            QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/mcediticon.png")))
        styleSheet = file(resourcePath("mcedit2/styles/mcedit2.qcss")).read()
        self.setStyleSheet(styleSheet)

        log.info("Loaded stylesheet.")

        # --- Main Window ---

        self.mainWindow = mainWindow = MCEditMainWindow()

        self.undoGroup = QtGui.QUndoGroup()

        self.tabWidget = self.mainWindow.tabWidget
        self.tabWidget.tabCloseRequested.connect(self.tabCloseRequested)
        self.tabWidget.currentChanged.connect(self.tabChanged)

        log.info("Loaded main window.")

        tttIcon = QtGui.QIcon(
            resourcePath("mcedit2/assets/mcedit2/icons/toolbar_text.png"))

        self.toggleToolbarTextAction = QtGui.QAction(tttIcon, "Toolbar Text",
                                                     self)

        self.toggleToolbarTextAction.setCheckable(True)
        self.toggleToolbarTextAction.setChecked(True)

        self.toggleToolbarTextAction.toggled.connect(self.toggleToolbarText)

        # --- OpenGL ---

        setDefaultFormat()

        # --- Sessions ---

        self._currentSession = None
        self.sessions = []
        self.sessionDockWidgets = []
        self.sessionChanged.connect(self.sessionDidChange)

        # --- Panel Widgets ---
        historyIcon = QtGui.QIcon(
            resourcePath("mcedit2/assets/mcedit2/icons/history.png"))

        self.undoView = QtGui.QUndoView(self.undoGroup)
        self.undoDockWidget = MCEDockWidget("History",
                                            mainWindow,
                                            objectName="HistoryWidget")
        self.undoDockWidget.setWidget(self.undoView)
        self.undoDockWidget.setWindowIcon(historyIcon)
        self.undoDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.undoDockWidget)
        undoToggleAction = self.undoDockWidget.toggleViewAction()
        undoToggleAction.setIcon(historyIcon)
        mainWindow.panelsToolBar.addAction(undoToggleAction)
        self.undoDockWidget.close()

        libraryIcon = QtGui.QIcon(
            resourcePath("mcedit2/assets/mcedit2/icons/library.png"))
        self.libraryWidget = LibraryWidget()
        self.libraryDockWidget = MCEDockWidget("Library",
                                               mainWindow,
                                               objectName="LibraryWidget")
        self.libraryDockWidget.setWidget(self.libraryWidget)
        self.libraryDockWidget.setWindowIcon(libraryIcon)
        self.libraryDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea,
                                 self.libraryDockWidget)

        libraryToggleAction = self.libraryDockWidget.toggleViewAction()
        libraryToggleAction.setIcon(libraryIcon)
        mainWindow.panelsToolBar.addAction(libraryToggleAction)
        self.libraryDockWidget.close()
        self.sessionChanged.connect(self.libraryWidget.sessionDidChange)

        self.libraryWidget.doubleClicked.connect(self.libraryItemDoubleClicked)

        self.globalPanels = [self.undoDockWidget, self.libraryDockWidget]

        log.info("Loaded panels.")

        # --- Debug Widgets ---

        self.debugMenu = self.createDebugMenu()

        self.debugObjectInspector = ObjectInspector(mainWindow)
        self.inspectorDockWidget = MCEDockWidget("Object Inspector",
                                                 mainWindow,
                                                 objectName="InspectorWidget")
        self.inspectorDockWidget.setWidget(self.debugObjectInspector)
        self.debugMenu.addAction(self.inspectorDockWidget.toggleViewAction())
        self.inspectorDockWidget.close()

        self.profileView = ProfilerWidget()
        self.profileDockWidget = MCEDockWidget("Profiler",
                                               mainWindow,
                                               objectName="ProfilerWidget")
        self.profileDockWidget.setWidget(self.profileView)
        self.debugMenu.addAction(self.profileDockWidget.toggleViewAction())
        self.profileDockWidget.close()

        self.textureAtlasView = QtGui.QLabel()
        self.textureAtlasView.setScaledContents(True)
        self.textureAtlasDockWidget = MCEDockWidget(
            "Texture Atlas", mainWindow, objectName="TextureAtlasWidget")

        self.textureAtlasArea = QtGui.QScrollArea()
        self.textureAtlasArea.setWidget(self.textureAtlasView)
        self.textureAtlasDockWidget.setWidget(self.textureAtlasArea)
        self.debugMenu.addAction(
            self.textureAtlasDockWidget.toggleViewAction())
        self.textureAtlasDockWidget.close()

        infoTabs = QtGui.QTabWidget()

        self.cursorInfo = WorldCursorInfo()
        infoTabs.addTab(self.cursorInfo, "Cursor")

        self.viewInfo = WorldViewInfo()
        infoTabs.addTab(self.viewInfo, "View")

        self.loaderInfo = ChunkLoaderInfo()
        infoTabs.addTab(self.loaderInfo, "Loader")

        self.infoDockWidget = MCEDockWidget("Debug Info",
                                            mainWindow,
                                            objectName="DebugInfo")
        self.infoDockWidget.setWidget(infoTabs)
        self.infoDockWidget.close()

        log.info("Loaded debug widgets.")

        # --- Menu Actions ---

        # -- MCEdit menu --
        mainWindow.actionNew_World.triggered.connect(self.createNewWorld)
        mainWindow.actionNew_World.setShortcut(QtGui.QKeySequence.New)

        mainWindow.actionOpen_World.triggered.connect(self.chooseOpenWorld)
        mainWindow.actionOpen_World.setShortcut(QtGui.QKeySequence.Open)

        mainWindow.actionShow_World_List.triggered.connect(self.showWorldList)
        mainWindow.actionShow_World_List.setShortcut(
            QtGui.QKeySequence("Ctrl+L"))

        mainWindow.actionSave_World.triggered.connect(self.saveCurrentWorld)
        mainWindow.actionSave_World.setShortcut(QtGui.QKeySequence.Save)

        mainWindow.actionSave_World_As.triggered.connect(
            self.saveCurrentWorldAs)
        mainWindow.actionSave_World_As.setShortcut(QtGui.QKeySequence.SaveAs)

        mainWindow.actionClose_World.triggered.connect(self.closeCurrentTab)
        mainWindow.actionClose_World.setShortcut(QtGui.QKeySequence.Close)

        mainWindow.actionExit_MCEdit.triggered.connect(self.exitEditor)
        mainWindow.actionExit_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Help menu --
        mainWindow.actionAbout_MCEdit.triggered.connect(self.showAbout)
        mainWindow.actionAbout_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Window Menu --
        mainWindow.menuWindow.addAction(self.undoDockWidget.toggleViewAction())
        mainWindow.menuWindow.addAction(
            self.libraryDockWidget.toggleViewAction())

        # -- Options Menu --
        mainWindow.actionEnable_Lighting_Updates.setChecked(
            EnableLightingSetting.value())
        mainWindow.actionEnable_Lighting_Updates.toggled.connect(
            EnableLightingSetting.setValue)

        EnableLightingSetting.valueChanged.connect(self.enableLightingChanged)
        self.enableLightingChanged(EnableLightingSetting.value())

        mainWindow.actionPreferences.triggered.connect(self.showPrefsDialog)
        mainWindow.actionConfigure_Blocks_Items.triggered.connect(
            self.showConfigureBlocksDialog)
        mainWindow.actionConfigure_Blocks_Items.setEnabled(False)
        mainWindow.actionPlugins.triggered.connect(self.showPluginsDialog)

        mainWindow.actionEnable_Developer_Mode.setChecked(
            DevModeSetting.value())
        mainWindow.actionEnable_Developer_Mode.toggled.connect(
            DevModeSetting.setValue)
        DevModeSetting.valueChanged.connect(self.toggleDeveloperMode)
        self.toggleDeveloperMode(DevModeSetting.value())

        log.info("Loaded menus.")

        # --- World List ---

        self.worldList = WorldListWidget(mainWindow)
        self.worldList.editWorldClicked.connect(self.editWorldFromList)
        self.worldList.viewWorldClicked.connect(self.viewWorldFromList)
        self.worldList.backupWorldClicked.connect(self.backupWorldFromList)
        self.worldList.repairWorldClicked.connect(self.repairWorldFromList)

        log.info("Loaded world list.")

        # --- Status Bar ---

        self.positionLabel = QtGui.QLabel("xx, yy, zz", minimumWidth=100)
        self.biomeLabel = QtGui.QLabel("Nowhere", minimumWidth=100)
        self.blocktypeLabel = QtGui.QLabel("(-1:-1)minecraft:rocktonium",
                                           minimumWidth=250)
        self.blockNameLabel = QtGui.QLabel("rocktonium", minimumWidth=150)
        self.cpsLabel = QtGui.QLabel("-1 cps", minimumWidth=65)
        self.fpsLabel = QtGui.QLabel("-1 fps", minimumWidth=65)

        statusBar = mainWindow.statusBar()
        statusBar.addPermanentWidget(self.positionLabel)
        statusBar.addPermanentWidget(self.biomeLabel)
        statusBar.addPermanentWidget(self.blocktypeLabel)
        statusBar.addPermanentWidget(self.blockNameLabel)
        statusBar.addPermanentWidget(self.cpsLabel)
        statusBar.addPermanentWidget(self.fpsLabel)

        log.info("Loaded status bar.")

        # --- Load settings ---

        mainWindow.loadSettings()
        self.updateRecentFilesMenu()

        log.info("Loaded settings.")

        # --- App Dialogs ---

        # Qt weirdness - initializing QDialog with parent puts the dialog at 0,
        # 0 instead of centering it on the parent. Have to set the parent explicitly
        # and put the Qt.Dialog flag back on since changing the parent resets the
        # window flags...

        self.prefsDialog = prefsdialog.PrefsDialog(None)
        self.prefsDialog.setParent(mainWindow)
        self.prefsDialog.setWindowFlags(Qt.Dialog)

        self.configureBlocksDialog = configure_blocks.ConfigureBlocksDialog(
            None)
        self.configureBlocksDialog.finished.connect(
            self.configureBlocksFinished)
        self.configureBlocksDialog.setParent(mainWindow)
        self.configureBlocksDialog.setWindowFlags(Qt.Dialog)

        self.pluginsDialog = PluginsDialog()
        self.pluginsDialog.setParent(mainWindow)
        self.pluginsDialog.setWindowFlags(Qt.Dialog)

        log.info("Loaded app dialogs.")

        # --- Loader timer ---

        self.loadTimer = timer = LoaderTimer(self)
        timer.setInterval(0)
        timer.timeout.connect(self.loadTimerFired)
        timer.start()
        log.info("Loading timer started")

        mainWindow.showMaximized()
Esempio n. 5
0
class FindReplaceNBT(QtGui.QWidget, Ui_findNBTWidget):
    def __init__(self, editorSession, dialog):
        super(FindReplaceNBT, self).__init__()
        self.editorSession = editorSession
        self.setupUi(self)
        self.dialog = dialog

        self.resultsWidget = FindReplaceNBTResults()
        self.resultsWidget.setupUi(self.resultsWidget)

        self.resultsDockWidget = MCEDockWidget("NBT Search",
                                               objectName="nbtSearch")
        self.resultsDockWidget.setWidget(self.resultsWidget)
        self.resultsDockWidget.hide()

        self.resultsModel = NBTResultsModel()
        self.resultsWidget.resultsView.setModel(self.resultsModel)
        self.resultsWidget.resultsView.clicked.connect(
            self.resultsViewIndexClicked)

        # --- Buttons ---
        self.findButton.clicked.connect(self.find)

        self.resultsWidget.stopButton.clicked.connect(self.stop)
        self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)

        self.resultsWidget.replaceSelectedButton.clicked.connect(
            self.replaceSelected)
        self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)

        self.resultsWidget.removeSelectedButton.clicked.connect(
            self.removeSelected)
        self.resultsWidget.removeAllButton.clicked.connect(self.removeAll)

        self.resultsWidget.replaceSelectedButton.setEnabled(
            not self.editorSession.readonly)
        self.resultsWidget.replaceAllButton.setEnabled(
            not self.editorSession.readonly)

        self.resultsWidget.removeSelectedButton.setEnabled(
            not self.editorSession.readonly)
        self.resultsWidget.removeAllButton.setEnabled(
            not self.editorSession.readonly)

        self.searchNameCheckbox.toggled.connect(self.searchForToggled)
        self.searchValueCheckbox.toggled.connect(self.searchForToggled)
        self.findTimer = None
        self.finder = None

        # --- Search for... ---
        self.nameField.setText(nbtReplaceSettings.nameField.value(""))
        self.searchNameCheckbox.setChecked(len(self.nameField.text()) > 0)
        self.nameField.textChanged.connect(self.nameFieldChanged)

        self.valueField.setText(nbtReplaceSettings.valueField.value(""))
        self.searchValueCheckbox.setChecked(len(self.valueField.text()) > 0)
        self.valueField.textChanged.connect(self.valueFieldChanged)

        # --- Search in... ---
        self.searchEntitiesCheckbox.setChecked(
            nbtReplaceSettings.searchEntitiesChecked.value())
        self.searchEntitiesCheckbox.toggled.connect(
            nbtReplaceSettings.searchEntitiesChecked.setValue)

        self.entityIDField.setText(nbtReplaceSettings.entityIDField.value())
        self.entityIDField.textChanged.connect(self.entityIDFieldChanged)

        self.searchTileEntitiesCheckbox.setChecked(
            nbtReplaceSettings.searchTileEntitiesChecked.value())
        self.searchTileEntitiesCheckbox.toggled.connect(
            nbtReplaceSettings.searchTileEntitiesChecked.setValue)

        self.tileEntityIDField.setText(
            nbtReplaceSettings.tileEntityIDField.value())
        self.tileEntityIDField.textChanged.connect(
            self.tileEntityIDFieldChanged)

        self.searchPlayersCheckbox.setChecked(
            nbtReplaceSettings.searchPlayersChecked.value())
        self.searchPlayersCheckbox.toggled.connect(
            nbtReplaceSettings.searchPlayersChecked.setValue)

        self.searchChunksCheckbox.setChecked(
            nbtReplaceSettings.searchChunksChecked.value())
        self.searchChunksCheckbox.toggled.connect(
            nbtReplaceSettings.searchChunksChecked.setValue)

        # --- Replace with... ---
        self.replaceNameField.setText(
            nbtReplaceSettings.replaceNameField.value())
        self.resultsWidget.replaceNameField.setText(
            nbtReplaceSettings.replaceNameField.value())

        self.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)
        self.resultsWidget.replaceNameField.textChanged.connect(
            self.replaceNameFieldChanged)

        self.replaceNameCheckbox.setChecked(len(self.replaceNameField.text()))
        self.resultsWidget.replaceNameCheckbox.setChecked(
            len(self.replaceNameField.text()))

        self.replaceNameCheckbox.toggled.connect(self.replaceNameToggled)
        self.resultsWidget.replaceNameCheckbox.toggled.connect(
            self.replaceNameToggled)

        self.replaceValueField.setText(
            nbtReplaceSettings.replaceValueField.value())
        self.resultsWidget.replaceValueField.setText(
            nbtReplaceSettings.replaceValueField.value())

        self.replaceValueField.textChanged.connect(
            self.replaceValueFieldChanged)
        self.resultsWidget.replaceValueField.textChanged.connect(
            self.replaceValueFieldChanged)

        self.replaceValueCheckbox.setChecked(len(
            self.replaceValueField.text()))
        self.resultsWidget.replaceValueCheckbox.setChecked(
            len(self.replaceValueField.text()))

        self.replaceValueCheckbox.toggled.connect(self.replaceValueToggled)
        self.resultsWidget.replaceValueCheckbox.toggled.connect(
            self.replaceValueToggled)

        self.replaceValueTagTypeComboBox.setCurrentIndex(
            nbtReplaceSettings.replaceValueTagType.value())
        self.replaceValueTagTypeComboBox.currentIndexChanged[int].connect(
            self.valueTagTypeChanged)

    def dialogOpened(self):
        currentSelection = self.editorSession.currentSelection
        self.inSelectionCheckbox.setChecked(currentSelection is not None
                                            and currentSelection.volume > 0)

    def searchForToggled(self):
        #canSearch = self.searchNameCheckbox.isChecked() or self.searchValueCheckbox.isChecked()
        #self.findButton.setEnabled(canSearch)
        pass

    def nameFieldChanged(self, value):
        nbtReplaceSettings.nameField.setValue(value)
        self.searchNameCheckbox.setChecked(len(value) > 0)

    def valueFieldChanged(self, value):
        nbtReplaceSettings.valueField.setValue(value)
        self.searchValueCheckbox.setChecked(len(value) > 0)

    def entityIDFieldChanged(self, value):
        nbtReplaceSettings.entityIDField.setValue(value)
        if len(value):
            self.searchEntitiesCheckbox.setChecked(True)

    def tileEntityIDFieldChanged(self, value):
        nbtReplaceSettings.tileEntityIDField.setValue(value)
        if len(value):
            self.searchTileEntitiesCheckbox.setChecked(True)

    def replaceNameFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceNameField.value():
            nbtReplaceSettings.replaceNameField.setValue(value)

            self.resultsWidget.replaceNameCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceNameField.setText(value)

            self.replaceNameCheckbox.setChecked(len(value) > 0)
            self.replaceNameField.setText(value)

    def replaceValueFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceValueField.value():
            nbtReplaceSettings.replaceValueField.setValue(value)

            self.resultsWidget.replaceValueCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceValueField.setText(value)

            self.replaceValueCheckbox.setChecked(len(value) > 0)
            self.replaceValueField.setText(value)

    def replaceNameToggled(self, enabled):
        if enabled != nbtReplaceSettings.replaceNameEnabled.Name():
            nbtReplaceSettings.replaceNameEnabled.setName(enabled)

            self.resultsWidget.replaceNameCheckbox.setChecked(enabled)
            self.replaceNameCheckbox.setChecked(enabled)

    def replaceValueToggled(self, enabled):
        if enabled != nbtReplaceSettings.replaceValueEnabled.value():
            nbtReplaceSettings.replaceValueEnabled.setValue(enabled)

            self.resultsWidget.replaceValueCheckbox.setChecked(enabled)
            self.replaceValueCheckbox.setChecked(enabled)

    def valueTagTypeChanged(self, index):
        if index != nbtReplaceSettings.replaceValueTagType.value():
            nbtReplaceSettings.replaceValueTagType.setValue(index)
            self.replaceValueTagTypeComboBox.setCurrentIndex(index)
            self.replaceValueTagTypeComboBox.setCurrentIndex(index)

    def resultsViewIndexClicked(self, modelIndex):
        row = modelIndex.row()
        result = self.resultsModel.results[row]

        if result.resultType == result.EntityResult:
            entityPtr = result.getEntityPtr()
            self.editorSession.zoomAndInspectEntity(entityPtr)

        if result.resultType == result.TileEntityResult:
            self.editorSession.zoomAndInspectBlock(result.position)

        if result.resultType == result.ChunkResult:
            self.editorSession.zoomAndInspectChunk(result.position)

    def find(self):
        searchNames = self.searchNameCheckbox.isChecked()
        targetName = self.nameField.text()
        searchValues = self.searchValueCheckbox.isChecked()
        targetValue = self.valueField.text()

        searchEntities = self.searchEntitiesCheckbox.isChecked()
        targetEntityIDs = self.entityIDField.text()
        if len(targetEntityIDs):
            targetEntityIDs = targetEntityIDs.split(';')

        searchTileEntities = self.searchTileEntitiesCheckbox.isChecked()
        targetTileEntityIDs = self.tileEntityIDField.text()
        if len(targetTileEntityIDs):
            targetTileEntityIDs = targetTileEntityIDs.split(';')

        searchChunks = self.searchChunksCheckbox.isChecked()

        if not any((searchNames, searchValues, searchEntities,
                    searchTileEntities, searchChunks)):
            # Nothing to find
            return

        dim = self.editorSession.currentDimension
        inSelection = self.inSelectionCheckbox.isChecked()
        if inSelection:
            selection = self.editorSession.currentSelection
            if selection is None:
                return
        else:
            selection = dim.bounds

        def _matchTag(name_or_index, tag):
            ok = True
            if searchValues and not tag.isCompound() and not tag.isList():
                if not (tag.tagID == nbt.ID_STRING
                        and targetValue in tag.value):
                    ok = False
                elif not (targetValue == tag.value):
                    ok = False
            if searchNames and isinstance(
                    name_or_index,
                    basestring) and not (targetName == name_or_index):
                ok = False
            return ok

        def _findTag(name_or_index, tag, path):
            if _matchTag(name_or_index, tag):
                if tag.isCompound():
                    value = "Compound"  # describeCompound
                elif tag.isList():
                    value = "List"  # describeList
                else:
                    value = unicode(tag.value)

                return str(name_or_index), path, value

        def _findEntitiesInChunk(chunk):
            for entity in chunk.Entities:
                if entity.Position not in selection:
                    continue
                if len(targetEntityIDs) and entity.id not in targetEntityIDs:
                    continue

                try:
                    uuid = entity.UUID
                except KeyError:
                    uuid = None  # Don't want to use find/replace on entities without UUIDs

                if not searchNames and not searchValues:
                    # Finding entities only
                    self.resultsModel.addEntry(
                        tagName="id",
                        value=entity.id,
                        ID=entity.id,
                        path=[],
                        position=entity.Position,
                        uuid=uuid,
                        resultType=NBTResultsEntry.EntityResult,
                        dimension=self.editorSession.currentDimension)
                    continue

                tag = entity.raw_tag()

                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(
                            tagName=name,
                            value=value,
                            ID=entity.id,
                            path=path,
                            position=entity.Position,
                            uuid=uuid,
                            resultType=NBTResultsEntry.EntityResult,
                            dimension=self.editorSession.currentDimension)

        def _findTileEntitiesInChunk(chunk):
            for tileEntity in chunk.TileEntities:
                if tileEntity.Position not in selection:
                    continue
                if len(targetTileEntityIDs
                       ) and tileEntity.id not in targetTileEntityIDs:
                    continue

                if not searchNames and not searchValues:
                    # Finding tile entities only
                    self.resultsModel.addEntry(
                        tagName="id",
                        value=tileEntity.id,
                        ID=tileEntity.id,
                        path=[],
                        position=tileEntity.Position,
                        uuid=None,
                        resultType=NBTResultsEntry.TileEntityResult,
                        dimension=self.editorSession.currentDimension)
                    continue

                tag = tileEntity.raw_tag()
                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(
                            tagName=name,
                            value=value,
                            ID=tileEntity.id,
                            path=path,
                            position=tileEntity.Position,
                            uuid=None,
                            resultType=NBTResultsEntry.TileEntityResult,
                            dimension=self.editorSession.currentDimension)

        def _findInChunk(chunk):
            tag = chunk.rootTag
            for name, subtag, path in nbt.walk(tag):
                if len(path) >= 2 and path[0] == "Level":
                    if path[1] in ("Entities", "TileEntities"):
                        continue

                result = _findTag(name, subtag, path)

                if result:
                    name, path, value = result

                    self.resultsModel.addEntry(
                        tagName=name,
                        value=value,
                        ID="chunk",
                        path=path,
                        position=chunk.chunkPosition,
                        uuid=None,
                        resultType=NBTResultsEntry.ChunkResult,
                        dimension=self.editorSession.currentDimension)

        def _find():
            self.resultsDockWidget.show()
            self.resultsModel.clear()
            self.dialog.accept()
            self.resultsWidget.findAgainButton.setEnabled(False)

            self.resultsWidget.progressBar.setMaximum(selection.chunkCount - 1)
            for i, cPos in enumerate(selection.chunkPositions()):
                if dim.containsChunk(*cPos):
                    chunk = dim.getChunk(*cPos)
                    if searchEntities:
                        _findEntitiesInChunk(chunk)
                    if searchTileEntities:
                        _findTileEntitiesInChunk(chunk)
                    if searchChunks:
                        _findInChunk(chunk)

                    yield
                self.resultsWidget.progressBar.setValue(i)

            self.stop()

        finder = _find()

        def find():
            try:
                finder.next()
            except StopIteration:
                pass

        self.findTimer = QtCore.QTimer(timeout=find, interval=1.0)
        self.findTimer.start()
        self.resultsWidget.stopButton.setEnabled(True)

    def stop(self):
        if self.findTimer:
            self.findTimer.stop()
        self.findButton.setEnabled(True)
        self.resultsWidget.stopButton.setEnabled(False)
        self.resultsWidget.findAgainButton.setEnabled(True)

    def replaceEntries(self, entries):
        shouldReplaceName = nbtReplaceSettings.replaceNameEnabled.value()
        newName = nbtReplaceSettings.replaceNameField.value()
        shouldReplaceValue = nbtReplaceSettings.replaceValueEnabled.value()
        newValue = nbtReplaceSettings.replaceValueField.value()

        # newTagType = self.replaceTagTypeComboBox.currentIndex()

        def _replaceInTag(result, tag):
            for component in result.path:
                tag = tag[component]

            if shouldReplaceName:
                subtag = tag.pop(result.tagName)
                tag[newName] = subtag
                result.setTagName(newName)

            if shouldReplaceValue:
                subtag = tag[result.tagName]
                # xxx newTagType
                if subtag.tagID in (nbt.ID_BYTE, nbt.ID_SHORT, nbt.ID_INT,
                                    nbt.ID_LONG):
                    try:
                        value = int(newValue)
                    except ValueError:
                        log.warn(
                            "Could not assign value %s to tag %s (could not convert to int)",
                            newValue, subtag)
                        return
                elif subtag.tagID in (nbt.ID_FLOAT, nbt.ID_DOUBLE):
                    try:
                        value = float(newValue)
                    except ValueError:
                        log.warn(
                            "Could not assign value %s to tag %s (could not convert to float)",
                            newValue, subtag)
                        return
                else:
                    value = newValue
                subtag.value = value
                result.value = value

        def _replace():
            for result in entries:
                ref = result.getTargetRef()

                tag = result.getTargetTag()
                _replaceInTag(result, tag)
                ref.dirty = True

                yield

        with self.editorSession.beginSimpleCommand(
                self.tr("Replace NBT data")):
            showProgress("Replacing NBT data...", _replace())

    def replaceAll(self):
        self.replaceEntries(self.resultsModel.results)

    def replaceSelected(self):
        entries = []
        for index in self.resultsWidget.resultsView.selectedIndexes():
            entries.append(self.resultsModel.data(index, role=Qt.UserRole))

        self.replaceEntries(entries)

    def removeEntries(self, entries):
        def _remove():
            for result in entries:
                ref = result.getTargetRef()
                tag = result.getTargetTag()

                for component in result.path[:-1]:
                    tag = tag[component]

                del tag[result.tagName]
                ref.dirty = True

                yield

            self.resultsModel.removeEntries(entries)

        with self.editorSession.beginSimpleCommand(self.tr("Remove NBT tags")):
            showProgress("Removing NBT tags...", _remove())

    def removeAll(self):
        self.removeEntries(self.resultsModel.results)

    def removeSelected(self):
        entries = []
        for index in self.resultsWidget.resultsView.selectedIndexes():
            entries.append(self.resultsModel.data(index, role=Qt.UserRole))

        self.removeEntries(entries)
Esempio n. 6
0
class MCEditApp(QtGui.QApplication):
    def __init__(self, argv):
        super(MCEditApp, self).__init__(argv)
        MCEditApp.app = self

        self.ensureSingle()

        self.commandLineWorlds = []
        self.parseArgs(argv)

        log.warn("UserFilesDirectory: %s", getUserFilesDirectory())

        # --- Necessities ---

        translator = QtCore.QTranslator()
        translator.load(resourcePath('mcedit2/i18n/en_US.ts'))
        self.installTranslator(translator)

        log.info("Loaded translator.")

        self.setOrganizationName("MCEdit")
        self.setOrganizationDomain("mcedit.net")
        self.setApplicationName("MCEdit")
        self.setWindowIcon(
            QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/mcediticon.png")))
        styleSheet = file(resourcePath("mcedit2/styles/mcedit2.qcss")).read()
        self.setStyleSheet(styleSheet)

        log.info("Loaded stylesheet.")

        # --- Main Window ---

        self.mainWindow = mainWindow = MCEditMainWindow()

        self.undoGroup = QtGui.QUndoGroup()

        self.tabWidget = self.mainWindow.tabWidget
        self.tabWidget.tabCloseRequested.connect(self.tabCloseRequested)
        self.tabWidget.currentChanged.connect(self.tabChanged)

        log.info("Loaded main window.")

        tttIcon = QtGui.QIcon(
            resourcePath("mcedit2/assets/mcedit2/icons/toolbar_text.png"))

        self.toggleToolbarTextAction = QtGui.QAction(tttIcon, "Toolbar Text",
                                                     self)

        self.toggleToolbarTextAction.setCheckable(True)
        self.toggleToolbarTextAction.setChecked(True)

        self.toggleToolbarTextAction.toggled.connect(self.toggleToolbarText)

        # --- OpenGL ---

        setDefaultFormat()

        # --- Sessions ---

        self._currentSession = None
        self.sessions = []
        self.sessionDockWidgets = []
        self.sessionChanged.connect(self.sessionDidChange)

        # --- Panel Widgets ---
        historyIcon = QtGui.QIcon(
            resourcePath("mcedit2/assets/mcedit2/icons/history.png"))

        self.undoView = QtGui.QUndoView(self.undoGroup)
        self.undoDockWidget = MCEDockWidget("History",
                                            mainWindow,
                                            objectName="HistoryWidget")
        self.undoDockWidget.setWidget(self.undoView)
        self.undoDockWidget.setWindowIcon(historyIcon)
        self.undoDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.undoDockWidget)
        undoToggleAction = self.undoDockWidget.toggleViewAction()
        undoToggleAction.setIcon(historyIcon)
        mainWindow.panelsToolBar.addAction(undoToggleAction)
        self.undoDockWidget.close()

        libraryIcon = QtGui.QIcon(
            resourcePath("mcedit2/assets/mcedit2/icons/library.png"))
        self.libraryWidget = LibraryWidget()
        self.libraryDockWidget = MCEDockWidget("Library",
                                               mainWindow,
                                               objectName="LibraryWidget")
        self.libraryDockWidget.setWidget(self.libraryWidget)
        self.libraryDockWidget.setWindowIcon(libraryIcon)
        self.libraryDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea,
                                 self.libraryDockWidget)

        libraryToggleAction = self.libraryDockWidget.toggleViewAction()
        libraryToggleAction.setIcon(libraryIcon)
        mainWindow.panelsToolBar.addAction(libraryToggleAction)
        self.libraryDockWidget.close()
        self.sessionChanged.connect(self.libraryWidget.sessionDidChange)

        self.libraryWidget.doubleClicked.connect(self.libraryItemDoubleClicked)

        self.globalPanels = [self.undoDockWidget, self.libraryDockWidget]

        log.info("Loaded panels.")

        # --- Debug Widgets ---

        self.debugMenu = self.createDebugMenu()

        self.debugObjectInspector = ObjectInspector(mainWindow)
        self.inspectorDockWidget = MCEDockWidget("Object Inspector",
                                                 mainWindow,
                                                 objectName="InspectorWidget")
        self.inspectorDockWidget.setWidget(self.debugObjectInspector)
        self.debugMenu.addAction(self.inspectorDockWidget.toggleViewAction())
        self.inspectorDockWidget.close()

        self.profileView = ProfilerWidget()
        self.profileDockWidget = MCEDockWidget("Profiler",
                                               mainWindow,
                                               objectName="ProfilerWidget")
        self.profileDockWidget.setWidget(self.profileView)
        self.debugMenu.addAction(self.profileDockWidget.toggleViewAction())
        self.profileDockWidget.close()

        self.textureAtlasView = QtGui.QLabel()
        self.textureAtlasView.setScaledContents(True)
        self.textureAtlasDockWidget = MCEDockWidget(
            "Texture Atlas", mainWindow, objectName="TextureAtlasWidget")

        self.textureAtlasArea = QtGui.QScrollArea()
        self.textureAtlasArea.setWidget(self.textureAtlasView)
        self.textureAtlasDockWidget.setWidget(self.textureAtlasArea)
        self.debugMenu.addAction(
            self.textureAtlasDockWidget.toggleViewAction())
        self.textureAtlasDockWidget.close()

        infoTabs = QtGui.QTabWidget()

        self.cursorInfo = WorldCursorInfo()
        infoTabs.addTab(self.cursorInfo, "Cursor")

        self.viewInfo = WorldViewInfo()
        infoTabs.addTab(self.viewInfo, "View")

        self.loaderInfo = ChunkLoaderInfo()
        infoTabs.addTab(self.loaderInfo, "Loader")

        self.infoDockWidget = MCEDockWidget("Debug Info",
                                            mainWindow,
                                            objectName="DebugInfo")
        self.infoDockWidget.setWidget(infoTabs)
        self.infoDockWidget.close()

        log.info("Loaded debug widgets.")

        # --- Menu Actions ---

        # -- MCEdit menu --
        mainWindow.actionNew_World.triggered.connect(self.createNewWorld)
        mainWindow.actionNew_World.setShortcut(QtGui.QKeySequence.New)

        mainWindow.actionOpen_World.triggered.connect(self.chooseOpenWorld)
        mainWindow.actionOpen_World.setShortcut(QtGui.QKeySequence.Open)

        mainWindow.actionShow_World_List.triggered.connect(self.showWorldList)
        mainWindow.actionShow_World_List.setShortcut(
            QtGui.QKeySequence("Ctrl+L"))

        mainWindow.actionSave_World.triggered.connect(self.saveCurrentWorld)
        mainWindow.actionSave_World.setShortcut(QtGui.QKeySequence.Save)

        mainWindow.actionSave_World_As.triggered.connect(
            self.saveCurrentWorldAs)
        mainWindow.actionSave_World_As.setShortcut(QtGui.QKeySequence.SaveAs)

        mainWindow.actionClose_World.triggered.connect(self.closeCurrentTab)
        mainWindow.actionClose_World.setShortcut(QtGui.QKeySequence.Close)

        mainWindow.actionExit_MCEdit.triggered.connect(self.exitEditor)
        mainWindow.actionExit_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Help menu --
        mainWindow.actionAbout_MCEdit.triggered.connect(self.showAbout)
        mainWindow.actionAbout_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Window Menu --
        mainWindow.menuWindow.addAction(self.undoDockWidget.toggleViewAction())
        mainWindow.menuWindow.addAction(
            self.libraryDockWidget.toggleViewAction())

        # -- Options Menu --
        mainWindow.actionEnable_Lighting_Updates.setChecked(
            EnableLightingSetting.value())
        mainWindow.actionEnable_Lighting_Updates.toggled.connect(
            EnableLightingSetting.setValue)

        EnableLightingSetting.valueChanged.connect(self.enableLightingChanged)
        self.enableLightingChanged(EnableLightingSetting.value())

        mainWindow.actionPreferences.triggered.connect(self.showPrefsDialog)
        mainWindow.actionConfigure_Blocks_Items.triggered.connect(
            self.showConfigureBlocksDialog)
        mainWindow.actionConfigure_Blocks_Items.setEnabled(False)
        mainWindow.actionPlugins.triggered.connect(self.showPluginsDialog)

        mainWindow.actionEnable_Developer_Mode.setChecked(
            DevModeSetting.value())
        mainWindow.actionEnable_Developer_Mode.toggled.connect(
            DevModeSetting.setValue)
        DevModeSetting.valueChanged.connect(self.toggleDeveloperMode)
        self.toggleDeveloperMode(DevModeSetting.value())

        log.info("Loaded menus.")

        # --- World List ---

        self.worldList = WorldListWidget(mainWindow)
        self.worldList.editWorldClicked.connect(self.editWorldFromList)
        self.worldList.viewWorldClicked.connect(self.viewWorldFromList)
        self.worldList.backupWorldClicked.connect(self.backupWorldFromList)
        self.worldList.repairWorldClicked.connect(self.repairWorldFromList)

        log.info("Loaded world list.")

        # --- Status Bar ---

        self.positionLabel = QtGui.QLabel("xx, yy, zz", minimumWidth=100)
        self.biomeLabel = QtGui.QLabel("Nowhere", minimumWidth=100)
        self.blocktypeLabel = QtGui.QLabel("(-1:-1)minecraft:rocktonium",
                                           minimumWidth=250)
        self.blockNameLabel = QtGui.QLabel("rocktonium", minimumWidth=150)
        self.cpsLabel = QtGui.QLabel("-1 cps", minimumWidth=65)
        self.fpsLabel = QtGui.QLabel("-1 fps", minimumWidth=65)

        statusBar = mainWindow.statusBar()
        statusBar.addPermanentWidget(self.positionLabel)
        statusBar.addPermanentWidget(self.biomeLabel)
        statusBar.addPermanentWidget(self.blocktypeLabel)
        statusBar.addPermanentWidget(self.blockNameLabel)
        statusBar.addPermanentWidget(self.cpsLabel)
        statusBar.addPermanentWidget(self.fpsLabel)

        log.info("Loaded status bar.")

        # --- Load settings ---

        mainWindow.loadSettings()
        self.updateRecentFilesMenu()

        log.info("Loaded settings.")

        # --- App Dialogs ---

        # Qt weirdness - initializing QDialog with parent puts the dialog at 0,
        # 0 instead of centering it on the parent. Have to set the parent explicitly
        # and put the Qt.Dialog flag back on since changing the parent resets the
        # window flags...

        self.prefsDialog = prefsdialog.PrefsDialog(None)
        self.prefsDialog.setParent(mainWindow)
        self.prefsDialog.setWindowFlags(Qt.Dialog)

        self.configureBlocksDialog = configure_blocks.ConfigureBlocksDialog(
            None)
        self.configureBlocksDialog.finished.connect(
            self.configureBlocksFinished)
        self.configureBlocksDialog.setParent(mainWindow)
        self.configureBlocksDialog.setWindowFlags(Qt.Dialog)

        self.pluginsDialog = PluginsDialog()
        self.pluginsDialog.setParent(mainWindow)
        self.pluginsDialog.setWindowFlags(Qt.Dialog)

        log.info("Loaded app dialogs.")

        # --- Loader timer ---

        self.loadTimer = timer = LoaderTimer(self)
        timer.setInterval(0)
        timer.timeout.connect(self.loadTimerFired)
        timer.start()
        log.info("Loading timer started")

        mainWindow.showMaximized()

    # --- Startup code ---

    def startup(self):

        minecraftinstall.GetInstalls().ensureValidInstall()

        log.info("Finding plugins")

        if getattr(sys, 'frozen', False):
            # frozen - load from app dir
            pluginsDir = getUserPluginsDirectory()
            plugins.findNewPluginsInDir(pluginsDir)
        else:
            # not frozen - load from src/plugins
            # from editorapp.py, ../../plugins
            devPluginsDir = os.path.join(
                os.path.dirname(os.path.dirname(__file__)), "plugins")
            plugins.findNewPluginsInDir(devPluginsDir)

        for pluginRef in plugins.getAllPlugins():
            if pluginRef.enabled:
                if not pluginRef.load():
                    showErrorDialog(
                        "%s while loading plugin \"%s\"" %
                        (pluginRef.loadError[0].__name__,
                         pluginRef.displayName), pluginRef.loadError, False)

        log.info("Opening worlds from command line.")

        if len(self.commandLineWorlds):
            for filename in self.commandLineWorlds:
                self.loadFile(filename, self.args.view)
        else:
            self.showWorldList()

        if len(self.sessions) and self.args.eval:
            session = self.sessions[-1]
            eval_globals = {"session": session, "self": self}
            exec(self.args.eval, eval_globals)

    pluginsChanged = QtCore.Signal()

    consoleWidget = None

    def createDebugMenu(self):
        debugMenu = QtGui.QMenu(self.tr("&Debug"))

        def raiseError():
            ret = QtGui.QMessageBox.warning(
                self.mainWindow,
                self.tr("Raise Error"),
                self.tr("Raise an error? This may crash MCEdit."),
                buttons=QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
            if ret == QtGui.QMessageBox.Yes:
                raise ValueError("User requested error")

        debugMenu.addAction(
            self.tr("Raise Error")).triggered.connect(raiseError)

        def showConsole():
            if self.consoleWidget is None:
                self.consoleWidget = terminal_widget(sessions=self.sessions)
            self.consoleWidget.show()

        debugMenu.addAction(
            self.tr("IPython Console")).triggered.connect(showConsole)

        objGraph = ObjGraphWidget()

        def showObjGraph():
            objGraph.show()

        debugMenu.addAction(
            self.tr("ObjGraph")).triggered.connect(showObjGraph)

        def showHeapy():
            from guppy import hpy
            h = hpy()
            print(h.heap())

        debugMenu.addAction(
            self.tr("Heap Trace (slow)")).triggered.connect(showHeapy)

        debugMenu.addAction(self.tr("Collect Garbage")).triggered.connect(
            gc.collect)

        return debugMenu

    def ensureSingle(self):
        serverName = "MCEdit.Application"
        socket = QtNetwork.QLocalSocket()
        socket.connectToServer(serverName)
        if socket.waitForConnected(500):
            # TODO: get filenames from argv and pass to running app
            log.error("%s already running", serverName)
            raise SystemExit  # Already running

        def newConnection():
            newSocket = server.nextPendingConnection()
            # TODO: read filenames from socket
            newSocket.close()
            self.mainWindow.activateWindow()
            self.mainWindow.raise_()

        server = QtNetwork.QLocalServer(newConnection=newConnection)
        server._listener = newConnection
        server.listen(serverName)

    def parseArgs(self, argv):
        parser = argparse.ArgumentParser()
        parser.add_argument("filename",
                            nargs="*",
                            help="A list of filenames to open")
        parser.add_argument("-resetPrefs",
                            action='store_true',
                            help="Reset MCEdit preferences")
        parser.add_argument(
            "-eval",
            type=str,
            help="Code to evaluate in context of current session")
        parser.add_argument("-view",
                            action='store_true',
                            help="Open the given filenames read-only")

        self.args = parser.parse_args(argv[1:])

        if self.args.resetPrefs:
            Settings().clear()

        for filename in self.args.filename:
            try:
                if os.path.exists(filename):
                    self.commandLineWorlds.append(filename)
                else:
                    log.info("File not found: %s", filename)
            except EnvironmentError as e:
                log.info("%r", e)

    # --- Status Bar ---

    def updateStatusLabel(self,
                          pos=None,
                          blocktype=None,
                          biome=None,
                          cps=None,
                          fps=None):
        if pos is not None:
            if isinstance(pos, basestring):
                self.positionLabel.setText(pos)
            else:
                self.positionLabel.setText("%s, chunk %s" %
                                           (tuple(pos), tuple(pos.chunkPos())))
        if biome is not None:
            self.biomeLabel.setText("%s" % biome)
        if blocktype is not None:
            self.blockNameLabel.setText("%s" % blocktype.displayName)
            self.blocktypeLabel.setText(
                "(%d:%d)%s%s" % (blocktype.ID, blocktype.meta,
                                 blocktype.internalName, blocktype.blockState))
        if cps is not None:
            self.cpsLabel.setText("%0.1f cps" % cps)
        if fps is not None:
            self.fpsLabel.setText("%0.1f fps" % fps)

    idleTime = 333

    # --- Global chunk loading timer ---

    @profiler.function
    def loadTimerFired(self):
        session = self.currentSession()
        if session is None or not hasattr(session, 'loader'):
            log.debug("Loading timer idle (session %r or session.loader %r",
                      session, getattr(session, 'loader', None))

            self.loadTimer.setInterval(self.idleTime)
            return
        try:
            session.loader.next()
            self.loadTimer.setInterval(0)
        except StopIteration:
            log.debug("Loading timer idle (no chunks)")
            self.loadTimer.setInterval(self.idleTime)

    # --- Update UI after tab change ---

    def sessionDidChange(self, session, previousSession):
        """
        :type session: EditorSession
        """
        self.mainWindow.panelsToolBar.clear()
        self.mainWindow.panelsToolBar.addAction(self.toggleToolbarTextAction)

        self.mainWindow.toolsToolBar.clear()
        self.removeSessionDockWidgets()

        menuBar = self.mainWindow.menuBar()
        if previousSession:
            for menu in previousSession.menus:
                menuBar.removeAction(menu.menuAction())

        if session is not None:
            self.undoGroup.setActiveStack(session.undoStack)

            log.info("Adding session menus: %s", session.menus)
            for menu in session.menus:
                menuBar.insertMenu(self.mainWindow.menuWindow.menuAction(),
                                   menu)

            for action in session.topToolbarActions:
                if action is None:
                    self.mainWindow.panelsToolBar.addSeparator()
                else:
                    self.mainWindow.panelsToolBar.addAction(action)

            for panel in session.panels:
                self.mainWindow.panelsToolBar.addAction(
                    panel.toggleViewAction())

            self.mainWindow.panelsToolBar.addSeparator()

            for panel in self.globalPanels:
                self.mainWindow.panelsToolBar.addAction(
                    panel.toggleViewAction())

            for action in session.toolActions:
                if action is None:
                    self.mainWindow.toolsToolBar.addSeparator()
                else:
                    self.mainWindow.toolsToolBar.addAction(action)

            self.loaderInfo.object = session.loader
            view = session.editorTab.currentView()
            self.cursorInfo.object = view
            session.editorTab.viewChanged.connect(self.cursorInfo.setObject)
            self.viewInfo.object = view
            session.editorTab.viewChanged.connect(self.viewInfo.setObject)

            atlas = session.textureAtlas
            try:
                atlas.load()
            except Exception as e:
                log.exception("Failed to finalize texture atlas.")
            else:
                argbData = numpy.dstack(
                    (atlas.textureData[..., 3:], atlas.textureData[..., :3]))
                argbData = argbData[::-1, :, ::-1]
                buf = argbData.tostring()
                textureAtlasImg = QtGui.QImage(buf, atlas.width, atlas.height,
                                               QtGui.QImage.Format_RGB32)

                textureAtlasImg.textureImageData = buf  # QImage does not retain backing data

                pixmap = QtGui.QPixmap.fromImage(textureAtlasImg)
                pixmap = pixmap.scaled(atlas.width * 2, atlas.height * 2)
                self.textureAtlasView.setPixmap(pixmap)
                self.textureAtlasView.resize(atlas.width * 2, atlas.height * 2)

            for pos, dw in session.dockWidgets:
                self.mainWindow.addDockWidget(pos, dw)
                self.sessionDockWidgets.append(dw)

            session.focusWorldView()

    def removeSessionDockWidgets(self):
        for dw in self.sessionDockWidgets:
            self.mainWindow.removeDockWidget(dw)
            dw.setParent(None)

        self.sessionDockWidgets[:] = ()

    # --- Recent files ---

    def updateRecentFilesMenu(self):
        recentFiles = RecentFilesSetting.value()
        recentWorldsMenu = self.mainWindow.menuRecent_Worlds
        for i, child in enumerate(recentWorldsMenu.children()):
            if i < 2:
                continue  # Skip "clear" and separator
            child.setParent(None)

        log.info("Updating recent files menu: (%d) %s", len(recentFiles),
                 recentFiles)
        filenames = []
        displayNames = collections.Counter()
        for filename in recentFiles:
            text = util.displayName(filename)
            filenames.append((text, filename))
            displayNames[text] += 1

        displayFilenames = []
        for text, path in filenames:
            if displayNames[text] > 1:
                text += " (%s)" % path
            displayFilenames.append((text, path))

        for text, path in displayFilenames:
            log.info("Adding %s", text)
            action = recentWorldsMenu.addAction(text)

            def _triggered(p):
                def _f():
                    self.loadFile(p)

                return _f

            triggered = _triggered(path)
            action.triggered.connect(triggered)
            action.__triggered = triggered

    def addRecentFile(self, filename):
        recentFiles = RecentFilesSetting.value()
        if filename in recentFiles:
            recentFiles.remove(filename)
        recentFiles.insert(0, filename)
        if len(recentFiles) > self.recentFileLimit:
            recentFiles = recentFiles[:-1]

        RecentFilesSetting.setValue(recentFiles)
        self.updateRecentFilesMenu()

    # --- Tabs and sessions ---

    def tabCloseRequested(self, index):
        tab = self.tabWidget.widget(index)
        if hasattr(tab, "editorSession"):
            session = tab.editorSession
            if session.closeTab():
                log.info("Closed session %s", str(session))
                self.tabWidget.removeTab(index)
                # IMPORTANT: Even after removeTab is called, the tab widget must be unparented
                tab.setParent(None)
                self.removeSessionDockWidgets()
                self.undoGroup.removeStack(session.undoStack)
                self.sessions.remove(session)
                session.dealloc()
                gc.collect()
        else:
            self.tabWidget.removeTab(index)

        if self.tabWidget.count() == 0:
            self.showWorldList()

    sessionChanged = QtCore.Signal(EditorSession, EditorSession)

    def tabChanged(self):
        session = self.currentSession()
        self.mainWindow.actionConfigure_Blocks_Items.setEnabled(
            session is not None)
        self.sessionChanged.emit(session, self._currentSession)
        self._currentSession = session

    def currentTab(self):
        """

        :rtype : EditorTab | QWidget
        """
        return self.tabWidget.currentWidget()

    def currentSession(self):
        """
        Return the current session. Return None if the frontmost tab is not a session tab.

        :rtype : EditorSession | None
        """
        tab = self.currentTab()
        return getattr(tab, 'editorSession', None)

    def loadFile(self, filename, readonly=False):
        self.hideWorldList()
        fileLoadingDialog = MCEProgressDialog(self.tr("Loading world..."),
                                              None, 0, 1, self.mainWindow)
        fileLoadingDialog.setAutoReset(False)
        fileLoadingDialog.setWindowModality(Qt.WindowModal)
        fileLoadingDialog.setMinimumDuration(0)
        fileLoadingDialog.setValue(0)
        fileLoadingDialog.setWindowTitle(self.tr("Loading world..."))
        self.processEvents()

        def callback(current, max, status):
            fileLoadingDialog.setValue(current)
            fileLoadingDialog.setMaximum(max)
            fileLoadingDialog.setLabelText(status)

        try:
            configuredBlocks = self.configureBlocksDialog.getConfiguredBlocks()
            session = EditorSession(filename,
                                    configuredBlocks,
                                    readonly=readonly,
                                    progressCallback=callback)
            self.undoGroup.addStack(session.undoStack)

            self.tabWidget.addTab(session.editorTab, session.tabCaption())
            self.tabWidget.setCurrentWidget(session.editorTab)
            self.sessions.append(session)
            self.addRecentFile(filename)

            session.loadDone()

        except Exception as e:
            log.exception("EditorSession failed to open %s: %r", filename, e)
            errorTab = QtGui.QWidget()
            setWidgetError(errorTab, e,
                           "An error occurred while opening %s" % filename)
            self.tabWidget.addTab(errorTab, "Failed to open %s" % filename)

        fileLoadingDialog.reset()
        # XXX trigger viewportMoved to update minimap after GL initialization
        # session.editorTab.currentView().viewportMoved.emit(session.editorTab.currentView())

    # --- Toolbar ---

    toolbarTextToggled = QtCore.Signal(bool)

    def toggleToolbarText(self, enable):
        if enable:
            style = Qt.ToolButtonTextUnderIcon
        else:
            style = Qt.ToolButtonIconOnly
        self.mainWindow.toolsToolBar.setToolButtonStyle(style)
        self.mainWindow.panelsToolBar.setToolButtonStyle(style)
        self.toolbarTextToggled.emit(enable)

    # --- Library ---

    def libraryItemDoubleClicked(self, filename):
        session = self.currentSession()
        if session is None:
            return
        if os.path.isfile(filename):
            session.importSchematic(filename)

    # --- World List actions ---

    def editWorldFromList(self, filename):
        for editor in self.sessions:
            if editor.filename == filename:
                self.tabWidget.setCurrentWidget(editor.editorTab)
        else:
            self.loadFile(filename)

    def viewWorldFromList(self, filename):
        for editor in self.sessions:
            if editor.filename == filename:
                self.tabWidget.setCurrentWidget(editor.editorTab)
        else:
            self.loadFile(filename, readonly=True)

    def repairWorldFromList(self, filename):
        NotImplementedYet()

    def backupWorldFromList(self, filename):
        NotImplementedYet()

    # --- MCEdit Menu Actions ---

    def createNewWorld(self):
        NotImplementedYet()

    def chooseOpenWorld(self):
        startingDir = Settings().value("open_world_dialog/starting_dir",
                                       os.path.expanduser(b"~"))
        result = QtGui.QFileDialog.getOpenFileName(
            self.mainWindow, self.tr("Open World, Level or Schematic"),
            startingDir, "All files (*.*)")
        if result:
            filename = result[0]
            if filename:
                dirname, basename = os.path.split(filename)
                if basename in ("level.dat", "level.dat_old"):
                    dirname, basename = os.path.split(filename)

                Settings().setValue("open_world_dialog/starting_dir", dirname)
                self.loadFile(filename)

    def showWorldList(self):
        self.worldList.show()

    def saveCurrentWorld(self):
        session = self.currentSession()
        if session:
            try:
                session.save()
            except SessionLockLost as e:
                msgBox = QtGui.QMessageBox(QtGui.qApp.mainWindow)
                msgBox.setWindowTitle("Session Lock Lost")
                msgBox.setText(
                    "MCEdit has lost the session lock on this world.")
                msgBox.setInformativeText(
                    "Minecraft or another program has taken the session lock for this world. "
                    "MCEdit cannot ensure the world will be in a consistent state after editing. "
                    "The world must be closed.\n\n(In the future, you may be able to reopen the "
                    "world and replay your editing history on top of the world's new state.)"
                )
                msgBox.exec_()
                session.dirty = False  # Avoid invoking session.save() again.
                self.closeCurrentTab()

    def saveCurrentWorldAs(self):
        pass

    def closeCurrentTab(self):
        tab = self.currentTab()
        idx = self.tabWidget.indexOf(tab)
        self.tabCloseRequested(idx)

    def exitEditor(self):
        for session in self.sessions:
            if not session.closeTab():
                return

        self.mainWindow.saveSettings()
        self.quit()

    # --- Help Menu Actions ---

    def showAbout(self):
        from mcedit2 import __version__ as v
        credits = """<b>Supporters:</b>
<br>
<br>Andrew Devillez
<br>Alek Poyato
<br>Josh Mann
<br>NodeCraft Hosting
<br>Drew L
<br>Capt_World
<br>Adrian Brightmoore
<br>Marcel C
<br>Tim G
<br>Owen C
<br>Julian C
<br>Ausstan L
<br>Leonard P
<br>Gregory M
<br>Joseph P
<br>Lance R
<br>John B
<br>Aaron J
<br>A.M.P.
<br>Daniel B
<br>Zachary B
<br>Geoffrey C
<br>Diane W
<br>Kyle H
<br>Nathan M
<br>Ross C
<br>Thomas H
<br>Jordan S
<br>Micael L
<br>Todd A
<br>John C
<br>Elisabeth F
<br>Chris L
<br>S Spurlock
<br>Paul H
<br>Jack T
<br>
<br><b>Technologies used:</b>
<br>
<br>Python
<br>Qt
<br>PySide
<br>PyOpenGL
<br>numpy
<br>cython
<br>PyCharm
<br>
"""

        aboutBox = QtGui.QDialog(self.mainWindow)
        icon = self.windowIcon()
        iconLabel = QtGui.QLabel()
        iconLabel.setPixmap(icon.pixmap(32, 32))

        versionText = "MCEdit %s" % v
        aboutBox.setWindowTitle(versionText)
        versionLabel = QtGui.QLabel(versionText)
        copyrightLabel = QtGui.QLabel(
            "Copyright 2014-2015 David Rio Vierra. All rights reserved.")

        okButton = QtGui.QPushButton(self.tr("OK"))
        okButton.clicked.connect(aboutBox.accept)

        creditsField = QtGui.QTextEdit()
        creditsField.setReadOnly(True)
        creditsField.setHtml(credits)

        creditsBox = QtGui.QGroupBox()
        creditsBox.setTitle("Credits")

        creditsBox.setLayout(Column(creditsField))
        aboutBox.setLayout(
            Column(Row(iconLabel, Column(versionLabel, copyrightLabel, None)),
                   creditsBox, Row(None, okButton)))

        aboutBox.exec_()

    recentFileLimit = 15

    # --- App-level widgets(?) ---

    def showBlockList(self):
        session = self.currentSession()

        blockList = BlockListWidget(session.worldEditor.blocktypes,
                                    session.textureAtlas)
        self.tabWidget.insertTab(0, blockList,
                                 "Blocks for world %s" % session.filename)
        self.tabWidget.setCurrentIndex(0)

    def hideWorldList(self):
        self.worldList.close()
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.worldList))

    # --- Options ---

    def enableLightingChanged(self, value):
        from mceditlib import relight
        relight.ENABLE_LIGHTING = value

    def showPrefsDialog(self):
        self.prefsDialog.exec_()

    def showConfigureBlocksDialog(self):
        self.configureBlocksDialog.showWithSession(self.currentSession())

    def configureBlocksFinished(self):
        configuredBlocks = self.configureBlocksDialog.getConfiguredBlocks()
        self.currentSession().setConfiguredBlocks(configuredBlocks)

    def showPluginsDialog(self):
        self.pluginsDialog.exec_()

    def toggleDeveloperMode(self, enable):
        if enable:
            self.mainWindow.menuBar().addAction(self.debugMenu.menuAction())
            self.mainWindow.addDockWidget(Qt.RightDockWidgetArea,
                                          self.inspectorDockWidget)
            self.mainWindow.addDockWidget(Qt.RightDockWidgetArea,
                                          self.profileDockWidget)
            self.mainWindow.addDockWidget(Qt.RightDockWidgetArea,
                                          self.textureAtlasDockWidget)
            self.mainWindow.addDockWidget(Qt.BottomDockWidgetArea,
                                          self.infoDockWidget)

        else:
            self.mainWindow.menuBar().removeAction(self.debugMenu.menuAction())
            self.mainWindow.removeDockWidget(self.inspectorDockWidget)
            self.mainWindow.removeDockWidget(self.profileDockWidget)
            self.mainWindow.removeDockWidget(self.textureAtlasDockWidget)
            self.mainWindow.removeDockWidget(self.infoDockWidget)

    # --- App foreground ---

    def event(self, event):
        """

        :type event: QtCore.QEvent
        :rtype: bool
        """
        if event.type() == QtCore.QEvent.ApplicationActivated:
            self.tryReloadPlugins()
            event.accept()
            return True

        else:
            return super(MCEditApp, self).event(event)

    def tryReloadPlugins(self):
        if not DevModeSetting.value():
            return

        for pluginRef in plugins.getAllPlugins():
            if pluginRef.checkTimestamps():
                log.info("Plugin %s changed. Reloading plugin module...",
                         pluginRef.displayName)
                if not pluginRef.unload():
                    showErrorDialog(
                        "%s while unloading plugin \"%s\"" %
                        (pluginRef.unloadError[0].__name__,
                         pluginRef.displayName), pluginRef.unloadError, False)
                elif not pluginRef.load():
                    showErrorDialog(
                        "%s while loading plugin \"%s\"" %
                        (pluginRef.loadError[0].__name__,
                         pluginRef.displayName), pluginRef.loadError, False)
                else:
                    log.info("Plugin %s reloaded.", pluginRef.displayName)
Esempio n. 7
0
    def __init__(self, argv):
        super(MCEditApp, self).__init__(argv)
        MCEditApp.app = self

        minecraftinstall.GetInstalls().ensureValidInstall()
        self.ensureSingle()

        self.commandLineWorlds = []
        self.parseArgs(argv)

        log.warn("UserFilesDirectory: %s", getUserFilesDirectory())

        # --- Necessities ---

        translator = QtCore.QTranslator()
        translator.load(resourcePath('mcedit2/i18n/en_US.ts'))
        self.installTranslator(translator)

        log.info("Loaded translator.")

        self.setOrganizationName("MCEdit")
        self.setOrganizationDomain("mcedit.net")
        self.setApplicationName("MCEdit")
        self.setWindowIcon(QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/mcediticon.png")))
        styleSheet = file(resourcePath("mcedit2/styles/mcedit2.qcss")).read()
        self.setStyleSheet(styleSheet)

        log.info("Loaded stylesheet.")

        # --- Main Window ---

        self.mainWindow = mainWindow = MCEditMainWindow()

        self.undoGroup = QtGui.QUndoGroup()

        self.tabWidget = self.mainWindow.tabWidget
        self.tabWidget.tabCloseRequested.connect(self.tabCloseRequested)
        self.tabWidget.currentChanged.connect(self.tabChanged)

        log.info("Loaded main window.")

        tttIcon = QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/icons/toolbar_text.png"))

        self.toggleToolbarTextAction = QtGui.QAction(tttIcon, "Toolbar Text", self)

        self.toggleToolbarTextAction.setCheckable(True)
        self.toggleToolbarTextAction.setChecked(True)

        self.toggleToolbarTextAction.toggled.connect(self.toggleToolbarText)

        # --- OpenGL ---

        setDefaultFormat()

        # --- Sessions ---

        self._currentSession = None
        self.sessions = []
        self.sessionDockWidgets = []
        self.sessionChanged.connect(self.sessionDidChange)

        # --- Panel Widgets ---
        historyIcon = QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/icons/history.png"))

        self.undoView = QtGui.QUndoView(self.undoGroup)
        self.undoDockWidget = MCEDockWidget("History", mainWindow, objectName="HistoryWidget")
        self.undoDockWidget.setWidget(self.undoView)
        self.undoDockWidget.setWindowIcon(historyIcon)
        self.undoDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.undoDockWidget)
        undoToggleAction = self.undoDockWidget.toggleViewAction()
        undoToggleAction.setIcon(historyIcon)
        mainWindow.panelsToolBar.addAction(undoToggleAction)
        self.undoDockWidget.close()

        libraryIcon = QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/icons/library.png"))
        self.libraryWidget = LibraryWidget()
        self.libraryDockWidget = MCEDockWidget("Library", mainWindow, objectName="LibraryWidget")
        self.libraryDockWidget.setWidget(self.libraryWidget)
        self.libraryDockWidget.setWindowIcon(libraryIcon)
        self.libraryDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.libraryDockWidget)

        libraryToggleAction = self.libraryDockWidget.toggleViewAction()
        libraryToggleAction.setIcon(libraryIcon)
        mainWindow.panelsToolBar.addAction(libraryToggleAction)
        self.libraryDockWidget.close()
        self.sessionChanged.connect(self.libraryWidget.sessionDidChange)

        self.libraryWidget.doubleClicked.connect(self.libraryItemDoubleClicked)

        self.globalPanels = [self.undoDockWidget, self.libraryDockWidget]

        log.info("Loaded panels.")

        # --- Debug Widgets ---

        self.debugMenu = self.createDebugMenu()

        self.debugObjectInspector = ObjectInspector(mainWindow)
        self.inspectorDockWidget = MCEDockWidget("Object Inspector", mainWindow, objectName="InspectorWidget")
        self.inspectorDockWidget.setWidget(self.debugObjectInspector)
        self.debugMenu.addAction(self.inspectorDockWidget.toggleViewAction())
        self.inspectorDockWidget.close()

        self.profileView = ProfilerWidget()
        self.profileDockWidget = MCEDockWidget("Profiler", mainWindow, objectName="ProfilerWidget")
        self.profileDockWidget.setWidget(self.profileView)
        self.debugMenu.addAction(self.profileDockWidget.toggleViewAction())
        self.profileDockWidget.close()

        self.textureAtlasView = QtGui.QLabel()
        self.textureAtlasView.setScaledContents(True)
        self.textureAtlasDockWidget = MCEDockWidget("Texture Atlas", mainWindow, objectName="TextureAtlasWidget")

        self.textureAtlasArea = QtGui.QScrollArea()
        self.textureAtlasArea.setWidget(self.textureAtlasView)
        self.textureAtlasDockWidget.setWidget(self.textureAtlasArea)
        self.debugMenu.addAction(self.textureAtlasDockWidget.toggleViewAction())
        self.textureAtlasDockWidget.close()

        infoTabs = QtGui.QTabWidget()

        self.cursorInfo = WorldCursorInfo()
        infoTabs.addTab(self.cursorInfo, "Cursor")

        self.viewInfo = WorldViewInfo()
        infoTabs.addTab(self.viewInfo, "View")

        self.loaderInfo = ChunkLoaderInfo()
        infoTabs.addTab(self.loaderInfo, "Loader")

        self.infoDockWidget = MCEDockWidget("Debug Info", mainWindow, objectName="DebugInfo")
        self.infoDockWidget.setWidget(infoTabs)
        self.infoDockWidget.close()

        log.info("Loaded debug widgets.")

        # --- Menu Actions ---

        # -- MCEdit menu --
        mainWindow.actionNew_World.triggered.connect(self.createNewWorld)
        mainWindow.actionNew_World.setShortcut(QtGui.QKeySequence.New)

        mainWindow.actionOpen_World.triggered.connect(self.chooseOpenWorld)
        mainWindow.actionOpen_World.setShortcut(QtGui.QKeySequence.Open)

        mainWindow.actionShow_World_List.triggered.connect(self.showWorldList)
        mainWindow.actionShow_World_List.setShortcut(QtGui.QKeySequence("Ctrl+L"))

        mainWindow.actionSave_World.triggered.connect(self.saveCurrentWorld)
        mainWindow.actionSave_World.setShortcut(QtGui.QKeySequence.Save)

        mainWindow.actionSave_World_As.triggered.connect(self.saveCurrentWorldAs)
        mainWindow.actionSave_World_As.setShortcut(QtGui.QKeySequence.SaveAs)

        mainWindow.actionClose_World.triggered.connect(self.closeCurrentTab)
        mainWindow.actionClose_World.setShortcut(QtGui.QKeySequence.Close)

        mainWindow.actionExit_MCEdit.triggered.connect(self.exitEditor)
        mainWindow.actionExit_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Help menu --
        mainWindow.actionAbout_MCEdit.triggered.connect(self.showAbout)
        mainWindow.actionAbout_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Window Menu --
        mainWindow.menuWindow.addAction(self.undoDockWidget.toggleViewAction())
        mainWindow.menuWindow.addAction(self.libraryDockWidget.toggleViewAction())

        # -- Options Menu --
        mainWindow.actionEnable_Lighting_Updates.setChecked(EnableLightingSetting.value())
        mainWindow.actionEnable_Lighting_Updates.toggled.connect(EnableLightingSetting.setValue)

        EnableLightingSetting.valueChanged.connect(self.enableLightingChanged)
        self.enableLightingChanged(EnableLightingSetting.value())

        mainWindow.actionPreferences.triggered.connect(self.showPrefsDialog)
        mainWindow.actionConfigure_Blocks_Items.triggered.connect(self.showConfigureBlocksDialog)
        mainWindow.actionPlugins.triggered.connect(self.showPluginsDialog)

        mainWindow.actionEnable_Developer_Mode.setChecked(DevModeSetting.value())
        mainWindow.actionEnable_Developer_Mode.toggled.connect(DevModeSetting.setValue)
        DevModeSetting.valueChanged.connect(self.toggleDeveloperMode)
        self.toggleDeveloperMode(DevModeSetting.value())

        log.info("Loaded menus.")

        # --- World List ---

        self.worldList = WorldListWidget(mainWindow)
        self.worldList.editWorldClicked.connect(self.editWorldFromList)
        self.worldList.viewWorldClicked.connect(self.viewWorldFromList)
        self.worldList.backupWorldClicked.connect(self.backupWorldFromList)
        self.worldList.repairWorldClicked.connect(self.repairWorldFromList)

        log.info("Loaded world list.")

        # --- Status Bar ---

        self.positionLabel = QtGui.QLabel("xx, yy, zz", minimumWidth=100)
        self.biomeLabel = QtGui.QLabel("Nowhere", minimumWidth=100)
        self.blocktypeLabel = QtGui.QLabel("(-1:-1)minecraft:rocktonium", minimumWidth=250)
        self.blockNameLabel = QtGui.QLabel("rocktonium", minimumWidth=150)
        self.cpsLabel = QtGui.QLabel("-1 cps", minimumWidth=65)
        self.fpsLabel = QtGui.QLabel("-1 fps", minimumWidth=65)

        statusBar = mainWindow.statusBar()
        statusBar.addPermanentWidget(self.positionLabel)
        statusBar.addPermanentWidget(self.biomeLabel)
        statusBar.addPermanentWidget(self.blocktypeLabel)
        statusBar.addPermanentWidget(self.blockNameLabel)
        statusBar.addPermanentWidget(self.cpsLabel)
        statusBar.addPermanentWidget(self.fpsLabel)

        log.info("Loaded status bar.")

        # --- Load settings ---

        mainWindow.loadSettings()
        self.updateRecentFilesMenu()

        log.info("Loaded settings.")

        # --- App Dialogs ---

        # Qt weirdness - initializing QDialog with parent puts the dialog at 0,
        # 0 instead of centering it on the parent. Have to set the parent explicitly
        # and put the Qt.Dialog flag back on since changing the parent resets the
        # window flags...

        self.prefsDialog = prefsdialog.PrefsDialog(None)
        self.prefsDialog.setParent(mainWindow)
        self.prefsDialog.setWindowFlags(Qt.Dialog)

        self.configureBlocksDialog = configure_blocks.ConfigureBlocksDialog(None)
        self.configureBlocksDialog.finished.connect(self.configureBlocksFinished)
        self.configureBlocksDialog.setParent(mainWindow)
        self.configureBlocksDialog.setWindowFlags(Qt.Dialog)

        self.pluginsDialog = PluginsDialog()
        self.pluginsDialog.setParent(mainWindow)
        self.pluginsDialog.setWindowFlags(Qt.Dialog)

        log.info("Loaded app dialogs.")

        # --- Loader timer ---

        self.loadTimer = timer = LoaderTimer(self)
        timer.setInterval(0)
        timer.timeout.connect(self.loadTimerFired)
        timer.start()
        log.info("Loading timer started")

        mainWindow.showMaximized()

        QtCore.QTimer.singleShot(0, self.didFinishLaunching)
Esempio n. 8
0
class MCEditApp(QtGui.QApplication):
    def __init__(self, argv):
        super(MCEditApp, self).__init__(argv)
        MCEditApp.app = self

        minecraftinstall.GetInstalls().ensureValidInstall()
        self.ensureSingle()

        self.commandLineWorlds = []
        self.parseArgs(argv)

        log.warn("UserFilesDirectory: %s", getUserFilesDirectory())

        # --- Necessities ---

        translator = QtCore.QTranslator()
        translator.load(resourcePath('mcedit2/i18n/en_US.ts'))
        self.installTranslator(translator)

        log.info("Loaded translator.")

        self.setOrganizationName("MCEdit")
        self.setOrganizationDomain("mcedit.net")
        self.setApplicationName("MCEdit")
        self.setWindowIcon(QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/mcediticon.png")))
        styleSheet = file(resourcePath("mcedit2/styles/mcedit2.qcss")).read()
        self.setStyleSheet(styleSheet)

        log.info("Loaded stylesheet.")

        # --- Main Window ---

        self.mainWindow = mainWindow = MCEditMainWindow()

        self.undoGroup = QtGui.QUndoGroup()

        self.tabWidget = self.mainWindow.tabWidget
        self.tabWidget.tabCloseRequested.connect(self.tabCloseRequested)
        self.tabWidget.currentChanged.connect(self.tabChanged)

        log.info("Loaded main window.")

        tttIcon = QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/icons/toolbar_text.png"))

        self.toggleToolbarTextAction = QtGui.QAction(tttIcon, "Toolbar Text", self)

        self.toggleToolbarTextAction.setCheckable(True)
        self.toggleToolbarTextAction.setChecked(True)

        self.toggleToolbarTextAction.toggled.connect(self.toggleToolbarText)

        # --- OpenGL ---

        setDefaultFormat()

        # --- Sessions ---

        self._currentSession = None
        self.sessions = []
        self.sessionDockWidgets = []
        self.sessionChanged.connect(self.sessionDidChange)

        # --- Panel Widgets ---
        historyIcon = QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/icons/history.png"))

        self.undoView = QtGui.QUndoView(self.undoGroup)
        self.undoDockWidget = MCEDockWidget("History", mainWindow, objectName="HistoryWidget")
        self.undoDockWidget.setWidget(self.undoView)
        self.undoDockWidget.setWindowIcon(historyIcon)
        self.undoDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.undoDockWidget)
        undoToggleAction = self.undoDockWidget.toggleViewAction()
        undoToggleAction.setIcon(historyIcon)
        mainWindow.panelsToolBar.addAction(undoToggleAction)
        self.undoDockWidget.close()

        libraryIcon = QtGui.QIcon(resourcePath("mcedit2/assets/mcedit2/icons/library.png"))
        self.libraryWidget = LibraryWidget()
        self.libraryDockWidget = MCEDockWidget("Library", mainWindow, objectName="LibraryWidget")
        self.libraryDockWidget.setWidget(self.libraryWidget)
        self.libraryDockWidget.setWindowIcon(libraryIcon)
        self.libraryDockWidget.setUnfocusedOpacity(0.8)

        mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.libraryDockWidget)

        libraryToggleAction = self.libraryDockWidget.toggleViewAction()
        libraryToggleAction.setIcon(libraryIcon)
        mainWindow.panelsToolBar.addAction(libraryToggleAction)
        self.libraryDockWidget.close()
        self.sessionChanged.connect(self.libraryWidget.sessionDidChange)

        self.libraryWidget.doubleClicked.connect(self.libraryItemDoubleClicked)

        self.globalPanels = [self.undoDockWidget, self.libraryDockWidget]

        log.info("Loaded panels.")

        # --- Debug Widgets ---

        self.debugMenu = self.createDebugMenu()

        self.debugObjectInspector = ObjectInspector(mainWindow)
        self.inspectorDockWidget = MCEDockWidget("Object Inspector", mainWindow, objectName="InspectorWidget")
        self.inspectorDockWidget.setWidget(self.debugObjectInspector)
        self.debugMenu.addAction(self.inspectorDockWidget.toggleViewAction())
        self.inspectorDockWidget.close()

        self.profileView = ProfilerWidget()
        self.profileDockWidget = MCEDockWidget("Profiler", mainWindow, objectName="ProfilerWidget")
        self.profileDockWidget.setWidget(self.profileView)
        self.debugMenu.addAction(self.profileDockWidget.toggleViewAction())
        self.profileDockWidget.close()

        self.textureAtlasView = QtGui.QLabel()
        self.textureAtlasView.setScaledContents(True)
        self.textureAtlasDockWidget = MCEDockWidget("Texture Atlas", mainWindow, objectName="TextureAtlasWidget")

        self.textureAtlasArea = QtGui.QScrollArea()
        self.textureAtlasArea.setWidget(self.textureAtlasView)
        self.textureAtlasDockWidget.setWidget(self.textureAtlasArea)
        self.debugMenu.addAction(self.textureAtlasDockWidget.toggleViewAction())
        self.textureAtlasDockWidget.close()

        infoTabs = QtGui.QTabWidget()

        self.cursorInfo = WorldCursorInfo()
        infoTabs.addTab(self.cursorInfo, "Cursor")

        self.viewInfo = WorldViewInfo()
        infoTabs.addTab(self.viewInfo, "View")

        self.loaderInfo = ChunkLoaderInfo()
        infoTabs.addTab(self.loaderInfo, "Loader")

        self.infoDockWidget = MCEDockWidget("Debug Info", mainWindow, objectName="DebugInfo")
        self.infoDockWidget.setWidget(infoTabs)
        self.infoDockWidget.close()

        log.info("Loaded debug widgets.")

        # --- Menu Actions ---

        # -- MCEdit menu --
        mainWindow.actionNew_World.triggered.connect(self.createNewWorld)
        mainWindow.actionNew_World.setShortcut(QtGui.QKeySequence.New)

        mainWindow.actionOpen_World.triggered.connect(self.chooseOpenWorld)
        mainWindow.actionOpen_World.setShortcut(QtGui.QKeySequence.Open)

        mainWindow.actionShow_World_List.triggered.connect(self.showWorldList)
        mainWindow.actionShow_World_List.setShortcut(QtGui.QKeySequence("Ctrl+L"))

        mainWindow.actionSave_World.triggered.connect(self.saveCurrentWorld)
        mainWindow.actionSave_World.setShortcut(QtGui.QKeySequence.Save)

        mainWindow.actionSave_World_As.triggered.connect(self.saveCurrentWorldAs)
        mainWindow.actionSave_World_As.setShortcut(QtGui.QKeySequence.SaveAs)

        mainWindow.actionClose_World.triggered.connect(self.closeCurrentTab)
        mainWindow.actionClose_World.setShortcut(QtGui.QKeySequence.Close)

        mainWindow.actionExit_MCEdit.triggered.connect(self.exitEditor)
        mainWindow.actionExit_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Help menu --
        mainWindow.actionAbout_MCEdit.triggered.connect(self.showAbout)
        mainWindow.actionAbout_MCEdit.setShortcut(QtGui.QKeySequence.Quit)

        # -- Window Menu --
        mainWindow.menuWindow.addAction(self.undoDockWidget.toggleViewAction())
        mainWindow.menuWindow.addAction(self.libraryDockWidget.toggleViewAction())

        # -- Options Menu --
        mainWindow.actionEnable_Lighting_Updates.setChecked(EnableLightingSetting.value())
        mainWindow.actionEnable_Lighting_Updates.toggled.connect(EnableLightingSetting.setValue)

        EnableLightingSetting.valueChanged.connect(self.enableLightingChanged)
        self.enableLightingChanged(EnableLightingSetting.value())

        mainWindow.actionPreferences.triggered.connect(self.showPrefsDialog)
        mainWindow.actionConfigure_Blocks_Items.triggered.connect(self.showConfigureBlocksDialog)
        mainWindow.actionPlugins.triggered.connect(self.showPluginsDialog)

        mainWindow.actionEnable_Developer_Mode.setChecked(DevModeSetting.value())
        mainWindow.actionEnable_Developer_Mode.toggled.connect(DevModeSetting.setValue)
        DevModeSetting.valueChanged.connect(self.toggleDeveloperMode)
        self.toggleDeveloperMode(DevModeSetting.value())

        log.info("Loaded menus.")

        # --- World List ---

        self.worldList = WorldListWidget(mainWindow)
        self.worldList.editWorldClicked.connect(self.editWorldFromList)
        self.worldList.viewWorldClicked.connect(self.viewWorldFromList)
        self.worldList.backupWorldClicked.connect(self.backupWorldFromList)
        self.worldList.repairWorldClicked.connect(self.repairWorldFromList)

        log.info("Loaded world list.")

        # --- Status Bar ---

        self.positionLabel = QtGui.QLabel("xx, yy, zz", minimumWidth=100)
        self.biomeLabel = QtGui.QLabel("Nowhere", minimumWidth=100)
        self.blocktypeLabel = QtGui.QLabel("(-1:-1)minecraft:rocktonium", minimumWidth=250)
        self.blockNameLabel = QtGui.QLabel("rocktonium", minimumWidth=150)
        self.cpsLabel = QtGui.QLabel("-1 cps", minimumWidth=65)
        self.fpsLabel = QtGui.QLabel("-1 fps", minimumWidth=65)

        statusBar = mainWindow.statusBar()
        statusBar.addPermanentWidget(self.positionLabel)
        statusBar.addPermanentWidget(self.biomeLabel)
        statusBar.addPermanentWidget(self.blocktypeLabel)
        statusBar.addPermanentWidget(self.blockNameLabel)
        statusBar.addPermanentWidget(self.cpsLabel)
        statusBar.addPermanentWidget(self.fpsLabel)

        log.info("Loaded status bar.")

        # --- Load settings ---

        mainWindow.loadSettings()
        self.updateRecentFilesMenu()

        log.info("Loaded settings.")

        # --- App Dialogs ---

        # Qt weirdness - initializing QDialog with parent puts the dialog at 0,
        # 0 instead of centering it on the parent. Have to set the parent explicitly
        # and put the Qt.Dialog flag back on since changing the parent resets the
        # window flags...

        self.prefsDialog = prefsdialog.PrefsDialog(None)
        self.prefsDialog.setParent(mainWindow)
        self.prefsDialog.setWindowFlags(Qt.Dialog)

        self.configureBlocksDialog = configure_blocks.ConfigureBlocksDialog(None)
        self.configureBlocksDialog.finished.connect(self.configureBlocksFinished)
        self.configureBlocksDialog.setParent(mainWindow)
        self.configureBlocksDialog.setWindowFlags(Qt.Dialog)

        self.pluginsDialog = PluginsDialog()
        self.pluginsDialog.setParent(mainWindow)
        self.pluginsDialog.setWindowFlags(Qt.Dialog)

        log.info("Loaded app dialogs.")

        # --- Loader timer ---

        self.loadTimer = timer = LoaderTimer(self)
        timer.setInterval(0)
        timer.timeout.connect(self.loadTimerFired)
        timer.start()
        log.info("Loading timer started")

        mainWindow.showMaximized()

        QtCore.QTimer.singleShot(0, self.didFinishLaunching)

    # --- Startup code ---

    @profiler.function
    def didFinishLaunching(self):
        log.info("Finding plugins")

        if getattr(sys, 'frozen', False):
            # frozen - load from app dir
            pluginsDir = getUserPluginsDirectory()
            plugins.findNewPluginsInDir(pluginsDir)
        else:
            # not frozen - load from src/plugins
            # from editorapp.py, ../../plugins
            devPluginsDir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "plugins")
            plugins.findNewPluginsInDir(devPluginsDir)

        for pluginRef in plugins.getAllPlugins():
            if pluginRef.enabled:
                if not pluginRef.load():
                    showErrorDialog("%s while loading plugin \"%s\"" % (pluginRef.loadError[0].__name__, pluginRef.displayName), pluginRef.loadError, False)

        log.info("Opening worlds from command line.")

        if len(self.commandLineWorlds):
            for filename in self.commandLineWorlds:
                self.loadFile(filename, self.args.view)
        else:
            self.showWorldList()

        if len(self.sessions) and self.args.eval:
            session = self.sessions[-1]
            eval_globals = {"session": session,
                            "self": self}
            exec(self.args.eval, eval_globals)

    pluginsChanged = QtCore.Signal()

    consoleWidget = None

    def createDebugMenu(self):
        debugMenu = QtGui.QMenu(self.tr("&Debug"))

        def raiseError():
            ret = QtGui.QMessageBox.warning(self.mainWindow,
                                            self.tr("Raise Error"),
                                            self.tr("Raise an error? This may crash MCEdit."),
                                            buttons=QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
            if ret == QtGui.QMessageBox.Yes:
                raise ValueError("User requested error")

        debugMenu.addAction(self.tr("Raise Error")).triggered.connect(raiseError)

        def showConsole():
            if self.consoleWidget is None:
                self.consoleWidget = terminal_widget(sessions=self.sessions)
            self.consoleWidget.show()

        debugMenu.addAction(self.tr("IPython Console")).triggered.connect(showConsole)

        objGraph = ObjGraphWidget()

        def showObjGraph():
            objGraph.show()

        debugMenu.addAction(self.tr("ObjGraph")).triggered.connect(showObjGraph)

        def showHeapy():
            from guppy import hpy
            h = hpy()
            print(h.heap())

        debugMenu.addAction(self.tr("Heap Trace (slow)")).triggered.connect(showHeapy)

        debugMenu.addAction(self.tr("Collect Garbage")).triggered.connect(gc.collect)

        return debugMenu

    def ensureSingle(self):
        serverName = "MCEdit.Application"
        socket = QtNetwork.QLocalSocket()
        socket.connectToServer(serverName)
        if socket.waitForConnected(500):
            # TODO: get filenames from argv and pass to running app
            log.error("%s already running", serverName)
            raise SystemExit  # Already running

        def newConnection():
            newSocket = server.nextPendingConnection()
            # TODO: read filenames from socket
            newSocket.close()
            self.mainWindow.activateWindow()
            self.mainWindow.raise_()

        server = QtNetwork.QLocalServer(newConnection=newConnection)
        server._listener = newConnection
        server.listen(serverName)

    def parseArgs(self, argv):
        parser = argparse.ArgumentParser()
        parser.add_argument("filename", nargs="*",
                            help="A list of filenames to open")
        parser.add_argument("-resetPrefs", action='store_true',
                            help="Reset MCEdit preferences")
        parser.add_argument("-eval", type=str,
                            help="Code to evaluate in context of current session")
        parser.add_argument("-view", action='store_true',
                            help="Open the given filenames read-only")

        self.args = parser.parse_args(argv[1:])

        if self.args.resetPrefs:
            Settings().clear()

        for filename in self.args.filename:
            try:
                if os.path.exists(filename):
                    self.commandLineWorlds.append(filename)
                else:
                    log.info("File not found: %s", filename)
            except EnvironmentError as e:
                log.info("%r", e)

    # --- Status Bar ---

    def updateStatusLabel(self, pos=None, blocktype=None, biome=None, cps=None, fps=None):
        if pos is not None:
            if isinstance(pos, basestring):
                self.positionLabel.setText(pos)
            else:
                self.positionLabel.setText("%s, chunk %s" % (tuple(pos), tuple(pos.chunkPos())))
        if biome is not None:
            self.biomeLabel.setText("%s" % biome)
        if blocktype is not None:
            self.blockNameLabel.setText("%s" % blocktype.displayName)
            self.blocktypeLabel.setText("(%d:%d)%s%s" % (blocktype.ID, blocktype.meta, blocktype.internalName, blocktype.blockState))
        if cps is not None:
            self.cpsLabel.setText("%0.1f cps" % cps)
        if fps is not None:
            self.fpsLabel.setText("%0.1f fps" % fps)

    idleTime = 333

    # --- Global chunk loading timer ---

    @profiler.function
    def loadTimerFired(self):
        session = self.currentSession()
        if session is None or not hasattr(session, 'loader'):
            log.debug("Loading timer idle (session %r or session.loader %r",
                      session, getattr(session, 'loader', None))

            self.loadTimer.setInterval(self.idleTime)
            return
        try:
            session.loader.next()
            self.loadTimer.setInterval(0)
        except StopIteration:
            log.debug("Loading timer idle (no chunks)")
            self.loadTimer.setInterval(self.idleTime)

    # --- Update UI after tab change ---

    def sessionDidChange(self, session, previousSession):
        """
        :type session: EditorSession
        """
        self.mainWindow.panelsToolBar.clear()
        self.mainWindow.panelsToolBar.addAction(self.toggleToolbarTextAction)

        self.mainWindow.toolsToolBar.clear()
        self.removeSessionDockWidgets()

        menuBar = self.mainWindow.menuBar()
        if previousSession:
            for menu in previousSession.menus:
                menuBar.removeAction(menu.menuAction())

        if session is not None:
            self.undoGroup.setActiveStack(session.undoStack)

            log.info("Adding session menus: %s", session.menus)
            for menu in session.menus:
                menuBar.insertMenu(self.mainWindow.menuWindow.menuAction(), menu)

            for action in session.topToolbarActions:
                if action is None:
                    self.mainWindow.panelsToolBar.addSeparator()
                else:
                    self.mainWindow.panelsToolBar.addAction(action)

            for panel in session.panels:
                self.mainWindow.panelsToolBar.addAction(panel.toggleViewAction())

            self.mainWindow.panelsToolBar.addSeparator()

            for panel in self.globalPanels:
                self.mainWindow.panelsToolBar.addAction(panel.toggleViewAction())

            for action in session.toolActions:
                if action is None:
                    self.mainWindow.toolsToolBar.addSeparator()
                else:
                    self.mainWindow.toolsToolBar.addAction(action)

            self.loaderInfo.object = session.loader
            view = session.editorTab.currentView()
            self.cursorInfo.object = view
            session.editorTab.viewChanged.connect(self.cursorInfo.setObject)
            self.viewInfo.object = view
            session.editorTab.viewChanged.connect(self.viewInfo.setObject)

            atlas = session.textureAtlas
            try:
                atlas.load()
            except Exception as e:
                log.exception("Failed to finalize texture atlas.")
            else:
                argbData = numpy.dstack((atlas.textureData[..., 3:], atlas.textureData[..., :3]))
                argbData = argbData[::-1, :, ::-1]
                buf = argbData.tostring()
                textureAtlasImg = QtGui.QImage(buf,
                                               atlas.width, atlas.height,
                                               QtGui.QImage.Format_RGB32)

                textureAtlasImg.textureImageData = buf  # QImage does not retain backing data

                pixmap = QtGui.QPixmap.fromImage(textureAtlasImg)
                pixmap = pixmap.scaled(atlas.width * 2, atlas.height * 2)
                self.textureAtlasView.setPixmap(pixmap)
                self.textureAtlasView.resize(atlas.width * 2, atlas.height * 2)

            for pos, dw in session.dockWidgets:
                self.mainWindow.addDockWidget(pos, dw)
                self.sessionDockWidgets.append(dw)

            session.focusWorldView()

    def removeSessionDockWidgets(self):
        for dw in self.sessionDockWidgets:
            self.mainWindow.removeDockWidget(dw)
            dw.setParent(None)

        self.sessionDockWidgets[:] = ()

    # --- Recent files ---

    def updateRecentFilesMenu(self):
        recentFiles = RecentFilesSetting.value()
        recentWorldsMenu = self.mainWindow.menuRecent_Worlds
        for i, child in enumerate(recentWorldsMenu.children()):
            if i < 2:
                continue  # Skip "clear" and separator
            child.setParent(None)

        log.info("Updating recent files menu: (%d) %s", len(recentFiles), recentFiles)
        filenames = []
        displayNames = collections.Counter()
        for filename in recentFiles:
            text = util.displayName(filename)
            filenames.append((text, filename))
            displayNames[text] += 1

        displayFilenames = []
        for text, path in filenames:
            if displayNames[text] > 1:
                text += " (%s)" % path
            displayFilenames.append((text, path))

        for text, path in displayFilenames:
            log.info("Adding %s", text)
            action = recentWorldsMenu.addAction(text)

            def _triggered(p):
                def _f():
                    self.loadFile(p)
                return _f

            triggered = _triggered(path)
            action.triggered.connect(triggered)
            action.__triggered = triggered

    def addRecentFile(self, filename):
        recentFiles = RecentFilesSetting.value()
        if filename in recentFiles:
            recentFiles.remove(filename)
        recentFiles.insert(0, filename)
        if len(recentFiles) > self.recentFileLimit:
            recentFiles = recentFiles[:-1]

        RecentFilesSetting.setValue(recentFiles)
        self.updateRecentFilesMenu()

    # --- Tabs and sessions ---

    def tabCloseRequested(self, index):
        tab = self.tabWidget.widget(index)
        if hasattr(tab, "editorSession"):
            session = tab.editorSession
            if session.closeTab():
                log.info("Closed session %s", str(session))
                self.tabWidget.removeTab(index)
                # IMPORTANT: Even after removeTab is called, the tab widget must be unparented
                tab.setParent(None)
                self.removeSessionDockWidgets()
                self.undoGroup.removeStack(session.undoStack)
                self.sessions.remove(session)
                session.dealloc()
                gc.collect()
        else:
            self.tabWidget.removeTab(index)

        if self.tabWidget.count() == 0:
            self.showWorldList()

    sessionChanged = QtCore.Signal(EditorSession, EditorSession)

    def tabChanged(self):
        session = self.currentSession()
        self.sessionChanged.emit(session, self._currentSession)
        self._currentSession = session

    def currentTab(self):
        """

        :rtype : EditorTab | QWidget
        """
        return self.tabWidget.currentWidget()

    def currentSession(self):
        """
        Return the current session. Return None if the frontmost tab is not a session tab.

        :rtype : EditorSession | None
        """
        tab = self.currentTab()
        return getattr(tab, 'editorSession', None)

    def loadFile(self, filename, readonly=False):
        self.hideWorldList()
        fileLoadingDialog = MCEProgressDialog(self.tr("Loading world..."),
                                              None,
                                              0,
                                              1,
                                              self.mainWindow)
        fileLoadingDialog.setAutoReset(False)
        fileLoadingDialog.setWindowModality(Qt.WindowModal)
        fileLoadingDialog.setMinimumDuration(0)
        fileLoadingDialog.setValue(0)
        fileLoadingDialog.setWindowTitle(self.tr("Loading world..."))
        self.processEvents()

        def callback(current, max, status):
            fileLoadingDialog.setValue(current)
            fileLoadingDialog.setMaximum(max)
            fileLoadingDialog.setLabelText(status)

        try:
            configuredBlocks = self.configureBlocksDialog.getConfiguredBlocks()
            session = EditorSession(filename, configuredBlocks, readonly=readonly, progressCallback=callback)
            self.undoGroup.addStack(session.undoStack)

            self.tabWidget.addTab(session.editorTab, session.tabCaption())
            self.tabWidget.setCurrentWidget(session.editorTab)
            self.sessions.append(session)
            self.addRecentFile(filename)

            session.loadDone()

        except Exception as e:
            log.exception("EditorSession failed to open %s: %r", filename, e)
            errorTab = QtGui.QWidget()
            setWidgetError(errorTab, e, "An error occurred while opening %s" % filename)
            self.tabWidget.addTab(errorTab, "Failed to open %s" % filename)

        fileLoadingDialog.reset()
        # XXX trigger viewportMoved to update minimap after GL initialization
        # session.editorTab.currentView().viewportMoved.emit(session.editorTab.currentView())

    # --- Toolbar ---

    toolbarTextToggled = QtCore.Signal(bool)

    def toggleToolbarText(self, enable):
        if enable:
            style = Qt.ToolButtonTextUnderIcon
        else:
            style = Qt.ToolButtonIconOnly
        self.mainWindow.toolsToolBar.setToolButtonStyle(style)
        self.mainWindow.panelsToolBar.setToolButtonStyle(style)
        self.toolbarTextToggled.emit(enable)

    # --- Library ---

    def libraryItemDoubleClicked(self, filename):
        session = self.currentSession()
        if session is None:
            return
        if os.path.isfile(filename):
            session.importSchematic(filename)

    # --- World List actions ---

    def editWorldFromList(self, filename):
        for editor in self.sessions:
            if editor.filename == filename:
                self.tabWidget.setCurrentWidget(editor.editorTab)
        else:
            self.loadFile(filename)

    def viewWorldFromList(self, filename):
        for editor in self.sessions:
            if editor.filename == filename:
                self.tabWidget.setCurrentWidget(editor.editorTab)
        else:
            self.loadFile(filename, readonly=True)

    def repairWorldFromList(self, filename):
        NotImplementedYet()

    def backupWorldFromList(self, filename):
        NotImplementedYet()

    # --- MCEdit Menu Actions ---

    def createNewWorld(self):
        NotImplementedYet()

    def chooseOpenWorld(self):
        startingDir = Settings().value("open_world_dialog/starting_dir", os.path.expanduser(b"~"))
        result = QtGui.QFileDialog.getOpenFileName(self.mainWindow, self.tr("Open World, Level or Schematic"),
                                                   startingDir,
                                                   "All files (*.*)")
        if result:
            filename = result[0]
            if filename:
                dirname, basename = os.path.split(filename)
                if basename in ("level.dat", "level.dat_old"):
                    dirname, basename = os.path.split(filename)

                Settings().setValue("open_world_dialog/starting_dir", dirname)
                self.loadFile(filename)

    def showWorldList(self):
        self.worldList.show()

    def saveCurrentWorld(self):
        session = self.currentSession()
        if session:
            try:
                session.save()
            except SessionLockLost as e:
                msgBox = QtGui.QMessageBox(QtGui.qApp.mainWindow)
                msgBox.setWindowTitle("Session Lock Lost")
                msgBox.setText("MCEdit has lost the session lock on this world.")
                msgBox.setInformativeText("Minecraft or another program has taken the session lock for this world. "
                                          "MCEdit cannot ensure the world will be in a consistent state after editing. "
                                          "The world must be closed.\n\n(In the future, you may be able to reopen the "
                                          "world and replay your editing history on top of the world's new state.)")
                msgBox.exec_()
                session.dirty = False  # Avoid invoking session.save() again.
                self.closeCurrentTab()

    def saveCurrentWorldAs(self):
        pass

    def closeCurrentTab(self):
        tab = self.currentTab()
        idx = self.tabWidget.indexOf(tab)
        self.tabCloseRequested(idx)

    def exitEditor(self):
        for session in self.sessions:
            if not session.closeTab():
                return

        self.mainWindow.saveSettings()
        self.quit()

    # --- Help Menu Actions ---

    def showAbout(self):
        from mcedit2 import __version__ as v
        credits = """<b>Supporters:</b>
<br>
<br>Andrew Devillez
<br>Alek Poyato
<br>Josh Mann
<br>NodeCraft Hosting
<br>Drew L
<br>Capt_World
<br>Adrian Brightmoore
<br>Marcel C
<br>Tim G
<br>Owen C
<br>Julian C
<br>Ausstan L
<br>Leonard P
<br>Gregory M
<br>Joseph P
<br>Lance R
<br>John B
<br>Aaron J
<br>A.M.P.
<br>Daniel B
<br>Zachary B
<br>Geoffrey C
<br>Diane W
<br>Kyle H
<br>Nathan M
<br>Ross C
<br>Thomas H
<br>Jordan S
<br>Micael L
<br>Todd A
<br>John C
<br>Elisabeth F
<br>Chris L
<br>S Spurlock
<br>Paul H
<br>Jack T
<br>
<br><b>Technologies used:</b>
<br>
<br>Python
<br>Qt
<br>PySide
<br>PyOpenGL
<br>numpy
<br>cython
<br>PyCharm
<br>
"""

        aboutBox = QtGui.QDialog(self.mainWindow)
        icon = self.windowIcon()
        iconLabel = QtGui.QLabel()
        iconLabel.setPixmap(icon.pixmap(32, 32))

        versionText = "MCEdit %s" % v
        aboutBox.setWindowTitle(versionText)
        versionLabel = QtGui.QLabel(versionText)
        copyrightLabel = QtGui.QLabel("Copyright 2014-2015 David Rio Vierra. All rights reserved.")

        okButton = QtGui.QPushButton(self.tr("OK"))
        okButton.clicked.connect(aboutBox.accept)

        creditsField = QtGui.QTextEdit()
        creditsField.setReadOnly(True)
        creditsField.setHtml(credits)

        creditsBox = QtGui.QGroupBox()
        creditsBox.setTitle("Credits")

        creditsBox.setLayout(Column(creditsField))
        aboutBox.setLayout(Column(Row(iconLabel, Column(versionLabel, copyrightLabel, None)),
                                  creditsBox,
                                  Row(None, okButton)))

        aboutBox.exec_()

    recentFileLimit = 15

    # --- App-level widgets(?) ---

    def showBlockList(self):
        session = self.currentSession()

        blockList = BlockListWidget(session.worldEditor.blocktypes, session.textureAtlas)
        self.tabWidget.insertTab(0, blockList, "Blocks for world %s" % session.filename)
        self.tabWidget.setCurrentIndex(0)

    def hideWorldList(self):
        self.worldList.close()
        self.tabWidget.removeTab(self.tabWidget.indexOf(self.worldList))

    # --- Options ---

    def enableLightingChanged(self, value):
        from mceditlib import relight
        relight.ENABLE_LIGHTING = value

    def showPrefsDialog(self):
        self.prefsDialog.exec_()

    def showConfigureBlocksDialog(self):
        self.configureBlocksDialog.showWithSession(self.currentSession())

    def configureBlocksFinished(self):
        configuredBlocks = self.configureBlocksDialog.getConfiguredBlocks()
        self.currentSession().setConfiguredBlocks(configuredBlocks)

    def showPluginsDialog(self):
        self.pluginsDialog.exec_()

    def toggleDeveloperMode(self, enable):
        if enable:
            self.mainWindow.menuBar().addAction(self.debugMenu.menuAction())
            self.mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.inspectorDockWidget)
            self.mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.profileDockWidget)
            self.mainWindow.addDockWidget(Qt.RightDockWidgetArea, self.textureAtlasDockWidget)
            self.mainWindow.addDockWidget(Qt.BottomDockWidgetArea, self.infoDockWidget)

        else:
            self.mainWindow.menuBar().removeAction(self.debugMenu.menuAction())
            self.mainWindow.removeDockWidget(self.inspectorDockWidget)
            self.mainWindow.removeDockWidget(self.profileDockWidget)
            self.mainWindow.removeDockWidget(self.textureAtlasDockWidget)
            self.mainWindow.removeDockWidget(self.infoDockWidget)

    # --- App foreground ---

    def event(self, event):
        """

        :type event: QtCore.QEvent
        :rtype: bool
        """
        if event.type() == QtCore.QEvent.ApplicationActivated:
            self.tryReloadPlugins()
            event.accept()
            return True

        else:
            return super(MCEditApp, self).event(event)

    def tryReloadPlugins(self):
        if not DevModeSetting.value():
            return
        
        for pluginRef in plugins.getAllPlugins():
            if pluginRef.checkTimestamps():
                log.info("Plugin %s changed. Reloading plugin module...", pluginRef.displayName)
                if not pluginRef.unload():
                    showErrorDialog("%s while unloading plugin \"%s\"" % (pluginRef.unloadError[0].__name__, pluginRef.displayName), pluginRef.unloadError, False)
                    if not pluginRef.load():
                        showErrorDialog("%s while loading plugin \"%s\"" % (pluginRef.loadError[0].__name__, pluginRef.displayName), pluginRef.loadError, False)
Esempio n. 9
0
class FindReplaceNBT(QtCore.QObject):
    def __init__(self, editorSession, dialog):
        super(FindReplaceNBT, self).__init__()
        self.editorSession = editorSession
        self.widget = load_ui("find_replace_nbt.ui")
        self.dialog = dialog

        self.resultsWidget = load_ui("find_replace_nbt_results.ui")
        self.resultsDockWidget = MCEDockWidget("NBT Search",
                                               objectName="nbtSearch")
        self.resultsDockWidget.setWidget(self.resultsWidget)
        self.resultsDockWidget.hide()

        self.resultsModel = NBTResultsModel()
        self.resultsWidget.resultsView.setModel(self.resultsModel)
        self.resultsWidget.resultsView.clicked.connect(
            self.resultsViewIndexClicked)

        # --- Buttons ---
        self.widget.findButton.clicked.connect(self.find)

        self.resultsWidget.stopButton.clicked.connect(self.stop)
        self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)

        self.resultsWidget.replaceSelectedButton.clicked.connect(
            self.replaceSelected)
        self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)

        self.widget.searchNameCheckbox.toggled.connect(self.searchForToggled)
        self.widget.searchValueCheckbox.toggled.connect(self.searchForToggled)
        self.findTimer = None
        self.finder = None

        # --- Search for... ---
        self.widget.nameField.setText(nbtReplaceSettings.nameField.value(""))
        self.widget.searchNameCheckbox.setChecked(
            len(self.widget.nameField.text()) > 0)
        self.widget.nameField.textChanged.connect(self.nameFieldChanged)

        self.widget.valueField.setText(nbtReplaceSettings.valueField.value(""))
        self.widget.searchValueCheckbox.setChecked(
            len(self.widget.valueField.text()) > 0)
        self.widget.valueField.textChanged.connect(self.valueFieldChanged)

        # --- Search in... ---
        self.widget.searchEntitiesCheckbox.setChecked(
            nbtReplaceSettings.searchEntitiesChecked.value())
        self.widget.searchEntitiesCheckbox.toggled.connect(
            nbtReplaceSettings.searchEntitiesChecked.setValue)

        self.widget.entityIDField.setText(
            nbtReplaceSettings.entityIDField.value())
        self.widget.entityIDField.textChanged.connect(
            self.entityIDFieldChanged)

        self.widget.searchTileEntitiesCheckbox.setChecked(
            nbtReplaceSettings.searchTileEntitiesChecked.value())
        self.widget.searchTileEntitiesCheckbox.toggled.connect(
            nbtReplaceSettings.searchTileEntitiesChecked.setValue)

        self.widget.tileEntityIDField.setText(
            nbtReplaceSettings.tileEntityIDField.value())
        self.widget.tileEntityIDField.textChanged.connect(
            self.tileEntityIDFieldChanged)

        # --- Replace with... ---
        self.widget.replaceNameField.setText(
            nbtReplaceSettings.replaceNameField.value())
        self.resultsWidget.replaceNameField.setText(
            nbtReplaceSettings.replaceNameField.value())
        self.widget.replaceNameField.textChanged.connect(
            self.replaceNameFieldChanged)

        self.widget.replaceNameCheckbox.setChecked(
            len(self.widget.replaceNameField.text()))
        self.resultsWidget.replaceNameCheckbox.setChecked(
            len(self.widget.replaceNameField.text()))

        self.widget.replaceValueField.setText(
            nbtReplaceSettings.replaceValueField.value())
        self.resultsWidget.replaceValueField.setText(
            nbtReplaceSettings.replaceValueField.value())
        self.widget.replaceValueField.textChanged.connect(
            self.replaceValueFieldChanged)

        self.widget.replaceValueCheckbox.setChecked(
            len(self.widget.replaceValueField.text()))
        self.resultsWidget.replaceValueCheckbox.setChecked(
            len(self.widget.replaceValueField.text()))

        self.widget.replaceValueTagTypeComboBox.setCurrentIndex(
            nbtReplaceSettings.replaceValueTagType.value())
        self.widget.replaceValueTagTypeComboBox.currentIndexChanged[
            int].connect(self.valueTagTypeChanged)

    def dialogOpened(self):
        currentSelection = self.editorSession.currentSelection
        self.widget.inSelectionCheckbox.setChecked(
            currentSelection is not None and currentSelection.volume > 0)

    def searchForToggled(self):
        #canSearch = self.widget.searchNameCheckbox.isChecked() or self.widget.searchValueCheckbox.isChecked()
        #self.widget.findButton.setEnabled(canSearch)
        pass

    def nameFieldChanged(self, value):
        nbtReplaceSettings.nameField.setValue(value)
        self.widget.searchNameCheckbox.setChecked(len(value) > 0)

    def valueFieldChanged(self, value):
        nbtReplaceSettings.valueField.setValue(value)
        self.widget.searchValueCheckbox.setChecked(len(value) > 0)

    def entityIDFieldChanged(self, value):
        nbtReplaceSettings.entityIDField.setValue(value)
        if len(value):
            self.widget.searchEntitiesCheckbox.setChecked(True)

    def tileEntityIDFieldChanged(self, value):
        nbtReplaceSettings.tileEntityIDField.setValue(value)
        if len(value):
            self.widget.searchTileEntitiesCheckbox.setChecked(True)

    def replaceNameFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceNameField.value():
            nbtReplaceSettings.replaceNameField.setValue(value)

            self.widget.replaceNameCheckbox.setChecked(len(value) > 0)
            self.widget.replaceNameField.setText(value)

            self.resultsWidget.replaceNameCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceNameField.setText(value)

    def replaceValueFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceValueField.value():
            nbtReplaceSettings.replaceValueField.setValue(value)

            self.widget.replaceValueCheckbox.setChecked(len(value) > 0)
            self.widget.replaceValueField.setText(value)

            self.resultsWidget.replaceValueCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceValueField.setText(value)

    def valueTagTypeChanged(self, index):
        if index != nbtReplaceSettings.replaceValueTagType.value():
            nbtReplaceSettings.replaceValueTagType.setValue(index)
            self.widget.replaceValueTagTypeComboBox.setCurrentIndex(index)
            self.resultsWidget.replaceValueTagTypeComboBox.setCurrentIndex(
                index)

    def resultsViewIndexClicked(self, modelIndex):
        row = modelIndex.row()
        result = self.resultsModel.results[row]
        if result.resultType == result.EntityResult:
            entity = result.getEntity(self.editorSession.currentDimension)
            if entity is not None:
                self.editorSession.zoomAndInspectEntity(entity)  # xxxxxxx!!!
            else:
                log.error("Entity not found for result %s", str(result))
        if result.resultType == result.TileEntityResult:
            self.editorSession.zoomAndInspectBlock(result.position)

    def find(self):
        searchNames = self.widget.searchNameCheckbox.isChecked()
        targetName = self.widget.nameField.text()
        searchValues = self.widget.searchValueCheckbox.isChecked()
        targetValue = self.widget.valueField.text()

        searchEntities = self.widget.searchEntitiesCheckbox.isChecked()
        targetEntityIDs = self.widget.entityIDField.text()
        if len(targetEntityIDs):
            targetEntityIDs = targetEntityIDs.split(';')

        searchTileEntities = self.widget.searchTileEntitiesCheckbox.isChecked()
        targetTileEntityIDs = self.widget.tileEntityIDField.text()
        if len(targetTileEntityIDs):
            targetTileEntityIDs = targetTileEntityIDs.split(';')

        if not any(
            (searchNames, searchValues, searchEntities, searchTileEntities)):
            # Nothing to find
            return

        dim = self.editorSession.currentDimension
        inSelection = self.widget.inSelectionCheckbox.isChecked()
        if inSelection:
            selection = self.editorSession.currentSelection
            if selection is None:
                return
        else:
            selection = dim.bounds

        def _matchTag(name_or_index, tag):
            if searchValues and not tag.isCompound() and not tag.isList():
                if tag.tagID == nbt.ID_STRING and targetValue in tag.value:
                    return True
                elif targetValue == tag.value:
                    return True
            if searchNames and isinstance(
                    name_or_index, basestring) and targetName in name_or_index:
                return True
            return False

        def _findTag(name_or_index, tag, path):
            if _matchTag(name_or_index, tag):
                if tag.isCompound():
                    value = "Compound"  # describeCompound
                elif tag.isList():
                    value = "List"  # describeList
                else:
                    value = str(tag.value)

                return str(name_or_index), path, value

        def _findEntitiesInChunk(chunk):
            for entity in chunk.Entities:
                if entity.Position not in selection:
                    continue
                if len(targetEntityIDs) and entity.id not in targetEntityIDs:
                    continue

                try:
                    uuid = entity.UUID
                except KeyError:
                    uuid = None  # Don't want to use find/replace on entities without UUIDs

                if not searchNames and not searchValues:
                    # Finding entities only
                    self.resultsModel.addEntry(
                        tagName="id",
                        value=entity.id,
                        id=entity.id,
                        path=[],
                        position=entity.Position,
                        uuid=uuid,
                        resultType=NBTResultsEntry.EntityResult)
                    continue

                tag = entity.raw_tag()

                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(
                            tagName=name,
                            value=value,
                            id=entity.id,
                            path=path,
                            position=entity.Position,
                            uuid=uuid,
                            resultType=NBTResultsEntry.EntityResult)

        def _findTileEntitiesInChunk(chunk):
            for tileEntity in chunk.TileEntities:
                if tileEntity.Position not in selection:
                    continue
                if len(targetTileEntityIDs
                       ) and tileEntity.id not in targetTileEntityIDs:
                    continue

                if not searchNames and not searchValues:
                    # Finding tile entities only
                    self.resultsModel.addEntry(
                        tagName="id",
                        value=tileEntity.id,
                        id=tileEntity.id,
                        path=[],
                        position=tileEntity.Position,
                        uuid=None,
                        resultType=NBTResultsEntry.TileEntityResult)
                    continue

                tag = tileEntity.raw_tag()
                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(
                            tagName=name,
                            value=value,
                            id=tileEntity.id,
                            path=path,
                            position=tileEntity.Position,
                            uuid=None,
                            resultType=NBTResultsEntry.TileEntityResult)

        def _find():
            self.resultsDockWidget.show()
            self.resultsModel.clear()
            self.dialog.accept()
            self.resultsWidget.findAgainButton.setEnabled(False)

            self.resultsWidget.progressBar.setMaximum(selection.chunkCount - 1)
            for i, cPos in enumerate(selection.chunkPositions()):
                if dim.containsChunk(*cPos):
                    chunk = dim.getChunk(*cPos)
                    if searchEntities:
                        _findEntitiesInChunk(chunk)
                    if searchTileEntities:
                        _findTileEntitiesInChunk(chunk)

                    yield
                self.resultsWidget.progressBar.setValue(i)

            self.stop()

        finder = _find()

        def find():
            try:
                finder.next()
            except StopIteration:
                pass

        self.findTimer = QtCore.QTimer(timeout=find, interval=1.0)
        self.findTimer.start()
        self.resultsWidget.stopButton.setEnabled(True)

    def stop(self):
        if self.findTimer:
            self.findTimer.stop()
        self.widget.findButton.setEnabled(True)
        self.resultsWidget.stopButton.setEnabled(False)
        self.resultsWidget.findAgainButton.setEnabled(True)

    def replaceEntries(self, entries):
        shouldReplaceName = self.widget.replaceNameCheckbox.isChecked()
        newName = self.widget.replaceNameField.text()
        shouldReplaceValue = self.widget.replaceValueCheckbox.isChecked()
        newValue = self.widget.replaceValueField.text()

        # newTagType = self.widget.replaceTagTypeComboBox.currentIndex()

        def _replaceInTag(result, tag):
            for component in result.path:
                tag = tag[component]

            if shouldReplaceName:
                subtag = tag.pop(result.tagName)
                tag[newName] = subtag
                result.setTagName(newName)

            if shouldReplaceValue:
                subtag = tag[result.tagName]
                # xxx newTagType
                if subtag.tagID in (nbt.ID_BYTE, nbt.ID_SHORT, nbt.ID_INT,
                                    nbt.ID_LONG):
                    try:
                        value = int(newValue)
                    except ValueError:
                        log.warn(
                            "Could not assign value %s to tag %s (could not convert to int)",
                            newValue, subtag)
                        return
                elif subtag.tagID in (nbt.ID_FLOAT, nbt.ID_DOUBLE):
                    try:
                        value = float(newValue)
                    except ValueError:
                        log.warn(
                            "Could not assign value %s to tag %s (could not convert to float)",
                            newValue, subtag)
                        return
                else:
                    value = newValue
                subtag.value = value

        def _replace():
            for result in entries:
                if result.resultType == result.TileEntityResult:
                    tileEntity = self.editorSession.currentDimension.getTileEntity(
                        result.position)
                    if tileEntity:
                        tag = tileEntity.raw_tag()
                        _replaceInTag(result, tag)
                        tileEntity.dirty()

                if result.resultType == result.EntityResult:
                    entity = result.getEntity(
                        self.editorSession.currentDimension
                    )  # xxx put dimension in result!!!!
                    if entity:
                        tag = entity.raw_tag()
                        _replaceInTag(result, tag)
                        entity.dirty()

                # if result.resultType == result.ItemResult:  # xxx
                yield

        command = NBTReplaceCommand(self.editorSession,
                                    "Replace NBT data")  # xxx replace details
        with command.begin():
            replacer = _replace()
            showProgress("Replacing NBT data...", replacer)

        self.editorSession.pushCommand(command)

    def replaceAll(self):
        self.replaceEntries(self.resultsModel.results)

    def replaceSelected(self):
        pass
Esempio n. 10
0
class FindReplaceNBT(QtGui.QWidget, Ui_findNBTWidget):
    def __init__(self, editorSession, dialog):
        super(FindReplaceNBT, self).__init__()
        self.editorSession = editorSession
        self.setupUi(self)
        self.dialog = dialog

        self.resultsWidget = FindReplaceNBTResults()
        self.resultsWidget.setupUi(self.resultsWidget)

        self.resultsDockWidget = MCEDockWidget("NBT Search", objectName="nbtSearch")
        self.resultsDockWidget.setWidget(self.resultsWidget)
        self.resultsDockWidget.hide()

        self.resultsModel = NBTResultsModel()
        self.resultsWidget.resultsView.setModel(self.resultsModel)
        self.resultsWidget.resultsView.clicked.connect(self.resultsViewIndexClicked)

        # --- Buttons ---
        self.findButton.clicked.connect(self.find)

        self.resultsWidget.stopButton.clicked.connect(self.stop)
        self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)

        self.resultsWidget.replaceSelectedButton.clicked.connect(self.replaceSelected)
        self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)

        self.resultsWidget.removeSelectedButton.clicked.connect(self.removeSelected)
        self.resultsWidget.removeAllButton.clicked.connect(self.removeAll)

        self.resultsWidget.replaceSelectedButton.setEnabled(not self.editorSession.readonly)
        self.resultsWidget.replaceAllButton.setEnabled(not self.editorSession.readonly)

        self.resultsWidget.removeSelectedButton.setEnabled(not self.editorSession.readonly)
        self.resultsWidget.removeAllButton.setEnabled(not self.editorSession.readonly)

        self.searchNameCheckbox.toggled.connect(self.searchForToggled)
        self.searchValueCheckbox.toggled.connect(self.searchForToggled)
        self.findTimer = None
        self.finder = None

        # --- Search for... ---
        self.nameField.setText(nbtReplaceSettings.nameField.value(""))
        self.searchNameCheckbox.setChecked(len(self.nameField.text()) > 0)
        self.nameField.textChanged.connect(self.nameFieldChanged)

        self.valueField.setText(nbtReplaceSettings.valueField.value(""))
        self.searchValueCheckbox.setChecked(len(self.valueField.text()) > 0)
        self.valueField.textChanged.connect(self.valueFieldChanged)

        # --- Search in... ---
        self.searchEntitiesCheckbox.setChecked(nbtReplaceSettings.searchEntitiesChecked.value())
        self.searchEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchEntitiesChecked.setValue)

        self.entityIDField.setText(nbtReplaceSettings.entityIDField.value())
        self.entityIDField.textChanged.connect(self.entityIDFieldChanged)

        self.searchTileEntitiesCheckbox.setChecked(nbtReplaceSettings.searchTileEntitiesChecked.value())
        self.searchTileEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchTileEntitiesChecked.setValue)

        self.tileEntityIDField.setText(nbtReplaceSettings.tileEntityIDField.value())
        self.tileEntityIDField.textChanged.connect(self.tileEntityIDFieldChanged)

        self.searchPlayersCheckbox.setChecked(nbtReplaceSettings.searchPlayersChecked.value())
        self.searchPlayersCheckbox.toggled.connect(nbtReplaceSettings.searchPlayersChecked.setValue)

        self.searchChunksCheckbox.setChecked(nbtReplaceSettings.searchChunksChecked.value())
        self.searchChunksCheckbox.toggled.connect(nbtReplaceSettings.searchChunksChecked.setValue)

        # --- Replace with... ---
        self.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
        self.resultsWidget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())

        self.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)
        self.resultsWidget.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)

        self.replaceNameCheckbox.setChecked(len(self.replaceNameField.text()))
        self.resultsWidget.replaceNameCheckbox.setChecked(len(self.replaceNameField.text()))

        self.replaceNameCheckbox.toggled.connect(self.replaceNameToggled)
        self.resultsWidget.replaceNameCheckbox.toggled.connect(self.replaceNameToggled)

        self.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
        self.resultsWidget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())

        self.replaceValueField.textChanged.connect(self.replaceValueFieldChanged)
        self.resultsWidget.replaceValueField.textChanged.connect(self.replaceValueFieldChanged)

        self.replaceValueCheckbox.setChecked(len(self.replaceValueField.text()))
        self.resultsWidget.replaceValueCheckbox.setChecked(len(self.replaceValueField.text()))

        self.replaceValueCheckbox.toggled.connect(self.replaceValueToggled)
        self.resultsWidget.replaceValueCheckbox.toggled.connect(self.replaceValueToggled)

        self.replaceValueTagTypeComboBox.setCurrentIndex(nbtReplaceSettings.replaceValueTagType.value())
        self.replaceValueTagTypeComboBox.currentIndexChanged[int].connect(self.valueTagTypeChanged)

    def dialogOpened(self):
        currentSelection = self.editorSession.currentSelection
        self.inSelectionCheckbox.setChecked(currentSelection is not None and currentSelection.volume > 0)

    def searchForToggled(self):
        #canSearch = self.searchNameCheckbox.isChecked() or self.searchValueCheckbox.isChecked()
        #self.findButton.setEnabled(canSearch)
        pass

    def nameFieldChanged(self, value):
        nbtReplaceSettings.nameField.setValue(value)
        self.searchNameCheckbox.setChecked(len(value) > 0)

    def valueFieldChanged(self, value):
        nbtReplaceSettings.valueField.setValue(value)
        self.searchValueCheckbox.setChecked(len(value) > 0)

    def entityIDFieldChanged(self, value):
        nbtReplaceSettings.entityIDField.setValue(value)
        if len(value):
            self.searchEntitiesCheckbox.setChecked(True)

    def tileEntityIDFieldChanged(self, value):
        nbtReplaceSettings.tileEntityIDField.setValue(value)
        if len(value):
            self.searchTileEntitiesCheckbox.setChecked(True)

    def replaceNameFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceNameField.value():
            nbtReplaceSettings.replaceNameField.setValue(value)

            self.resultsWidget.replaceNameCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceNameField.setText(value)

            self.replaceNameCheckbox.setChecked(len(value) > 0)
            self.replaceNameField.setText(value)

    def replaceValueFieldChanged(self, value):
        if value != nbtReplaceSettings.replaceValueField.value():
            nbtReplaceSettings.replaceValueField.setValue(value)

            self.resultsWidget.replaceValueCheckbox.setChecked(len(value) > 0)
            self.resultsWidget.replaceValueField.setText(value)

            self.replaceValueCheckbox.setChecked(len(value) > 0)
            self.replaceValueField.setText(value)

    def replaceNameToggled(self, enabled):
        if enabled != nbtReplaceSettings.replaceNameEnabled.Name():
            nbtReplaceSettings.replaceNameEnabled.setName(enabled)

            self.resultsWidget.replaceNameCheckbox.setChecked(enabled)
            self.replaceNameCheckbox.setChecked(enabled)

    def replaceValueToggled(self, enabled):
        if enabled != nbtReplaceSettings.replaceValueEnabled.value():
            nbtReplaceSettings.replaceValueEnabled.setValue(enabled)

            self.resultsWidget.replaceValueCheckbox.setChecked(enabled)
            self.replaceValueCheckbox.setChecked(enabled)

    def valueTagTypeChanged(self, index):
        if index != nbtReplaceSettings.replaceValueTagType.value():
            nbtReplaceSettings.replaceValueTagType.setValue(index)
            self.replaceValueTagTypeComboBox.setCurrentIndex(index)
            self.replaceValueTagTypeComboBox.setCurrentIndex(index)

    def resultsViewIndexClicked(self, modelIndex):
        row = modelIndex.row()
        result = self.resultsModel.results[row]

        if result.resultType == result.EntityResult:
            entityPtr = result.getEntityPtr()
            self.editorSession.zoomAndInspectEntity(entityPtr)

        if result.resultType == result.TileEntityResult:
            self.editorSession.zoomAndInspectBlock(result.position)

        if result.resultType == result.ChunkResult:
            self.editorSession.zoomAndInspectChunk(result.position)

    def find(self):
        searchNames = self.searchNameCheckbox.isChecked()
        targetName = self.nameField.text()
        searchValues = self.searchValueCheckbox.isChecked()
        targetValue = self.valueField.text()

        searchEntities = self.searchEntitiesCheckbox.isChecked()
        targetEntityIDs = self.entityIDField.text()
        if len(targetEntityIDs):
            targetEntityIDs = targetEntityIDs.split(';')

        searchTileEntities = self.searchTileEntitiesCheckbox.isChecked()
        targetTileEntityIDs = self.tileEntityIDField.text()
        if len(targetTileEntityIDs):
            targetTileEntityIDs = targetTileEntityIDs.split(';')

        searchChunks = self.searchChunksCheckbox.isChecked()

        if not any((searchNames, searchValues, searchEntities, searchTileEntities, searchChunks)):
            # Nothing to find
            return

        dim = self.editorSession.currentDimension
        inSelection = self.inSelectionCheckbox.isChecked()
        if inSelection:
            selection = self.editorSession.currentSelection
            if selection is None:
                return
        else:
            selection = dim.bounds

        def _matchTag(name_or_index, tag):
            ok = True
            if searchValues and not tag.isCompound() and not tag.isList():
                if not (tag.tagID == nbt.ID_STRING and targetValue in tag.value):
                    ok = False
                elif not (targetValue == tag.value):
                    ok = False
            if searchNames:
                if not isinstance(name_or_index, basestring):
                    ok = False
                elif (targetName == name_or_index):
                    ok = True
                else:
                    ok = False
            return ok

        def _findTag(name_or_index, tag, path):
            if _matchTag(name_or_index, tag):
                if tag.isCompound():
                    value = "Compound"  # describeCompound
                elif tag.isList():
                    value = "List"  # describeList
                else:
                    value = unicode(tag.value)

                return str(name_or_index), path, value

        def _findEntitiesInChunk(chunk):
            for entity in chunk.Entities:
                if entity.Position not in selection:
                    continue
                if len(targetEntityIDs) and entity.id not in targetEntityIDs:
                    continue

                try:
                    uuid = entity.UUID
                except KeyError:
                    uuid = None  # Don't want to use find/replace on entities without UUIDs

                if not searchNames and not searchValues:
                    # Finding entities only
                    self.resultsModel.addEntry(tagName="id",
                                               value=entity.id,
                                               ID=entity.id,
                                               path=[],
                                               position=entity.Position,
                                               uuid=uuid,
                                               resultType=NBTResultsEntry.EntityResult,
                                               dimension=self.editorSession.currentDimension)
                    continue

                tag = entity.raw_tag()

                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(tagName=name,
                                                   value=value,
                                                   ID=entity.id,
                                                   path=path,
                                                   position=entity.Position,
                                                   uuid=uuid,
                                                   resultType=NBTResultsEntry.EntityResult,
                                                   dimension=self.editorSession.currentDimension)

        def _findTileEntitiesInChunk(chunk):
            for tileEntity in chunk.TileEntities:
                if tileEntity.Position not in selection:
                    continue
                if len(targetTileEntityIDs) and tileEntity.id not in targetTileEntityIDs:
                    continue

                if not searchNames and not searchValues:
                    # Finding tile entities only
                    self.resultsModel.addEntry(tagName="id",
                                               value=tileEntity.id,
                                               ID=tileEntity.id,
                                               path=[],
                                               position=tileEntity.Position,
                                               uuid=None,
                                               resultType=NBTResultsEntry.TileEntityResult,
                                               dimension=self.editorSession.currentDimension)
                    continue

                tag = tileEntity.raw_tag()
                for name, subtag, path in nbt.walk(tag):
                    result = _findTag(name, subtag, path)
                    if result:
                        name, path, value = result

                        self.resultsModel.addEntry(tagName=name,
                                                   value=value,
                                                   ID=tileEntity.id,
                                                   path=path,
                                                   position=tileEntity.Position,
                                                   uuid=None,
                                                   resultType=NBTResultsEntry.TileEntityResult,
                                                   dimension=self.editorSession.currentDimension)

        def _findInChunk(chunk):
            tag = chunk.rootTag
            for name, subtag, path in nbt.walk(tag):
                if len(path) >= 2 and path[0] == "Level":
                    if path[1] in ("Entities", "TileEntities"):
                        continue

                result = _findTag(name, subtag, path)

                if result:
                    name, path, value = result

                    self.resultsModel.addEntry(tagName=name,
                                               value=value,
                                               ID="chunk",
                                               path=path,
                                               position=chunk.chunkPosition,
                                               uuid=None,
                                               resultType=NBTResultsEntry.ChunkResult,
                                               dimension=self.editorSession.currentDimension)

        def _find():
            self.resultsDockWidget.show()
            self.resultsModel.clear()
            self.dialog.accept()
            self.resultsWidget.findAgainButton.setEnabled(False)

            self.resultsWidget.progressBar.setMaximum(selection.chunkCount-1)
            for i, cPos in enumerate(selection.chunkPositions()):
                if dim.containsChunk(*cPos):
                    chunk = dim.getChunk(*cPos)
                    if searchEntities:
                        _findEntitiesInChunk(chunk)
                    if searchTileEntities:
                        _findTileEntitiesInChunk(chunk)
                    if searchChunks:
                        _findInChunk(chunk)

                    yield
                self.resultsWidget.progressBar.setValue(i)

            self.stop()

        finder = _find()

        def find():
            try:
                finder.next()
            except StopIteration:
                pass

        self.findTimer = QtCore.QTimer(timeout=find, interval=1.0)
        self.findTimer.start()
        self.resultsWidget.stopButton.setEnabled(True)

    def stop(self):
        if self.findTimer:
            self.findTimer.stop()
        self.findButton.setEnabled(True)
        self.resultsWidget.stopButton.setEnabled(False)
        self.resultsWidget.findAgainButton.setEnabled(True)

    def replaceEntries(self, entries):
        shouldReplaceName = nbtReplaceSettings.replaceNameEnabled.value()
        newName = nbtReplaceSettings.replaceNameField.value()
        shouldReplaceValue = nbtReplaceSettings.replaceValueEnabled.value()
        newValue = nbtReplaceSettings.replaceValueField.value()
        # newTagType = self.replaceTagTypeComboBox.currentIndex()

        def _replaceInTag(result, tag):
            for component in result.path:
                tag = tag[component]

            if shouldReplaceName:
                subtag = tag.pop(result.tagName)
                tag[newName] = subtag
                result.setTagName(newName)

            if shouldReplaceValue:
                subtag = tag[result.tagName]
                # xxx newTagType
                if subtag.tagID in (nbt.ID_BYTE, nbt.ID_SHORT, nbt.ID_INT, nbt.ID_LONG):
                    try:
                        value = int(newValue)
                    except ValueError:
                        log.warn("Could not assign value %s to tag %s (could not convert to int)", newValue, subtag)
                        return
                elif subtag.tagID in (nbt.ID_FLOAT, nbt.ID_DOUBLE):
                    try:
                        value = float(newValue)
                    except ValueError:
                        log.warn("Could not assign value %s to tag %s (could not convert to float)", newValue, subtag)
                        return
                else:
                    value = newValue
                subtag.value = value
                result.value = value

        def _replace():
            for result in entries:
                ref = result.getTargetRef()

                tag = result.getTargetTag()
                _replaceInTag(result, tag)
                ref.dirty = True

                yield

        with self.editorSession.beginSimpleCommand(self.tr("Replace NBT data")):
            showProgress("Replacing NBT data...", _replace())

    def replaceAll(self):
        self.replaceEntries(self.resultsModel.results)

    def replaceSelected(self):
        entries = []
        for index in self.resultsWidget.resultsView.selectedIndexes():
            entries.append(self.resultsModel.data(index, role=Qt.UserRole))
            
        self.replaceEntries(entries)

    def removeEntries(self, entries):
        def _remove():
            for result in entries:
                ref = result.getTargetRef()
                tag = result.getTargetTag()
                
                for component in result.path[:-1]:
                    tag = tag[component]
                
                del tag[result.tagName]
                ref.dirty = True
                
                yield
            
            self.resultsModel.removeEntries(entries)

        with self.editorSession.beginSimpleCommand(self.tr("Remove NBT tags")):
            showProgress("Removing NBT tags...", _remove())

    def removeAll(self):
        self.removeEntries(self.resultsModel.results)

    def removeSelected(self):
        entries = []
        for index in self.resultsWidget.resultsView.selectedIndexes():
            entries.append(self.resultsModel.data(index, role=Qt.UserRole))
            
        self.removeEntries(entries)