105
|
1 # Copyright (C) 2000-2001 The OpenRPG Project
|
|
2 #
|
|
3 # openrpg-dev@lists.sourceforge.net
|
|
4 #
|
|
5 # This program is free software; you can redistribute it and/or modify
|
|
6 # it under the terms of the GNU General Public License as published by
|
|
7 # the Free Software Foundation; either version 2 of the License, or
|
|
8 # (at your option) any later version.
|
|
9 #
|
|
10 # This program is distributed in the hope that it will be useful,
|
|
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13 # GNU General Public License for more details.
|
|
14 #
|
|
15 # You should have received a copy of the GNU General Public License
|
|
16 # along with this program; if not, write to the Free Software
|
|
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
18 # --
|
|
19 #
|
|
20 # File: mapper/whiteboard_hander.py
|
|
21 # Author: OpenRPG Team
|
|
22 # Maintainer:
|
|
23 # Version:
|
|
24 # $Id: token_handler.py,v 1.43 2007/12/07 20:39:50 digitalxero Exp $
|
|
25 #
|
|
26 # Description: Token layer handler; derived from token Layer handler
|
|
27 #
|
|
28 __version__ = "$Id: token_handler.py,v 1.43 2007/12/07 20:39:50 madmathlabs Exp $"
|
|
29
|
|
30 from orpg.mapper.base_handler import *
|
|
31 from tok_dialogs import *
|
|
32 import thread
|
|
33 import time
|
|
34 import mimetypes
|
|
35 import urllib
|
|
36 import xml.dom.minidom as minidom
|
|
37 import wx
|
|
38
|
|
39 ## rewrite Grid
|
|
40 from orpg.mapper.grid import GRID_RECTANGLE
|
|
41 from orpg.mapper.grid import GRID_HEXAGON
|
|
42 from orpg.mapper.grid import GRID_ISOMETRIC
|
|
43 import os
|
|
44
|
|
45 from orpg.tools.orpg_settings import settings
|
|
46
|
|
47 ## Jesus H! No Face, rotate!
|
|
48 TOK_ROT_LEFT = wx.NewId()
|
|
49 ROT_LEFT_45 = wx.NewId()
|
|
50 LABEL_TOOL = wx.NewId()
|
|
51 LAYER_TOOL = wx.NewId()
|
|
52 TOK_LIST_TOOL = wx.NewId()
|
|
53 TOK_TOOL = wx.NewId()
|
|
54 TOK_URL = wx.NewId()
|
|
55 SERIAL_TOOL = wx.NewId()
|
|
56 TOK_MOVE = wx.NewId()
|
|
57 TOK_REMOVE = wx.NewId()
|
|
58 TOK_PROP_DLG = wx.NewId()
|
|
59 TOK_FACING_NONE = wx.NewId()
|
|
60 TOK_FACING_MATCH = wx.NewId()
|
|
61 TOK_FACING_EAST = wx.NewId()
|
|
62 TOK_FACING_WEST = wx.NewId()
|
|
63 TOK_FACING_NORTH = wx.NewId()
|
|
64 TOK_FACING_SOUTH = wx.NewId()
|
|
65 TOK_FACING_NORTHEAST = wx.NewId()
|
|
66 TOK_FACING_SOUTHEAST = wx.NewId()
|
|
67 TOK_FACING_SOUTHWEST = wx.NewId()
|
|
68 TOK_FACING_NORTHWEST = wx.NewId()
|
|
69 TOK_HEADING_NONE = wx.NewId()
|
|
70 TOK_HEADING_MATCH = wx.NewId()
|
|
71 TOK_HEADING_EAST = wx.NewId()
|
|
72 TOK_HEADING_WEST = wx.NewId()
|
|
73 TOK_HEADING_NORTH = wx.NewId()
|
|
74 TOK_HEADING_SOUTH = wx.NewId()
|
|
75 TOK_HEADING_NORTHEAST = wx.NewId()
|
|
76 TOK_HEADING_SOUTHEAST = wx.NewId()
|
|
77 TOK_HEADING_SOUTHWEST = wx.NewId()
|
|
78 TOK_HEADING_NORTHWEST = wx.NewId()
|
|
79 TOK_HEADING_SUBMENU = wx.NewId()
|
|
80 TOK_FACING_SUBMENU = wx.NewId()
|
|
81 TOK_ALIGN_SUBMENU = wx.NewId()
|
|
82 TOK_ALIGN_GRID_CENTER = wx.NewId()
|
|
83 TOK_ALIGN_GRID_TL = wx.NewId()
|
|
84 TOK_TITLE_HACK = wx.NewId()
|
|
85 TOK_TO_GAMETREE = wx.NewId()
|
|
86 TOK_BACK_ONE = wx.NewId()
|
|
87 TOK_FORWARD_ONE = wx.NewId()
|
|
88 TOK_TO_BACK = wx.NewId()
|
|
89 TOK_TO_FRONT = wx.NewId()
|
|
90 TOK_LOCK_BACK = wx.NewId()
|
|
91 TOK_LOCK_FRONT = wx.NewId()
|
|
92 TOK_FRONTBACK_UNLOCK = wx.NewId()
|
|
93 TOK_ZORDER_SUBMENU = wx.NewId()
|
|
94 TOK_SHOW_HIDE = wx.NewId()
|
|
95 TOK_LOCK_UNLOCK = wx.NewId()
|
|
96 MAP_REFRESH_MINI_URLS = wx.NewId()
|
|
97
|
|
98 class myFileDropTarget(wx.FileDropTarget):
|
|
99 def __init__(self, handler):
|
|
100 wx.FileDropTarget.__init__(self)
|
|
101 self.m_handler = handler
|
|
102 def OnDropFiles(self, x, y, filenames):
|
|
103 self.m_handler.on_drop_files(x, y, filenames)
|
|
104
|
|
105 class token_handler(base_layer_handler):
|
|
106
|
|
107 def __init__(self, parent, id, canvas):
|
|
108 self.sel_min = None
|
|
109 self.auto_label = 1
|
|
110 self.use_serial = 1
|
|
111 self.auto_label_cb = None
|
|
112 self.canvas = canvas
|
|
113 self.settings = settings
|
|
114 self.mini_rclick_menu_extra_items = {}
|
|
115 self.background_rclick_menu_extra_items = {}
|
|
116 base_layer_handler.__init__(self, parent, id, canvas)
|
|
117 # id is the index of the last good menu choice or 'None'
|
|
118 # if the last menu was left without making a choice
|
|
119 # should be -1 at other times to prevent events overlapping
|
|
120 self.lastMenuChoice = None
|
|
121 self.drag_mini = None
|
|
122 self.tooltip_delay_miliseconds = 500
|
|
123 self.tooltip_timer = wx.CallLater(self.tooltip_delay_miliseconds, self.on_tooltip_timer)
|
|
124 self.tooltip_timer.Stop()
|
|
125 dt = myFileDropTarget(self)
|
|
126 self.canvas.SetDropTarget(dt)
|
|
127 #wxInitAllImageHandlers()
|
|
128
|
|
129 def build_ctrls(self):
|
|
130 base_layer_handler.build_ctrls(self)
|
|
131 # add controls in reverse order! (unless you want them after the default tools)
|
|
132 self.auto_label_cb = wx.CheckBox(self, wx.ID_ANY, ' Auto Label ', (-1,-1),(-1,-1))
|
|
133 self.auto_label_cb.SetValue(self.auto_label)
|
|
134 self.min_url = wx.ComboBox(self, wx.ID_ANY, "http://", style=wx.CB_DROPDOWN | wx.CB_SORT)
|
|
135 self.localBrowse = wx.Button(self, wx.ID_ANY, 'Browse', style=wx.BU_EXACTFIT)
|
|
136 minilist = createMaskedButton( self, dir_struct["icon"]+'questionhead.gif', 'Edit token properties', wx.ID_ANY)
|
|
137 miniadd = wx.Button(self, wx.ID_OK, "Add Token", style=wx.BU_EXACTFIT)
|
|
138 self.sizer.Add(self.auto_label_cb,0,wx.ALIGN_CENTER)
|
|
139 self.sizer.Add((6, 0))
|
|
140 self.sizer.Add(self.min_url, 1, wx.ALIGN_CENTER)
|
|
141 self.sizer.Add((6, 0))
|
|
142 self.sizer.Add(miniadd, 0, wx.ALIGN_CENTER)
|
|
143 self.sizer.Add((6, 0))
|
|
144 self.sizer.Add(self.localBrowse, 0, wx.ALIGN_CENTER)
|
|
145 self.sizer.Add((6, 0))
|
|
146 self.sizer.Add(minilist, 0, wx.ALIGN_CENTER)
|
|
147 self.Bind(wx.EVT_BUTTON, self.on_min_list, minilist)
|
|
148 self.Bind(wx.EVT_BUTTON, self.on_token, miniadd)
|
|
149 self.Bind(wx.EVT_BUTTON, self.on_browse, self.localBrowse)
|
|
150 self.Bind(wx.EVT_CHECKBOX, self.on_label, self.auto_label_cb)
|
|
151
|
|
152 def on_browse(self, evt):
|
|
153 if not self.role_is_gm_or_player(): return
|
|
154 dlg = wx.FileDialog(None, "Select a Token to load", dir_struct["user"]+'webfiles/',
|
|
155 wildcard="Image files (*.bmp, *.gif, *.jpg, *.png)|*.bmp;*.gif;*.jpg;*.png", style=wx.OPEN)
|
|
156 if not dlg.ShowModal() == wx.ID_OK:
|
|
157 dlg.Destroy()
|
|
158 return
|
|
159 file = open(dlg.GetPath(), "rb")
|
|
160 imgdata = file.read()
|
|
161 file.close()
|
|
162 filename = dlg.GetFilename()
|
|
163 (imgtype,j) = mimetypes.guess_type(filename)
|
|
164 postdata = urllib.urlencode({'filename':filename, 'imgdata':imgdata, 'imgtype':imgtype})
|
|
165
|
|
166 ## Removal of Remote Server?
|
|
167 if self.settings.get_setting('LocalorRemote') == 'Remote':
|
|
168 # make the new mini appear in top left of current viewable map
|
|
169 dc = wx.ClientDC(self.canvas)
|
|
170 self.canvas.PrepareDC(dc)
|
|
171 dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
|
|
172 x = dc.DeviceToLogicalX(0)
|
|
173 y = dc.DeviceToLogicalY(0)
|
|
174 thread.start_new_thread(self.canvas.layers['token'].upload,
|
|
175 (postdata, dlg.GetPath()), {'pos':cmpPoint(x,y)})
|
|
176 else:
|
|
177 try: min_url = component.get("cherrypy") + filename
|
|
178 except: return #chat.InfoPost('CherryPy is not started!')
|
|
179 min_url = dlg.GetDirectory().replace(dir_struct["user"]+'webfiles' + os.sep,
|
|
180 component.get("cherrypy")) + '/' + filename
|
|
181 # build url
|
|
182 if min_url == "" or min_url == "http://": return
|
|
183 if min_url[:7] != "http://": min_url = "http://" + min_url
|
|
184 # make label
|
|
185 if self.auto_label and min_url[-4:-3] == '.':
|
|
186 start = min_url.rfind("/") + 1
|
|
187 min_label = min_url[start:len(min_url)-4]
|
|
188 if self.use_serial: min_label = '%s %d' % ( min_label, self.canvas.layers['token'].next_serial() )
|
|
189 else: min_label = ""
|
|
190 if self.min_url.FindString(min_url) == -1: self.min_url.Append(min_url)
|
|
191 try:
|
|
192 id = 'mini-' + self.canvas.frame.session.get_next_id()
|
|
193 # make the new mini appear in top left of current viewable map
|
|
194 dc = wx.ClientDC(self.canvas)
|
|
195 self.canvas.PrepareDC(dc)
|
|
196 dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
|
|
197 x = dc.DeviceToLogicalX(0)
|
|
198 y = dc.DeviceToLogicalY(0)
|
|
199 self.canvas.layers['token'].add_token(id, min_url, pos=cmpPoint(x,y), label=min_label)
|
|
200 except:
|
|
201 # When there is an exception here, we should be decrementing the serial_number for reuse!!
|
|
202 unablemsg= "Unable to load/resolve URL: " + min_url + " on resource \"" + min_label + "\"!!!\n\n"
|
|
203 dlg = wx.MessageDialog(self,unablemsg, 'Url not found',wx.ICON_EXCLAMATION)
|
|
204 dlg.ShowModal()
|
|
205 dlg.Destroy()
|
|
206 self.canvas.layers['token'].rollback_serial()
|
|
207 self.canvas.send_map_data()
|
|
208 self.canvas.Refresh(False)
|
|
209
|
|
210
|
|
211 def build_menu(self,label = "Token"):
|
|
212 ## Menu Changes: Rotate
|
|
213 ## Menu into Component
|
|
214 ## Remove To GameTree option
|
|
215 base_layer_handler.build_menu(self,label)
|
|
216 self.main_menu.AppendSeparator()
|
|
217 self.main_menu.Append(LABEL_TOOL,"&Auto label","",1)
|
|
218 self.main_menu.Check(LABEL_TOOL,self.auto_label)
|
|
219 self.main_menu.Append(SERIAL_TOOL,"&Number minis","",1)
|
|
220 self.main_menu.Check(SERIAL_TOOL, self.use_serial)
|
|
221 self.main_menu.Append(MAP_REFRESH_MINI_URLS,"&Refresh tokens") # Add the menu item
|
|
222 self.main_menu.AppendSeparator()
|
|
223 self.main_menu.Append(TOK_MOVE, "Move")
|
|
224 self.canvas.Bind(wx.EVT_MENU, self.on_map_board_menu_item, id=MAP_REFRESH_MINI_URLS) # Set the handler
|
|
225 self.canvas.Bind(wx.EVT_MENU, self.on_label, id=LABEL_TOOL)
|
|
226 self.canvas.Bind(wx.EVT_MENU, self.on_serial, id=SERIAL_TOOL)
|
|
227 # build token menu
|
|
228 self.min_menu = wx.Menu()
|
|
229 # Rectangles and hexagons require slightly different menus because of
|
|
230 # facing and heading possibilities.
|
|
231
|
|
232 rot_left = wx.Menu()
|
|
233 rot_left_45 = rot_left.Append(-1, '45*')
|
|
234 self.canvas.Bind(wx.EVT_MENU, self.rot_left_45, rot_left_45)
|
|
235 rot_right = wx.Menu()
|
|
236
|
|
237 rot_right_45 = rot_right.Append(-1, '45*')
|
|
238 self.canvas.Bind(wx.EVT_MENU, self.rot_right_45, rot_right_45)
|
|
239 ## Replace with Rotate. Left - Right, 45 - 90 degress.
|
|
240 """
|
|
241 face_menu = wx.Menu()
|
|
242 face_menu.Append(TOK_FACING_NONE,"&None")
|
|
243 face_menu.Append(TOK_FACING_NORTH,"&North")
|
|
244 face_menu.Append(TOK_FACING_NORTHEAST,"Northeast")
|
|
245 face_menu.Append(TOK_FACING_EAST,"East")
|
|
246 face_menu.Append(TOK_FACING_SOUTHEAST,"Southeast")
|
|
247 face_menu.Append(TOK_FACING_SOUTH,"&South")
|
|
248 face_menu.Append(TOK_FACING_SOUTHWEST,"Southwest")
|
|
249 face_menu.Append(TOK_FACING_WEST,"West")
|
|
250 face_menu.Append(TOK_FACING_NORTHWEST,"Northwest")
|
|
251 """
|
|
252 ###
|
|
253
|
|
254 heading_menu = wx.Menu()
|
|
255 heading_menu.Append(TOK_HEADING_NONE,"&None")
|
|
256 heading_menu.Append(TOK_HEADING_NORTH,"&North")
|
|
257 heading_menu.Append(TOK_HEADING_NORTHEAST,"Northeast")
|
|
258 heading_menu.Append(TOK_HEADING_EAST,"East")
|
|
259 heading_menu.Append(TOK_HEADING_SOUTHEAST,"Southeast")
|
|
260 heading_menu.Append(TOK_HEADING_SOUTH,"&South")
|
|
261 heading_menu.Append(TOK_HEADING_SOUTHWEST,"Southwest")
|
|
262 heading_menu.Append(TOK_HEADING_WEST,"West")
|
|
263 heading_menu.Append(TOK_HEADING_NORTHWEST,"Northwest")
|
|
264
|
|
265 align_menu = wx.Menu()
|
|
266 align_menu.Append(TOK_ALIGN_GRID_CENTER,"&Center")
|
|
267 align_menu.Append(TOK_ALIGN_GRID_TL,"&Top-Left")
|
|
268 # This is a hack to simulate a menu title, due to problem in Linux
|
|
269 if wx.Platform == '__WXMSW__': self.min_menu.SetTitle(label)
|
|
270 else:
|
|
271 self.min_menu.Append(TOK_TITLE_HACK,label)
|
|
272 self.min_menu.AppendSeparator()
|
|
273 self.min_menu.Append(TOK_SHOW_HIDE,"Show / Hide")
|
|
274 self.min_menu.Append(TOK_LOCK_UNLOCK, "Lock / Unlock")
|
|
275 self.min_menu.Append(TOK_REMOVE,"&Remove")
|
|
276
|
|
277 ##Remove
|
|
278 #self.min_menu.Append(TOK_TO_GAMETREE,"To &Gametree")
|
|
279 ###
|
|
280
|
|
281 self.min_menu.AppendMenu(TOK_HEADING_SUBMENU,"Set &Heading",heading_menu)
|
|
282
|
|
283 ##Remove
|
|
284 self.min_menu.AppendMenu(TOK_ROT_LEFT,"&Rotate Left",rot_left)
|
|
285 self.min_menu.AppendMenu(wx.ID_ANY,"&Rotate Right",rot_right)
|
|
286 ###
|
|
287
|
|
288 self.min_menu.AppendMenu(TOK_ALIGN_SUBMENU,"Snap-to &Alignment",align_menu)
|
|
289 self.min_menu.AppendSeparator()
|
|
290 zorder_menu = wx.Menu()
|
|
291 zorder_menu.Append(TOK_BACK_ONE,"Back one")
|
|
292 zorder_menu.Append(TOK_FORWARD_ONE,"Forward one")
|
|
293 zorder_menu.Append(TOK_TO_BACK,"To back")
|
|
294 zorder_menu.Append(TOK_TO_FRONT,"To front")
|
|
295 zorder_menu.AppendSeparator()
|
|
296 zorder_menu.Append(TOK_LOCK_BACK,"Lock to back")
|
|
297 zorder_menu.Append(TOK_LOCK_FRONT,"Lock to front")
|
|
298 zorder_menu.Append(TOK_FRONTBACK_UNLOCK,"Unlock Front/Back")
|
|
299 self.min_menu.AppendMenu(TOK_ZORDER_SUBMENU, "Token Z-Order",zorder_menu)
|
|
300 #self.min_menu.Append(TOK_LOCK,"&Lock")
|
|
301 self.min_menu.AppendSeparator()
|
|
302 self.min_menu.Append(TOK_PROP_DLG,"&Properties")
|
|
303
|
|
304
|
|
305 #self.min_menu.AppendSeparator()
|
|
306 #self.min_menu.Append(TOK_MOVE, "Move")
|
|
307
|
|
308 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_MOVE)
|
|
309 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_SHOW_HIDE)
|
|
310 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_LOCK_UNLOCK)
|
|
311 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_REMOVE)
|
|
312
|
|
313 ##Remove
|
|
314 #self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_TO_GAMETREE)
|
|
315 ###
|
|
316
|
|
317 #self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_LOCK)
|
|
318 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_PROP_DLG)
|
|
319
|
|
320 ##Remove
|
|
321 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_NONE)
|
|
322 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_EAST)
|
|
323 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_WEST)
|
|
324 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_NORTH)
|
|
325 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_SOUTH)
|
|
326 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_NORTHEAST)
|
|
327 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_SOUTHEAST)
|
|
328 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_SOUTHWEST)
|
|
329 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FACING_NORTHWEST)
|
|
330 ###
|
|
331
|
|
332 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_NONE)
|
|
333 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_EAST)
|
|
334 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_WEST)
|
|
335 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_NORTH)
|
|
336 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_SOUTH)
|
|
337 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_NORTHEAST)
|
|
338 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_SOUTHEAST)
|
|
339 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_SOUTHWEST)
|
|
340 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_HEADING_NORTHWEST)
|
|
341 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_ALIGN_GRID_CENTER)
|
|
342 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_ALIGN_GRID_TL)
|
|
343 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_BACK_ONE)
|
|
344 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FORWARD_ONE)
|
|
345 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_TO_BACK)
|
|
346 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_TO_FRONT)
|
|
347 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_LOCK_BACK)
|
|
348 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_LOCK_FRONT)
|
|
349 self.canvas.Bind(wx.EVT_MENU, self.on_min_menu_item, id=TOK_FRONTBACK_UNLOCK)
|
|
350 ######### add plugin added menu items #########
|
|
351 if len(self.mini_rclick_menu_extra_items)>0:
|
|
352 self.min_menu.AppendSeparator()
|
|
353 for item in self.mini_rclick_menu_extra_items.items(): self.min_menu.Append(item[1], item[0])
|
|
354 if len(self.background_rclick_menu_extra_items)>0:
|
|
355 self.main_menu.AppendSeparator()
|
|
356 for item in self.background_rclick_menu_extra_items.items():
|
|
357 self.main_menu.Append(item[1], item[0])
|
|
358
|
|
359 def do_min_menu(self,pos):
|
|
360 self.canvas.PopupMenu(self.min_menu,pos)
|
|
361
|
|
362 def rot_left_45(self, evt):
|
|
363 #self.sel_rmin.rotate += 0.785
|
|
364 self.sel_rmin.rotate += 1.046
|
|
365 #component.get('drawn')[self.sel_rmin] = False
|
|
366
|
|
367 def rot_right_45(self, evt):
|
|
368 #self.sel_rmin.rotate -= 0.785
|
|
369 self.sel_rmin.rotate -= 1.046
|
|
370
|
|
371 def do_min_select_menu(self, min_list, pos):
|
|
372 # to prevent another event being processed
|
|
373 self.lastMenuChoice = None
|
|
374 self.min_select_menu = wx.Menu()
|
|
375 self.min_select_menu.SetTitle("Select Token")
|
|
376 loop_count = 1
|
|
377 try:
|
|
378 for m in min_list:
|
|
379 # Either use the token label for the selection list
|
|
380 if m.label: self.min_select_menu.Append(loop_count, m.label)
|
|
381 # Or use part of the images filename as an identifier
|
|
382 else:
|
|
383 string_split = string.split(m.path,"/",)
|
|
384 last_string = string_split[len(string_split)-1]
|
|
385 self.min_select_menu.Append(loop_count, 'Unlabeled - ' + last_string[:len(last_string)-4])
|
|
386 self.canvas.Bind(wx.EVT_MENU, self.min_selected, id=loop_count)
|
|
387 loop_count += 1
|
|
388 self.canvas.PopupMenu(self.min_select_menu,pos)
|
|
389 except: pass
|
|
390
|
|
391 def min_selected(self,evt):
|
|
392 # this is the callback function for the menu that is used to choose
|
|
393 # between minis when you right click, left click or left double click
|
|
394 # on a stack of two or more
|
|
395 self.canvas.Refresh(False)
|
|
396 self.canvas.send_map_data()
|
|
397 self.lastMenuChoice = evt.GetId()-1
|
|
398
|
|
399 def on_min_menu_item(self,evt):
|
|
400 id = evt.GetId()
|
|
401 if id == TOK_MOVE:
|
|
402 if self.sel_min:
|
|
403 self.moveSelectedMini(self.last_rclick_pos)
|
|
404 self.deselectAndRefresh()
|
|
405 return
|
|
406 elif id == TOK_REMOVE: self.canvas.layers['token'].del_token(self.sel_rmin)
|
|
407
|
|
408 ##Remove
|
|
409 elif id == TOK_TO_GAMETREE:
|
|
410 min_xml = self.sel_rmin.toxml(action="new")
|
|
411 node_begin = "<nodehandler module='map_token_nodehandler' class='map_token_handler' name='"
|
|
412 if self.sel_rmin.label: node_begin += self.sel_rmin.label + "'"
|
|
413 else: node_begin += "Unnamed token'"
|
|
414 node_begin += ">"
|
|
415 gametree = component.get('tree')
|
|
416 node_xml = node_begin + min_xml + '</nodehandler>'
|
|
417 #print "Sending this XML to insert_xml:" + node_xml
|
|
418 gametree.insert_xml(str(node_xml))
|
|
419
|
|
420 elif id == TOK_SHOW_HIDE:
|
|
421 if self.sel_rmin.hide: self.sel_rmin.hide = 0
|
|
422 else: self.sel_rmin.hide = 1
|
|
423 elif id == TOK_LOCK_UNLOCK:
|
|
424 if self.sel_rmin.locked: self.sel_rmin.locked = False
|
|
425 else: self.sel_rmin.locked = True
|
|
426 if self.sel_rmin == self.sel_min:
|
|
427 # when we lock / unlock the selected mini make sure it isn't still selected
|
|
428 # or it might easily get moved by accident and be hard to move back
|
|
429 self.sel_min.selected = False
|
|
430 self.sel_min.isUpdated = True
|
|
431 self.sel_min = None
|
|
432 recycle_bin = {TOK_HEADING_NONE: FACE_NONE, TOK_HEADING_NORTH: FACE_NORTH,
|
|
433 TOK_HEADING_NORTHWEST: FACE_NORTHWEST, TOK_HEADING_NORTHEAST: FACE_NORTHEAST,
|
|
434 TOK_HEADING_EAST: FACE_EAST, TOK_HEADING_SOUTHEAST: FACE_SOUTHEAST, TOK_HEADING_SOUTHWEST: FACE_SOUTHWEST,
|
|
435 TOK_HEADING_SOUTH: FACE_SOUTH, TOK_HEADING_WEST: FACE_WEST}
|
|
436 if recycle_bin.has_key(id):
|
|
437 self.sel_rmin.heading = recycle_bin[id]
|
|
438 del recycle_bin
|
|
439 recycle_bin = {TOK_FACING_NONE: FACE_NONE, TOK_FACING_NORTH: FACE_NORTH,
|
|
440 TOK_FACING_NORTHWEST: FACE_NORTHWEST, TOK_FACING_NORTHEAST: FACE_NORTHEAST,
|
|
441 TOK_FACING_EAST: FACE_EAST, TOK_FACING_SOUTHEAST: FACE_SOUTHEAST, TOK_FACING_SOUTHWEST: FACE_SOUTHWEST,
|
|
442 TOK_FACING_SOUTH: FACE_SOUTH, TOK_FACING_WEST: FACE_WEST}
|
|
443 if recycle_bin.has_key(id):
|
|
444 self.sel_rmin.face = recycle_bin[id]
|
|
445 del recycle_bin
|
|
446 elif id == TOK_ALIGN_GRID_CENTER: self.sel_rmin.snap_to_align = SNAPTO_ALIGN_CENTER
|
|
447 elif id == TOK_ALIGN_GRID_TL: self.sel_rmin.snap_to_align = SNAPTO_ALIGN_TL
|
|
448 elif id == TOK_PROP_DLG:
|
|
449 old_lock_value = self.sel_rmin.locked
|
|
450 dlg = min_edit_dialog(self.canvas.frame.GetParent(),self.sel_rmin)
|
|
451 if dlg.ShowModal() == wx.ID_OK:
|
|
452 if self.sel_rmin == self.sel_min and self.sel_rmin.locked != old_lock_value:
|
|
453 # when we lock / unlock the selected mini make sure it isn't still selected
|
|
454 # or it might easily get moved by accident and be hard to move back
|
|
455 self.sel_min.selected = False
|
|
456 self.sel_min.isUpdated = True
|
|
457 self.sel_min = None
|
|
458 self.canvas.Refresh(False)
|
|
459 self.canvas.send_map_data()
|
|
460 return
|
|
461
|
|
462 elif id == TOK_BACK_ONE:
|
|
463 # This assumes that we always start out with a z-order
|
|
464 # that starts at 0 and goes up to the number of
|
|
465 # minis - 1. If this isn't the case, then execute
|
|
466 # a self.canvas.layers['token'].collapse_zorder()
|
|
467 # before getting the oldz to test
|
|
468 # Save the selected minis current z-order
|
|
469 oldz = self.sel_rmin.zorder
|
|
470 # Make sure the mini isn't sticky front or back
|
|
471 if (oldz != TOK_STICKY_BACK) and (oldz != TOK_STICKY_FRONT):
|
|
472 ## print "old z-order = " + str(oldz)
|
|
473 self.sel_rmin.zorder -= 1
|
|
474 # Re-collapse to normalize
|
|
475 # Note: only one update (with the final values) will be sent
|
|
476 self.canvas.layers['token'].collapse_zorder()
|
|
477
|
|
478 elif id == TOK_FORWARD_ONE:
|
|
479 # This assumes that we always start out with a z-order
|
|
480 # that starts at 0 and goes up to the number of
|
|
481 # minis - 1. If this isn't the case, then execute
|
|
482 # a self.canvas.layers['token'].collapse_zorder()
|
|
483 # before getting the oldz to test
|
|
484 # Save the selected minis current z-order
|
|
485 oldz = self.sel_rmin.zorder
|
|
486 ## print "old z-order = " + str(oldz)
|
|
487 self.sel_rmin.zorder += 1
|
|
488
|
|
489 # Re-collapse to normalize
|
|
490 # Note: only one update (with the final values) will be sent
|
|
491 self.canvas.layers['token'].collapse_zorder()
|
|
492
|
|
493 elif id == TOK_TO_FRONT:
|
|
494 # This assumes that we always start out with a z-order
|
|
495 # that starts at 0 and goes up to the number of
|
|
496 # minis - 1. If this isn't the case, then execute
|
|
497 # a self.canvas.layers['token'].collapse_zorder()
|
|
498 # before getting the oldz to test
|
|
499 # Save the selected minis current z-order
|
|
500 oldz = self.sel_rmin.zorder
|
|
501
|
|
502 # Make sure the mini isn't sticky front or back
|
|
503 if (oldz != TOK_STICKY_BACK) and (oldz != TOK_STICKY_FRONT):
|
|
504 ## print "old z-order = " + str(oldz)
|
|
505 # The new z-order will be one more than the last index
|
|
506 newz = len(self.canvas.layers['token'].token)
|
|
507 ## print "new z-order = " + str(newz)
|
|
508 self.sel_rmin.zorder = newz
|
|
509 # Re-collapse to normalize
|
|
510 # Note: only one update (with the final values) will be sent
|
|
511 self.canvas.layers['token'].collapse_zorder()
|
|
512
|
|
513 elif id == TOK_TO_BACK:
|
|
514 # This assumes that we always start out with a z-order
|
|
515 # that starts at 0 and goes up to the number of
|
|
516 # minis - 1. If this isn't the case, then execute
|
|
517 # a self.canvas.layers['token'].collapse_zorder()
|
|
518 # before getting the oldz to test
|
|
519 # Save the selected minis current z-order
|
|
520 oldz = self.sel_rmin.zorder
|
|
521 # Make sure the mini isn't sticky front or back
|
|
522 if (oldz != TOK_STICKY_BACK) and (oldz != TOK_STICKY_FRONT):
|
|
523 ## print "old z-order = " + str(oldz)
|
|
524
|
|
525 # Since 0 is the lowest in a normalized order, be one less
|
|
526 newz = -1
|
|
527 ## print "new z-order = " + str(newz)
|
|
528 self.sel_rmin.zorder = newz
|
|
529 # Re-collapse to normalize
|
|
530 # Note: only one update (with the final values) will be sent
|
|
531 self.canvas.layers['token'].collapse_zorder()
|
|
532
|
|
533 elif id == TOK_FRONTBACK_UNLOCK:
|
|
534 #print "Unlocked/ unstickified..."
|
|
535 if self.sel_rmin.zorder == TOK_STICKY_BACK: self.sel_rmin.zorder = TOK_STICKY_BACK + 1
|
|
536 elif self.sel_rmin.zorder == TOK_STICKY_FRONT: self.sel_rmin.zorder = TOK_STICKY_FRONT - 1
|
|
537 elif id == TOK_LOCK_BACK: self.sel_rmin.zorder = TOK_STICKY_BACK
|
|
538 elif id == TOK_LOCK_FRONT: self.sel_rmin.zorder = TOK_STICKY_FRONT
|
|
539 # Pretty much, we always want to refresh when we go through here
|
|
540 # This helps us remove the redundant self.Refresh() on EVERY menu event
|
|
541 # that we process above.
|
|
542 self.sel_rmin.isUpdated = True
|
|
543 self.canvas.Refresh(False)
|
|
544 self.canvas.send_map_data()
|
|
545
|
|
546 def on_token(self, evt):
|
|
547 session = self.canvas.frame.session
|
|
548 if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER) and (session.use_roles()):
|
|
549 self.infoPost("You must be either a player or GM to use the token Layer")
|
|
550 return
|
|
551 min_url = self.min_url.GetValue()
|
|
552 # build url
|
|
553 if min_url == "" or min_url == "http://": return
|
|
554 if min_url[:7] != "http://" : min_url = "http://" + min_url
|
|
555 # make label
|
|
556 if self.auto_label and min_url[-4:-3] == '.':
|
|
557 start = min_url.rfind("/") + 1
|
|
558 min_label = min_url[start:len(min_url)-4]
|
|
559 if self.use_serial:
|
|
560 min_label = '%s %d' % ( min_label, self.canvas.layers['token'].next_serial() )
|
|
561 else: min_label = ""
|
|
562 if self.min_url.FindString(min_url) == -1: self.min_url.Append(min_url)
|
|
563 try:
|
|
564 id = 'mini-' + self.canvas.frame.session.get_next_id()
|
|
565 # make the new mini appear in top left of current viewable map
|
|
566 dc = wx.ClientDC(self.canvas)
|
|
567 self.canvas.PrepareDC(dc)
|
|
568 dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
|
|
569 x = dc.DeviceToLogicalX(0)
|
|
570 y = dc.DeviceToLogicalY(0)
|
|
571 self.canvas.layers['token'].add_token(id, min_url, pos=cmpPoint(x,y), label=min_label)
|
|
572 except:
|
|
573 # When there is an exception here, we should be decrementing the serial_number for reuse!!
|
|
574 unablemsg= "Unable to load/resolve URL: " + min_url + " on resource \"" + min_label + "\"!!!\n\n"
|
|
575 #print unablemsg
|
|
576 dlg = wx.MessageDialog(self,unablemsg, 'Url not found',wx.ICON_EXCLAMATION)
|
|
577 dlg.ShowModal()
|
|
578 dlg.Destroy()
|
|
579 self.canvas.layers['token'].rollback_serial()
|
|
580 self.canvas.send_map_data()
|
|
581 self.canvas.Refresh(False)
|
|
582 #except Exception, e:
|
|
583 #wx.MessageBox(str(e),"token Error")
|
|
584
|
|
585 def on_label(self,evt):
|
|
586 self.auto_label = not self.auto_label
|
|
587 self.auto_label_cb.SetValue(self.auto_label)
|
|
588 #self.send_map_data()
|
|
589 #self.Refresh()
|
|
590
|
|
591 def on_min_list(self,evt):
|
|
592 session = self.canvas.frame.session
|
|
593 if (session.my_role() != session.ROLE_GM):
|
|
594 self.infoPost("You must be a GM to use this feature")
|
|
595 return
|
|
596 #d = min_list_panel(self.frame.GetParent(),self.canvas.layers,"token list")
|
|
597 d = min_list_panel(self.canvas.frame,self.canvas.layers,"token list")
|
|
598 if d.ShowModal() == wx.ID_OK: d.Destroy()
|
|
599 self.canvas.Refresh(False)
|
|
600
|
|
601 def on_serial(self, evt):
|
|
602 self.use_serial = not self.use_serial
|
|
603
|
|
604 def on_map_board_menu_item(self,evt):
|
|
605 id = evt.GetId()
|
|
606 if id == MAP_REFRESH_MINI_URLS: # Note: this doesn't change the mini, so no need to update the map
|
|
607 for mini in self.canvas.layers['token'].tokens: # For all minis
|
|
608 mini.set_bmp(ImageHandler.load(mini.path, 'token', mini.id)) # Reload their bmp member
|
|
609 self.canvas.Refresh(False)
|
|
610
|
|
611 ####################################################################
|
|
612 ## old functions, changed an awful lot
|
|
613
|
|
614 def on_left_down(self, evt):
|
|
615 if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return
|
|
616 mini = self.find_mini(evt, evt.CmdDown() and self.role_is_gm())
|
|
617 if mini:
|
|
618 deselecting_selected_mini = (mini == self.sel_min) #clicked on the selected mini
|
|
619 self.deselectAndRefresh()
|
|
620 self.drag_mini = mini
|
|
621 if deselecting_selected_mini: return
|
|
622 self.sel_min = mini
|
|
623 self.sel_min.selected = True
|
|
624 self.canvas.Refresh()
|
|
625 else:
|
|
626 self.drag_mini = None
|
|
627 pos = self.getLogicalPosition(evt)
|
|
628 self.moveSelectedMini(pos)
|
|
629 self.deselectAndRefresh()
|
|
630
|
|
631 def on_right_down(self, evt):
|
|
632 if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return
|
|
633 self.last_rclick_pos = self.getLogicalPosition(evt)
|
|
634 mini = self.find_mini(evt, evt.CmdDown() and self.role_is_gm())
|
|
635 if mini:
|
|
636 self.sel_rmin = mini
|
|
637 if self.sel_min: self.min_menu.Enable(TOK_MOVE, True)
|
|
638 else: self.min_menu.Enable(TOK_MOVE, False)
|
|
639 self.prepare_mini_rclick_menu(evt)
|
|
640 self.do_min_menu(evt.GetPosition())
|
|
641 else:# pass it on
|
|
642 if self.sel_min: self.main_menu.Enable(TOK_MOVE, True)
|
|
643 else: self.main_menu.Enable(TOK_MOVE, False)
|
|
644 self.prepare_background_rclick_menu(evt)
|
|
645 base_layer_handler.on_right_down(self, evt)
|
|
646
|
|
647 ####################################################################
|
|
648 ## new functions
|
|
649
|
|
650 def on_drop_files(self, x, y, filepaths):
|
|
651 # currently we ignore multiple files
|
|
652 filepath = filepaths[0]
|
|
653 start1 = filepath.rfind("\\") + 1 # check for both slashes in path to be on the safe side
|
|
654 start2 = filepath.rfind("/") + 1
|
|
655 if start1 < start2: start1 = start2
|
|
656 filename = filepath[start1:]
|
|
657 pos = filename.rfind('.')
|
|
658 ext = filename[pos:].lower()
|
|
659 #ext = filename[-4:].lower()
|
|
660 if(ext != ".bmp" and ext != ".gif" and ext != ".jpg" and ext != ".jpeg" and ext != ".png"):
|
|
661 self.infoPost("Supported file extensions are: *.bmp, *.gif, *.jpg, *.jpeg, *.png")
|
|
662 return
|
|
663 file = open(filepath, "rb")
|
|
664 imgdata = file.read()
|
|
665 file.close()
|
|
666 dc = wx.ClientDC(self.canvas)
|
|
667 self.canvas.PrepareDC(dc)
|
|
668 dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
|
|
669 x = dc.DeviceToLogicalX(x)
|
|
670 y = dc.DeviceToLogicalY(y)
|
|
671 (imgtype,j) = mimetypes.guess_type(filename)
|
|
672 postdata = urllib.urlencode({'filename':filename, 'imgdata':imgdata, 'imgtype':imgtype})
|
|
673 thread.start_new_thread(self.canvas.layers['token'].upload, (postdata, filepath), {'pos':cmpPoint(x,y)})
|
|
674
|
|
675 def on_tooltip_timer(self, *args):
|
|
676 pos = args[0]
|
|
677 dc = wx.ClientDC(self.canvas)
|
|
678 self.canvas.PrepareDC(dc)
|
|
679 dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
|
|
680 pos = wx.Point(dc.DeviceToLogicalX(pos.x), dc.DeviceToLogicalY(pos.y))
|
|
681 mini_list = self.getMiniListOrSelectedMini(pos)
|
|
682 if len(mini_list) > 0:
|
|
683 print mini_list
|
|
684 tooltip = self.get_mini_tooltip(mini_list); print tooltip
|
|
685 self.canvas.SetToolTipString(tooltip)
|
|
686 else: self.canvas.SetToolTipString("")
|
|
687
|
|
688 def on_motion(self,evt):
|
|
689 if evt.Dragging() and evt.LeftIsDown():
|
|
690 if self.canvas.drag is None and self.drag_mini is not None:
|
|
691 drag_bmp = self.drag_mini.bmp
|
|
692 if self.drag_mini.width and self.drag_mini.height:
|
|
693 tmp_image = drag_bmp.ConvertToImage()
|
|
694 tmp_image.Rescale(int(self.drag_mini.width * self.canvas.layers['grid'].mapscale),
|
|
695 int(self.drag_mini.height * self.canvas.layers['grid'].mapscale))
|
|
696 tmp_image.ConvertAlphaToMask()
|
|
697
|
|
698 ### Should show rotated image when dragging.
|
|
699 if self.drag_mini.rotate != 0 :
|
|
700 tmp_image = tmp_image.Rotate(self.drag_mini.rotate,
|
|
701 (self.drag_mini.width/2, self.drag_mini.height/2))
|
|
702
|
|
703 drag_bmp = tmp_image.ConvertToBitmap()
|
|
704 mask = wx.Mask(drag_bmp, wx.Colour(tmp_image.GetMaskRed(),
|
|
705 tmp_image.GetMaskGreen(), tmp_image.GetMaskBlue()))
|
|
706 drag_bmp.SetMask(mask)
|
|
707 tmp_image = tmp_image.ConvertToGreyscale()
|
|
708 self.drag_mini.gray = True
|
|
709 self.drag_mini.isUpdated = True
|
|
710 def refresh():
|
|
711 self.canvas.drag.Hide()
|
|
712 self.canvas.Refresh(False)
|
|
713 wx.CallAfter(refresh)
|
|
714 self.canvas.drag = wx.DragImage(drag_bmp)
|
|
715 self.drag_offset = self.getLogicalPosition(evt)- self.drag_mini.pos
|
|
716 self.canvas.drag.BeginDrag((int(self.drag_offset.x * self.canvas.layers['grid'].mapscale),
|
|
717 int(self.drag_offset.y * self.canvas.layers['grid'].mapscale)), self.canvas, False)
|
|
718 elif self.canvas.drag is not None:
|
|
719 self.canvas.drag.Move(evt.GetPosition())
|
|
720 self.canvas.drag.Show()
|
|
721 # reset tool tip timer
|
|
722 self.canvas.SetToolTipString("")
|
|
723 self.tooltip_timer.Restart(self.tooltip_delay_miliseconds, evt.GetPosition())
|
|
724
|
|
725 def on_left_up(self,evt):
|
|
726 if self.canvas.drag:
|
|
727 self.canvas.drag.Hide()
|
|
728 self.canvas.drag.EndDrag()
|
|
729 self.canvas.drag = None
|
|
730 pos = self.getLogicalPosition(evt)
|
|
731 pos = pos - self.drag_offset
|
|
732 if self.canvas.layers['grid'].snap:
|
|
733 nudge = int(self.canvas.layers['grid'].unit_size/2)
|
|
734 if self.canvas.layers['grid'].mode != GRID_ISOMETRIC:
|
|
735 if self.drag_mini.snap_to_align == SNAPTO_ALIGN_CENTER:
|
|
736 pos = pos + (int(self.drag_mini.bmp.GetWidth()/2),int(self.drag_mini.bmp.GetHeight()/2))
|
|
737 else: pos = pos + (nudge, nudge)
|
|
738 else:# GRID_ISOMETRIC
|
|
739 if self.drag_mini.snap_to_align == SNAPTO_ALIGN_CENTER:
|
|
740 pos = pos + (int(self.drag_mini.bmp.GetWidth()/2), self.drag_mini.bmp.GetHeight())
|
|
741 else: pass # no nudge for the isomorphic / top-left
|
|
742 self.sel_min = self.drag_mini
|
|
743 # check to see if the mouse is inside the window still
|
|
744 w = self.canvas.GetClientSizeTuple() # this is the window size, minus any scrollbars
|
|
745 p = evt.GetPosition() # compare the window size, w with the non-logical position
|
|
746 c = self.canvas.size # this is the grid size, compare with the logical position, pos
|
|
747 # both are [width, height]
|
|
748 if p.x>=0 and pos.x<c[0] and p.x<w[0] and p.y>=0 and pos.y<c[1] and p.y<w[1]:
|
|
749 self.moveSelectedMini(pos)
|
|
750 self.sel_min.gray = False
|
|
751 self.sel_min.selected = False
|
|
752 self.sel_min.isUpdated = True
|
|
753 self.canvas.Refresh(False)
|
|
754 self.canvas.send_map_data()
|
|
755 self.sel_min = None
|
|
756 self.drag_mini = None
|
|
757
|
|
758 def on_left_dclick(self,evt):
|
|
759 if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return
|
|
760 mini = self.find_mini(evt, evt.CmdDown() and self.role_is_gm())
|
|
761 if mini: self.on_mini_dclick(evt, mini)
|
|
762 else: base_layer_handler.on_left_dclick(self, evt)
|
|
763
|
|
764
|
|
765 ####################################################################
|
|
766 ## hook functions (although with python you can override any of the functions)
|
|
767
|
|
768 def prepare_mini_rclick_menu(self, evt):
|
|
769 # override the entire right-click on a mini menu
|
|
770 pass
|
|
771
|
|
772 def prepare_background_rclick_menu(self, evt):
|
|
773 # override the entire right-click on the map menu
|
|
774 pass
|
|
775
|
|
776 def get_mini_tooltip(self, mini_list):
|
|
777 # override to create a tooltip
|
|
778 return ""
|
|
779
|
|
780 def on_mini_dclick(self, evt, mini):
|
|
781 # do something after the mini was left double clicked
|
|
782 pass
|
|
783
|
|
784 ####################################################################
|
|
785 ## easy way to add a single menu item
|
|
786
|
|
787 def set_mini_rclick_menu_item(self, label, callback_function):
|
|
788 # remember you might want to call these at the end of your callback function:
|
|
789 # mini_handler.sel_rmin.isUpdated = True
|
|
790 # canvas.Refresh(False)
|
|
791 # canvas.send_map_data()
|
|
792 if callback_function == None: del self.mini_rclick_menu_extra_items[label]
|
|
793 else:
|
|
794 if not self.mini_rclick_menu_extra_items.has_key(label):
|
|
795 self.mini_rclick_menu_extra_items[label]=wx.NewId()
|
|
796 menu_id = self.mini_rclick_menu_extra_items[label]
|
|
797 self.canvas.Bind(wx.EVT_MENU, callback_function, id=menu_id)
|
|
798 self.build_menu()
|
|
799
|
|
800 def set_background_rclick_menu_item(self, label, callback_function):
|
|
801 if callback_function == None: del self.background_rclick_menu_extra_items[label]
|
|
802 else:
|
|
803 if not self.background_rclick_menu_extra_items.has_key(label):
|
|
804 self.background_rclick_menu_extra_items[label]=wx.NewId()
|
|
805 menu_id = self.background_rclick_menu_extra_items[label]
|
|
806 self.canvas.Bind(wx.EVT_MENU, callback_function, id=menu_id)
|
|
807 self.build_menu()
|
|
808
|
|
809
|
|
810 ####################################################################
|
|
811 ## helper functions
|
|
812
|
|
813 def infoPost(self, message):
|
|
814 component.get("chat").InfoPost(message)
|
|
815
|
|
816 def role_is_gm_or_player(self):
|
|
817 session = self.canvas.frame.session
|
|
818 if (session.my_role() <> session.ROLE_GM) and (session.my_role() <> session.ROLE_PLAYER) and (session.use_roles()):
|
|
819 self.infoPost("You must be either a player or GM to use the token Layer")
|
|
820 return False
|
|
821 return True
|
|
822
|
|
823 def role_is_gm(self):
|
|
824 session = self.canvas.frame.session
|
|
825 if (session.my_role() <> session.ROLE_GM) and (session.use_roles()): return False
|
|
826 return True
|
|
827
|
|
828 def alreadyDealingWithMenu(self):
|
|
829 return self.lastMenuChoice is not None
|
|
830
|
|
831 def getLastMenuChoice(self):
|
|
832 choice = self.lastMenuChoice
|
|
833 self.lastMenuChoice = None
|
|
834 return choice
|
|
835
|
|
836 def getLogicalPosition(self, evt):
|
|
837 dc = wx.ClientDC(self.canvas)
|
|
838 self.canvas.PrepareDC(dc)
|
|
839 dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
|
|
840 pos = evt.GetLogicalPosition(dc)
|
|
841 return pos
|
|
842
|
|
843 def getMiniListOrSelectedMini(self, pos, include_locked=False):
|
|
844 if self.sel_min and self.sel_min.hit_test(pos):
|
|
845 # clicked on the selected mini - assume that is the intended target
|
|
846 # and don't give a choice of it and any other minis stacked with it
|
|
847 mini_list = []
|
|
848 mini_list.append(self.sel_min)
|
|
849 return mini_list
|
|
850 mini_list = self.canvas.layers['token'].find_token(pos, (not include_locked))
|
|
851 if mini_list: return mini_list
|
|
852 mini_list = []
|
|
853 return mini_list
|
|
854
|
|
855 def deselectAndRefresh(self):
|
|
856 if self.sel_min:
|
|
857 self.sel_min.selected = False
|
|
858 self.sel_min.isUpdated = True
|
|
859 self.canvas.Refresh(False)
|
|
860 self.canvas.send_map_data()
|
|
861 self.sel_min = None
|
|
862
|
|
863 def moveSelectedMini(self, pos):
|
|
864 if self.sel_min: self.moveMini(pos, self.sel_min)
|
|
865
|
|
866 def moveMini(self, pos, mini):
|
|
867 grid = self.canvas.layers['grid']
|
|
868 mini.pos = grid.get_snapped_to_pos(pos, mini.snap_to_align, mini.bmp.GetWidth(), mini.bmp.GetHeight())
|
|
869
|
|
870 def find_mini(self, evt, include_locked):
|
|
871 if not self.role_is_gm_or_player() or self.alreadyDealingWithMenu(): return
|
|
872 pos = self.getLogicalPosition(evt)
|
|
873 mini_list = self.getMiniListOrSelectedMini(pos, include_locked)
|
|
874 mini = None
|
|
875 if len(mini_list) > 1:
|
|
876 try: self.do_min_select_menu(mini_list, evt.GetPosition())
|
|
877 except: pass
|
|
878 choice = self.getLastMenuChoice()
|
|
879 if choice == None: return None # left menu without making a choice, eg by clicking outside menu
|
|
880 mini = mini_list[choice]
|
|
881 elif len(mini_list) == 1: mini = mini_list[0]
|
|
882 return mini
|
|
883
|