comparison tools/editor/scripts/gui/mapeditor.py @ 378:64738befdf3b

bringing in the changes from the build_system_rework branch in preparation for the 0.3.0 release. This commit will require the Jan2010 devkit. Clients will also need to be modified to the new way to import fife.
author vtchill@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 11 Jan 2010 23:34:52 +0000
parents
children 70ba57cd9d18
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
1 # -*- coding: utf-8 -*-
2
3 # ####################################################################
4 # Copyright (C) 2005-2009 by the FIFE team
5 # http://www.fifengine.de
6 # This file is part of FIFE.
7 #
8 # FIFE is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the
20 # Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 # ####################################################################
23
24 # MapEditor is a plugin for Fifedit. It allows for selection and visual editing of maps.
25
26 import math, os, time
27 from datetime import date
28
29 from fife import fife
30 from fife.extensions import pychan
31 import fife.extensions.pychan.widgets as widgets
32 import scripts
33 import scripts.events as events
34 import action
35 from toolbar import ToolBar
36 from menubar import Menu, MenuBar
37 from action import Action, ActionGroup
38 from scripts.mapcontroller import MapController
39 from fife.extensions import fife_utils
40
41 states = (u'SELECTING', u'INSERTING', u'REMOVING', u'MOVING', u'OBJECTPICKER')
42 for s in states:
43 globals()[s] = s
44 NOT_INITIALIZED = -9999999
45
46 class MapEditor:
47 def __init__(self):
48 self._ignoreToggles = False # A hack to avoid infinite recursion when toggling a button
49 self._controller = None
50 self._mode = SELECTING
51
52 self._editor = scripts.editor.getEditor()
53 self._eventlistener = self._editor.getEventListener()
54 self._statusbar = self._editor.getStatusBar()
55 self._toolbar = self._editor.getToolBar()
56 self._object = None
57 self._startDragPos = None
58 self._lastDragPos = None
59 self._lastDragPosExact = None
60
61 self._toolbox = self._editor.getToolbox()
62
63 self._initToolbuttons()
64
65 self._toolbox.show()
66 self._instances = []
67
68 events.postMapShown.connect(self._mapChanged)
69 events.preMapClosed.connect(self._mapClosed)
70 events.onObjectSelected.connect(self.setObject)
71 self._undogroup = False
72
73 self._scrollX = 0
74 self._scrollY = 0
75
76 def _init(self):
77 self._dragx = NOT_INITIALIZED
78 self._dragy = NOT_INITIALIZED
79
80 self._setMode(SELECTING)
81
82 self._initToolbarbuttons()
83
84 events.keyPressed.connect(self.keyPressed)
85 events.keyReleased.connect(self.keyReleased)
86
87 events.mousePressed.connect(self.mousePressed)
88 events.mouseDragged.connect(self.mouseDragged)
89 events.mouseReleased.connect(self.mouseReleased)
90 events.mouseMoved.connect(self.mouseMoved)
91 events.mouseEntered.connect(self.mouseEntered)
92 events.mouseExited.connect(self.mouseExited)
93 events.mouseWheelMovedUp.connect(self.mouseWheelMovedUp)
94 events.mouseWheelMovedDown.connect(self.mouseWheelMovedDown)
95 events.onPump.connect(self.pump)
96
97 def _clear(self):
98 self._clearToolbarButtons()
99
100 events.keyPressed.disconnect(self.keyPressed)
101 events.keyReleased.disconnect(self.keyReleased)
102
103 events.mousePressed.disconnect(self.mousePressed)
104 events.mouseDragged.disconnect(self.mouseDragged)
105 events.mouseReleased.disconnect(self.mouseReleased)
106 events.mouseMoved.disconnect(self.mouseMoved)
107 events.mouseExited.disconnect(self.mouseExited)
108 events.mouseEntered.disconnect(self.mouseEntered)
109 events.mouseWheelMovedUp.disconnect(self.mouseWheelMovedUp)
110 events.mouseWheelMovedDown.disconnect(self.mouseWheelMovedDown)
111 events.onPump.disconnect(self.pump)
112
113 def _mapChanged(self, sender, mapview):
114 self.setController(mapview.getController())
115
116 def _mapClosed(self, sender, mapview):
117 self.setController(None)
118
119 def _setCursor(self):
120 engine = self._editor.getEngine()
121 cursor = engine.getCursor()
122
123 id = -1
124 if self._mode == SELECTING:
125 id = engine.getImagePool().addResourceFromFile("gui/icons/select_instance.png")
126 image = engine.getImagePool().getImage(id)
127 image.setXShift(-7)
128 image.setYShift(-7)
129 elif self._mode == INSERTING:
130 id = engine.getImagePool().addResourceFromFile("gui/icons/add_instance.png")
131 image = engine.getImagePool().getImage(id)
132 image.setXShift(-2)
133 image.setYShift(-20)
134 elif self._mode == REMOVING:
135 id = engine.getImagePool().addResourceFromFile("gui/icons/erase_instance.png")
136 image = engine.getImagePool().getImage(id)
137 image.setXShift(-2)
138 image.setYShift(-19)
139 elif self._mode == MOVING:
140 id = engine.getImagePool().addResourceFromFile("gui/icons/move_instance.png")
141 image = engine.getImagePool().getImage(id)
142 image.setXShift(-11)
143 image.setYShift(-11)
144 elif self._mode == OBJECTPICKER:
145 id = engine.getImagePool().addResourceFromFile("gui/icons/objectpicker.png")
146 image = engine.getImagePool().getImage(id)
147 image.setXShift(-0)
148 image.setYShift(-22)
149
150 if id < 0:
151 self._resetCursor()
152 else:
153 cursor.set(fife.CURSOR_IMAGE, id)
154
155 def _resetCursor(self):
156 cursor = self._editor.getEngine().getCursor()
157 cursor.set(fife.CURSOR_NATIVE, fife.NC_ARROW)
158
159 def setObject(self, object):
160 self._object = object
161
162 def setController(self, controller):
163 if self._controller is not None:
164 self._clear()
165
166 if controller is not None:
167 self._init()
168
169 self._controller = controller
170
171 def _initToolbuttons(self):
172 self._selectAction = Action(text=u"Select", icon="gui/icons/select_instance.png", checkable=True)
173 self._drawAction = Action(text=u"Draw", icon="gui/icons/add_instance.png", checkable=True)
174 self._removeAction = Action(text=u"Remove", icon="gui/icons/erase_instance.png", checkable=True)
175 self._moveAction = Action(text=u"Move", icon="gui/icons/move_instance.png", checkable=True)
176 self._objectpickerAction = Action(text=u"Pick object", icon="gui/icons/objectpicker.png", checkable=True)
177
178 self._selectAction.helptext = u"Select cells on layer (S)"
179 self._moveAction.helptext = u"Moves instances (M)"
180 self._drawAction.helptext = u"Adds new instances based on currently selected object (I)"
181 self._removeAction.helptext = u"Deletes instances (R)"
182 self._objectpickerAction.helptext = u"Click an instance to set the current object to the one used by instance"
183
184 action.toggled.connect(self._buttonToggled, sender=self._selectAction)
185 action.toggled.connect(self._buttonToggled, sender=self._moveAction)
186 action.toggled.connect(self._buttonToggled, sender=self._drawAction)
187 action.toggled.connect(self._buttonToggled, sender=self._removeAction)
188 action.toggled.connect(self._buttonToggled, sender=self._objectpickerAction)
189
190 self._toolgroup = ActionGroup(exclusive=True, name=u"Tool group")
191 self._toolgroup.addAction(self._selectAction)
192 self._toolgroup.addAction(self._moveAction)
193 self._toolgroup.addAction(self._drawAction)
194 self._toolgroup.addAction(self._removeAction)
195 self._toolgroup.addAction(self._objectpickerAction)
196
197
198 self._toolbox.addAction(self._toolgroup)
199 self._toolbox.adaptLayout()
200
201 self._editor._edit_menu.addAction(self._toolgroup)
202
203 def _initToolbarbuttons(self):
204 rotateLeftAction = Action(text=u"Rotate counterclockwise", icon="gui/icons/rotate_countercw.png")
205 rotateRightAction = Action(text=u"Rotate clockwise", icon="gui/icons/rotate_clockwise.png")
206 zoomInAction = Action(text=u"Zoom in", icon="gui/icons/zoom_in.png")
207 zoomOutAction = Action(text=u"Zoom out", icon="gui/icons/zoom_out.png")
208 zoomResetAction = Action(text=u"Reset zoom", icon="gui/icons/zoom_default.png")
209 screenshotAction = Action(text=u"Take screenshot", icon="gui/icons/take_screenshot.png")
210
211 rotateLeftAction.helptext = u"Rotate counterclockwise by 90 degrees"
212 rotateRightAction.helptext = u"Rotate clockwise by 90 degrees"
213 zoomInAction.helptext = u"Zoom in (CTRL + Mousewheel up)"
214 zoomOutAction.helptext = u"Zoom out (CTRL + Mousewheel down)"
215 zoomResetAction.helptext = u"Reset zoom to default level"
216 screenshotAction.helptext = u"Take screenshot (F7)"
217
218 action.activated.connect(self.rotateCounterClockwise, sender=rotateLeftAction)
219 action.activated.connect(self.rotateClockwise, sender=rotateRightAction)
220 action.activated.connect(self.zoomIn, sender=zoomInAction)
221 action.activated.connect(self.zoomOut, sender=zoomOutAction)
222 action.activated.connect(self.resetZoom, sender=zoomResetAction)
223 action.activated.connect(self.captureScreen, sender=screenshotAction)
224
225 self._viewGroup = ActionGroup(name=u"View group")
226 self._viewGroup.addAction(rotateLeftAction)
227 self._viewGroup.addAction(rotateRightAction)
228 self._viewGroup.addAction(zoomInAction)
229 self._viewGroup.addAction(zoomOutAction)
230 self._viewGroup.addAction(zoomResetAction)
231 self._viewGroup.addAction(screenshotAction)
232
233 self._toolbar.addAction(self._viewGroup)
234 self._toolbar.adaptLayout()
235
236 def _clearToolbarButtons(self):
237 self._toolbar.removeAction(self._viewGroup)
238 self._toolbar.adaptLayout()
239 self._viewGroup = None
240
241
242 def _setMode(self, mode):
243 if (mode == INSERTING) and (not self._object):
244 self._statusbar.setText(u'Please select object first')
245 mode = self._mode
246
247 self._ignoreToggles = True
248 # Update toolbox buttons
249 if (mode == INSERTING):
250 self._drawAction.setChecked(True)
251 elif mode == REMOVING:
252 self._removeAction.setChecked(True)
253 elif mode == MOVING:
254 self._moveAction.setChecked(True)
255 elif mode == OBJECTPICKER:
256 self._objectpickerAction.setChecked(True)
257 else:
258 self._selectAction.setChecked(True)
259 self._ignoreToggles = False
260
261 self._mode = mode
262 print "Entered mode " + mode
263 self._statusbar.setText(mode.replace('_', ' ').capitalize())
264 self._setCursor()
265
266 def _buttonToggled(self, sender, toggled):
267 if self._controller is None: return
268 if self._ignoreToggles is True: return
269
270 mode = SELECTING
271
272 if toggled:
273 if sender == self._selectAction:
274 mode = SELECTING
275 elif sender == self._moveAction:
276 mode = MOVING
277 elif sender == self._drawAction:
278 mode = INSERTING
279 elif sender == self._removeAction:
280 mode = REMOVING
281 elif sender == self._objectpickerAction:
282 mode = OBJECTPICKER
283
284 self._setMode(mode)
285
286 def mousePressed(self, sender, event):
287 if event.isConsumedByWidgets():
288 return
289
290 if not self._controller._layer:
291 if self._controller.debug: print 'No layers active. Cancelling map action'
292 return
293
294 realCoords = self._getRealCoords(sender, event)
295
296 if event.getButton() == fife.MouseEvent.MIDDLE:
297 self._dragx = realCoords[0]
298 self._dragy = realCoords[1]
299
300 else:
301 if event.getButton() == fife.MouseEvent.RIGHT:
302 self._controller.deselectSelection()
303
304 if self._mode == SELECTING:
305 if event.getButton() == fife.MouseEvent.LEFT:
306 if self._eventlistener.shiftPressed:
307 self._controller.deselectCell(realCoords[0], realCoords[1])
308 else:
309 if self._eventlistener.controlPressed is False:
310 self._controller.deselectSelection()
311 self._controller.selectCell(realCoords[0], realCoords[1])
312
313 elif event.getButton() == fife.MouseEvent.RIGHT:
314 self._controller.deselectSelection()
315
316 elif self._mode == INSERTING:
317 if event.getButton() == fife.MouseEvent.LEFT:
318 self._controller.deselectSelection()
319 self._controller.selectCell(realCoords[0], realCoords[1])
320 self._controller.getUndoManager().startGroup("Inserted instances")
321 self._undogroup = True
322
323 position = self._controller._camera.toMapCoordinates(fife.ScreenPoint(realCoords[0], realCoords[1]), False)
324 position = self._controller._layer.getCellGrid().toLayerCoordinates(position)
325
326 self._controller.selectCell(realCoords[0], realCoords[1])
327 self._controller.placeInstance(position, self._object)
328
329 elif self._mode == REMOVING:
330 if event.getButton() == fife.MouseEvent.LEFT:
331 self._controller.deselectSelection()
332 self._controller.selectCell(realCoords[0], realCoords[1])
333 self._controller.getUndoManager().startGroup("Removed instances")
334 self._undogroup = True
335
336 self._controller.removeInstances(self._controller.getInstancesFromSelection())
337
338 elif self._mode == MOVING:
339 if event.getButton() == fife.MouseEvent.LEFT:
340
341 position = self._controller._camera.toMapCoordinates(fife.ScreenPoint(realCoords[0], realCoords[1]), False)
342
343 self._lastDragPos = self._controller._layer.getCellGrid().toLayerCoordinates(position)
344 self._lastDragPosExact = self._controller._layer.getCellGrid().toExactLayerCoordinates(position)
345
346 for loc in self._controller._selection:
347 if loc.getLayerCoordinates() == self._lastDragPos:
348 break
349 else:
350 self._controller.deselectSelection()
351 self._controller.selectCell(realCoords[0], realCoords[1])
352
353 self._instances = self._controller.getInstancesFromSelection()
354
355 self._controller.getUndoManager().startGroup("Moved instances")
356 self._undogroup = True
357
358 elif self._mode == OBJECTPICKER:
359 position = self._controller._camera.toMapCoordinates(fife.ScreenPoint(realCoords[0], realCoords[1]), False)
360 exact = self._controller._layer.getCellGrid().toExactLayerCoordinates(position)
361 instances = self._controller.getInstancesFromPosition(exact)
362 if len(instances) >= 1:
363 object = instances[0].getObject()
364 if object.getId() != self._object.getId() or object.getNamespace() != self._object.getNamespace():
365 events.onObjectSelected.send(sender=self, object=object)
366
367 def mouseDragged(self, sender, event):
368 if event.isConsumedByWidgets():
369 return
370
371 if not self._controller._layer:
372 if self._controller.debug: print 'No layers active. Cancelling map action'
373 return
374
375 realCoords = self._getRealCoords(sender, event)
376
377 if event.getButton() == fife.MouseEvent.MIDDLE:
378 self._scrollX = (self._dragx-realCoords[0])/10.0
379 self._scrollY = (self._dragy-realCoords[1])/10.0
380 else:
381 if self._mode != SELECTING:
382 self._controller.deselectSelection()
383
384 if self._mode == SELECTING:
385 if event.getButton() == fife.MouseEvent.LEFT:
386 if self._eventlistener.shiftPressed:
387 self._controller.deselectCell(realCoords[0], realCoords[1])
388 else:
389 self._controller.selectCell(realCoords[0], realCoords[1])
390
391 elif self._mode == INSERTING:
392 position = self._controller._camera.toMapCoordinates(fife.ScreenPoint(realCoords[0], realCoords[1]), False)
393 position = self._controller._layer.getCellGrid().toLayerCoordinates(position)
394
395 self._controller.selectCell(realCoords[0], realCoords[1])
396 self._controller.placeInstance(position, self._object)
397
398 elif self._mode == REMOVING:
399 self._controller.selectCell(realCoords[0], realCoords[1])
400 self._controller.removeInstances(self._controller.getInstancesFromSelection())
401
402 elif self._mode == MOVING:
403 position = self._controller._camera.toMapCoordinates(fife.ScreenPoint(realCoords[0], realCoords[1]), False)
404
405 positionExact = self._controller._layer.getCellGrid().toExactLayerCoordinates(position)
406 position = self._controller._layer.getCellGrid().toLayerCoordinates(position)
407
408 if self._eventlistener.shiftPressed:
409 self._controller.moveInstances(self._instances, positionExact-self._lastDragPosExact, True)
410 else:
411 self._controller.moveInstances(self._instances, position-self._lastDragPos, False)
412 self._lastDragPos = position
413 self._lastDragPosExact = positionExact
414
415 # Update selection
416 self._controller.deselectSelection()
417
418 for i in self._instances:
419 pos = i.getLocation().getMapCoordinates()
420 pos = self._controller._camera.toScreenCoordinates(pos)
421 self._controller.selectCell(pos.x, pos.y)
422 elif self._mode == OBJECTPICKER:
423 pass
424
425 def mouseReleased(self, sender, event):
426 if event.isConsumedByWidgets():
427 return
428
429 if not self._controller._layer:
430 if self._controller.debug: print 'No layers active. Cancelling map action'
431 return
432
433 if self._mode == SELECTING or self._mode == MOVING:
434 instances = self._controller.getInstancesFromSelection()
435 if len(instances) > 0:
436 events.onInstancesSelected.send(sender=self, instances=instances)
437
438 if event.getButton() == fife.MouseEvent.MIDDLE:
439 self._scrollX = 0
440 self._scrollY = 0
441
442 realCoords = self._getRealCoords(sender, event)
443
444 if self._undogroup:
445 self._controller.getUndoManager().endGroup()
446 self._undogroup = False
447
448 self._dragx = NOT_INITIALIZED
449 self._dragy = NOT_INITIALIZED
450
451 def mouseMoved(self, sender, event):
452 pass
453
454 def mouseEntered(self, sender, event):
455 self._setCursor()
456
457 def mouseExited(self, sender, event):
458 self._resetCursor()
459
460 def mouseWheelMovedUp(self, event):
461 if self._eventlistener.controlPressed and self._controller._camera:
462 self._controller._camera.setZoom(self._controller._camera.getZoom() * 1.10)
463
464 def mouseWheelMovedDown(self, event):
465 if self._eventlistener.controlPressed and self._controller._camera:
466 self._controller._camera.setZoom(self._controller._camera.getZoom() / 1.10)
467
468
469 def keyPressed(self, event):
470 keyval = event.getKey().getValue()
471 keystr = event.getKey().getAsString().lower()
472
473 if keyval == fife.Key.LEFT:
474 self._controller.moveCamera(50, 0)
475 elif keyval == fife.Key.RIGHT:
476 self._controller.moveCamera(-50, 0)
477 elif keyval == fife.Key.UP:
478 self._controller.moveCamera(0, 50)
479 elif keyval == fife.Key.DOWN:
480 self._controller.moveCamera(0, -50)
481
482 elif keyval == fife.Key.INSERT:
483 self._controller.fillSelection(self._object)
484
485 elif keyval == fife.Key.DELETE:
486 self._controller.clearSelection()
487
488 elif keyval == fife.Key.F7:
489 self.captureScreen()
490
491 elif keystr == "s":
492 self._setMode(SELECTING)
493
494 elif keystr == "i":
495 if self._mode != INSERTING:
496 self._setMode(INSERTING)
497 else:
498 self._setMode(SELECTING)
499
500 elif keystr == "r":
501 if self._mode != REMOVING:
502 self._setMode(REMOVING)
503 else:
504 self._setMode(SELECTING)
505
506 elif keystr == 'm':
507 if self._mode != MOVING:
508 self._setMode(MOVING)
509 else:
510 self._setMode(SELECTING)
511
512 elif keystr == 't':
513 gridrenderer = self._controller._camera.getRenderer('GridRenderer')
514 gridrenderer.setEnabled(not gridrenderer.isEnabled())
515
516 elif keystr == 'b':
517 blockrenderer = self._controller._camera.getRenderer('BlockingInfoRenderer')
518 blockrenderer.setEnabled(not blockrenderer.isEnabled())
519
520 elif keystr == 'z':
521 if self._eventlistener.controlPressed:
522 if self._eventlistener.altPressed:
523 if self._eventlistener.shiftPressed:
524 self._controller.getUndoManager().previousBranch()
525 else:
526 self._controller.getUndoManager().nextBranch()
527 else:
528 if self._eventlistener.shiftPressed:
529 self._controller.redo()
530 else:
531 self._controller.undo()
532
533
534 def keyReleased(self, event):
535 pass
536
537 def zoomIn(self, zoom=1.10):
538 self._controller.setZoom(self._controller.getZoom()*zoom)
539
540 def zoomOut(self, zoom=1.10):
541 self._controller.setZoom(self._controller.getZoom()/zoom)
542
543 def resetZoom(self):
544 self._controller.setZoom(1)
545
546 def rotateCounterClockwise(self):
547 self._controller.rotateCounterClockwise()
548
549 def rotateClockwise(self):
550 self._controller.rotateClockwise()
551
552 def _getRealCoords(self, sender, event):
553 cw = sender
554 offsetX = event.getX()
555 offsetY = event.getY()
556
557 parent = cw
558 while parent is not None:
559 if isinstance(parent, widgets.Widget):
560 offsetX += parent.x
561 offsetY += parent.y
562 parent = parent.parent
563 else:
564 break
565
566 return (offsetX, offsetY)
567
568 def captureScreen(self):
569 userDir = fife_utils.getUserDataDirectory("fife", "editor")
570 t = userDir+"/screenshots"
571 if not os.path.isdir(t):
572 os.makedirs(t)
573 t += "/screen-%s-%s.png" % (date.today().strftime('%Y-%m-%d'),
574 time.strftime('%H-%M-%S'))
575
576 self._editor.getEngine().getRenderBackend().captureScreen(t)
577 print "Saved screenshot to:", t
578
579 def pump(self):
580 self._controller.moveCamera(self._scrollX, self._scrollY)