Mercurial > fife-parpg
view clients/editor/plugins/LayerTool.py @ 321:7ddec4ce99b3
Fixed a crash when removing the layer which the editor camera uses
author | cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 16 Aug 2009 21:35:15 +0000 |
parents | 62ec3b530cc6 |
children | 3853f8659598 |
line wrap: on
line source
# coding: utf-8 # ################################################### # Copyright (C) 2008 The Zero-Projekt team # http://zero-projekt.net # info@zero-projekt.net # This file is part of Zero "Was vom Morgen blieb" # # The Zero-Projekt codebase is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ################################################### """ An advanced layer tool for FIFedit """ import fife import scripts.plugin as plugin import scripts.editor from scripts.events import * from scripts.gui.action import Action import pychan import pychan.widgets as widgets from pychan.tools import callbackWithArguments as cbwa # default should be pychan default, highlight can be choosen (format: r,g,b) _DEFAULT_BACKGROUND_COLOR = pychan.internal.DEFAULT_STYLE['default']['base_color'] _HIGHLIGHT_BACKGROUND_COLOR = pychan.internal.DEFAULT_STYLE['default']['selection_color'] # the dynamicly created widgets have the name scheme prefix + layerid _LABEL_NAME_PREFIX = "select_" class LayerTool(plugin.Plugin): """ The B{LayerTool} is an advanced method to view and change layer informations. While the original FIFedit tool only allows to select layers, this one will provide the following functionality: - toggle layer visibility - select layer - list layers """ def __init__(self): self._editor = None self._enabled = False self._mapview = None self._showAction = None self.subwrappers = [] self._layer_wizard = None #--- Plugin function ---# def enable(self): """ """ if self._enabled is True: return # Fifedit plugin data self._editor = scripts.editor.getEditor() self._showAction = Action(u"LayerTool", checkable=True) scripts.gui.action.activated.connect(self.toggle, sender=self._showAction) self._editor._toolsMenu.addAction(self._showAction) self.__create_gui() self.toggle() events.postMapShown.connect(self.update) def disable(self): """ """ if self._enabled is False: return self.container.setDocked(False) self.container.hide() events.postMapShown.disconnect(self.update) self._editor._toolsMenu.removeAction(self._showAction) def isEnabled(self): """ """ return self._enabled; def getName(self): """ """ return u"Layertool" #--- End plugin functions ---# def __create_gui(self): """ create the basic gui container """ self.container = pychan.loadXML('gui/layertool.xml') self.wrapper = self.container.findChild(name="layers_wrapper") self.removeLayerButton = self.container.findChild(name="remove_layer_button") self.createLayerButton = self.container.findChild(name="add_layer_button") self.editLayerButton = self.container.findChild(name="edit_layer_button") self.removeLayerButton.capture(self.removeSelectedLayer) self.createLayerButton.capture(self.showLayerWizard) self.editLayerButton.capture(self.showEditDialog) self.update(None) def _adjust_position(self): """ adjusts the position of the container - we don't want to let the window appear at the center of the screen. (new default position: left, beneath the tools window) """ self.container.position = (50, 200) def removeSelectedLayer(self): if not self._mapview: return if self._mapview.getMap().getNumLayers() <= 1: print "Can't remove the last layer" return layer = self.getActiveLayer() if not layer: return self.select_no_layer() map = self._mapview.getMap() # FIFE will crash if we try to delete the layer which is in use by a camera # so we will set the camera to another layer instead for cam in self._editor.getEngine().getView().getCameras(): if cam.getLocationRef().getMap().getId() == map.getId(): if cam.getLocation().getLayer().getId() == layer.getId(): for l in map.getLayers(): if l.getId() == layer.getId(): continue cam.getLocationRef().setLayer(l) break map.deleteLayer(layer) self.update(self._mapview) def showLayerWizard(self): if not self._mapview: return if self._layer_wizard: self._layer_wizard._widget.hide() self._layer_wizard = LayerEditor(self._editor.getEngine(), self._mapview.getMap(), callback=cbwa(self.update, self._mapview)) def showEditDialog(self): if not self._mapview: return layer = self.getActiveLayer() if not layer: return if self._layer_wizard: self._layer_wizard._widget.hide() self._layer_wizard = LayerEditor(self._editor.getEngine(), self._mapview.getMap(), layer=layer, callback=cbwa(self.update, self._mapview)) def clear(self): """ remove all subwrappers """ if self.subwrappers is []: return for subwrapper in self.subwrappers: self.wrapper.removeChild(subwrapper) self.subwrappers = [] def update(self, mapview): """ Dump new layer informations into the wrapper We group one ToggleButton and one Label into a HBox, the main wrapper itself is a VBox and we also capture both the Button and the Label to listen for mouse actions """ layers = [] self._mapview = mapview if self._mapview is not None: layers = self._mapview.getMap().getLayers() self.clear() if len(layers) <= 0: if not self._mapview: layerid = "No map is open" else: layerid = "No layers" subwrapper = pychan.widgets.HBox() layerLabel = pychan.widgets.Label() layerLabel.text = unicode(layerid) layerLabel.name = _LABEL_NAME_PREFIX + layerid subwrapper.addChild(layerLabel) self.wrapper.addChild(subwrapper) self.subwrappers.append(subwrapper) active_layer = self.getActiveLayer() if active_layer: active_layer = active_layer.getId() for layer in reversed(layers): layerid = layer.getId() subwrapper = pychan.widgets.HBox() toggleVisibleButton = pychan.widgets.ToggleButton(hexpand=0, up_image="gui/icons/is_visible.png", down_image="gui/icons/is_visible.png", hover_image="gui/icons/is_visible.png") toggleVisibleButton.name = "toggle_" + layerid if layer.areInstancesVisible(): toggleVisibleButton.toggled = True toggleVisibleButton.capture(self.toggle_layer_visibility) layerLabel = pychan.widgets.Label() layerLabel.text = unicode(layerid) layerLabel.name = _LABEL_NAME_PREFIX + layerid layerLabel.capture(self.select_active_layer,"mousePressed") if active_layer == layerid: layerLabel.background_color = _HIGHLIGHT_BACKGROUND_COLOR layerLabel.foreground_color = _HIGHLIGHT_BACKGROUND_COLOR layerLabel.base_color = _HIGHLIGHT_BACKGROUND_COLOR subwrapper.addChild(toggleVisibleButton) subwrapper.addChild(layerLabel) self.wrapper.addChild(subwrapper) self.subwrappers.append(subwrapper) self.container.adaptLayout(False) def toggle_layer_visibility(self, event, widget): """ Callback for ToggleButtons Toggle the chosen layer visible / invisible NOTE: - if a layer is set to invisible, it also shouldn't be the active layer anymore @type event: object @param event: pychan mouse event @type widget: object @param widget: the pychan widget where the event occurs, transports the layer id in it's name """ layerid = widget.name[len(_LABEL_NAME_PREFIX):] layer = self._mapview.getMap().getLayer(layerid) active_layer = self.getActiveLayer() if active_layer: active_layer = active_layer.getId() if layer.areInstancesVisible(): layer.setInstancesVisible(False) else: layer.setInstancesVisible(True) if active_layer == layerid: self.select_no_layer() def select_no_layer(self): """ Resets the current active layer (widget + editor) """ previous_active_layer = self.getActiveLayer() if previous_active_layer is not None: previous_layer_id = previous_active_layer.getId() previous_active_widget = self.container.findChild(name=_LABEL_NAME_PREFIX + previous_layer_id) previous_active_widget.background_color = _DEFAULT_BACKGROUND_COLOR previous_active_widget.foreground_color = _DEFAULT_BACKGROUND_COLOR previous_active_widget.base_color = _DEFAULT_BACKGROUND_COLOR previous_active_widget.text = unicode(previous_layer_id) self._mapview.getController().selectLayer(None) def getActiveLayer(self): """ Returns the active layer """ if self._mapview: return self._mapview.getController()._layer def select_active_layer(self, event, widget): """ callback for Labels We hand the layerid over to the mapeditor module to select a new active layer Additionally, we mark the active layer widget (changing base color) and reseting the previous one @type event: object @param event: pychan mouse event @type widget: object @param widget: the pychan widget where the event occurs, transports the layer id in it's name """ self.select_no_layer() layerid = widget.name[len(_LABEL_NAME_PREFIX):] widget.background_color = _HIGHLIGHT_BACKGROUND_COLOR widget.foreground_color = _HIGHLIGHT_BACKGROUND_COLOR widget.base_color = _HIGHLIGHT_BACKGROUND_COLOR self.container.adaptLayout(False) self._mapview.getController().selectLayer(layerid) def toggle(self): """ toggles the layertool visible / invisible and sets dock status """ if self.container.isVisible() or self.container.isDocked(): self.container.setDocked(False) self.container.hide() self._showAction.setChecked(False) else: self.container.show() self._showAction.setChecked(True) self._adjust_position() class LayerEditor(object): """ LayerEditor provides a gui dialog for creating and editing layers. """ def __init__(self, engine, map, callback=None, onCancel=None, layer=None): self.engine = engine self.model = engine.getModel() self.map = map self.layer = layer self.callback = callback self.onCancel = onCancel self._widget = pychan.loadXML('gui/layereditor.xml') # TODO: Add access method for adopted grid types? self._widget.findChild(name="gridBox").items = ['square']#, 'hex'] # Hex does not work? # TODO: Ditto for pather? self._widget.findChild(name="pathingBox").items = ['cell_edges_only', 'cell_edges_and_diagonals', 'freeform'] if layer: cg = layer.getCellGrid() cgtype = 0 if cg.getType() == 'hex': cgtype = 1 self._widget.distributeData({ "layerBox" : unicode(layer.getId()), "xScaleBox" : unicode(cg.getXScale()), "yScaleBox" : unicode(cg.getYScale()), "rotBox" : unicode(cg.getRotation()), "xOffsetBox" : unicode(cg.getXShift()), "yOffsetBox" : unicode(cg.getYShift()) }) self._widget.findChild(name="pathingBox").selected = int(layer.getPathingStrategy()) self._widget.findChild(name="gridBox").selected = int(cgtype) else: self._widget.findChild(name="pathingBox").selected = 0 self._widget.findChild(name="gridBox").selected = 0 self._widget.mapEvents({ 'okButton' : self._finished, 'cancelButton' : self._cancelled }) self._widget.show() def _cancelled(self): if self.onCancel: self.onCancel() self._widget.hide() def _finished(self): # Collect and validate data layerId = self._widget.collectData('layerBox') if layerId == '': print 'Please enter a layer id.' return try: x_offset = float(self._widget.collectData('xOffsetBox')) y_offset = float(self._widget.collectData('yOffsetBox')) except ValueError: print 'Please enter integer or decimal values for offset.' return try: x_scale = float(self._widget.collectData('xScaleBox')) y_scale = float(self._widget.collectData('yScaleBox')) except ValueError: print 'Please enter integer or decimal values for scale.' return try: rotation = float(self._widget.collectData('rotBox')) except ValueError: print 'Please enter integer or decimal value for rotation.' return grid_type = int(self._widget.collectData('gridBox')) pathing = int(self._widget.collectData('pathingBox')) if grid_type == 0: grid_type = "square" else: grid_type = "hex" # Set up layer layer = self.layer cellgrid = None if not self.layer: # TODO: FIFE currently does not support setting layer ID and cellgrid after the layer has been created cellgrid = self.model.getCellGrid(grid_type) if not cellgrid: print "Invalid grid type" return cellgrid.setRotation(rotation) cellgrid.setXScale(x_scale) cellgrid.setYScale(y_scale) cellgrid.setXShift(x_offset) cellgrid.setYShift(y_offset) try: layer = self.map.createLayer(str(layerId), cellgrid) except: print 'The layer ' + str(layerId) + ' already exists!' return else: cellgrid = layer.getCellGrid() cellgrid.setRotation(rotation) cellgrid.setXScale(x_scale) cellgrid.setYScale(y_scale) cellgrid.setXShift(x_offset) cellgrid.setYShift(y_offset) layer.setPathingStrategy(pathing) # Hide dialog and call back self._widget.hide() if self.callback: self.callback()