Example #1
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
Example #2
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
Example #3
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)
Example #4
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)