131
|
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: rpg_grid.py
|
|
21 # Author: Chris Davis
|
|
22 # Maintainer:
|
|
23 # Version:
|
|
24 # $Id: rpg_grid.py,v 1.20 2006/11/15 12:11:24 digitalxero Exp $
|
|
25 #
|
|
26 # Description: The file contains code for the grid nodehanlers
|
|
27 #
|
|
28
|
|
29 __version__ = "$Id: rpg_grid.py,v 1.20 2006/11/15 12:11:24 digitalxero Exp $"
|
|
30
|
|
31 from core import *
|
|
32 from forms import *
|
|
33
|
|
34 class rpg_grid_handler(node_handler):
|
|
35 """ Node handler for rpg grid tool
|
|
36 <nodehandler module='rpg_grid' class='rpg_grid_handler' name='sample'>
|
|
37 <grid border='' autosize='1' >
|
|
38 <row>
|
|
39 <cell size='?'></cell>
|
|
40 <cell></cell>
|
|
41 </row>
|
|
42 <row>
|
|
43 <cell></cell>
|
|
44 <cell></cell>
|
|
45 </row>
|
|
46 </grid>
|
|
47 <macros>
|
|
48 <macro name=''/>
|
|
49 </macros>
|
|
50 </nodehandler>
|
|
51 """
|
|
52 def __init__(self,xml,tree_node):
|
|
53 node_handler.__init__(self,xml,tree_node)
|
|
54 self.grid = self.xml.find('grid')
|
|
55 if self.grid.get("border") == "":
|
|
56 self.grid.set("border","1")
|
|
57 if self.grid.get("autosize") == "":
|
|
58 self.grid.set("autosize","1")
|
|
59 self.macros = self.xml.find('macros')
|
|
60 self.myeditor = None
|
|
61 self.refresh_rows()
|
|
62
|
|
63 def refresh_die_macros(self):
|
|
64 pass
|
|
65
|
|
66 def refresh_rows(self):
|
|
67 self.rows = {}
|
|
68 tree = self.tree
|
|
69 icons = self.tree.icons
|
|
70 tree.CollapseAndReset(self.mytree_node)
|
|
71 for row in self.grid.findall('row'):
|
|
72 first_cell = row.find('cell')
|
|
73 name = first_cell.text
|
|
74 if name == None or name == '':
|
|
75 name = "Row"
|
|
76 new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear'])
|
|
77 handler = grid_row_handler(row,new_tree_node,self)
|
|
78 tree.SetPyData(new_tree_node,handler)
|
|
79
|
|
80 def tohtml(self):
|
|
81 border = self.grid.get("border")
|
|
82 name = self.xml.get('name')
|
|
83 rows = self.grid.findall('row')
|
|
84 colspan = str(len(rows[0].findall('cell')))
|
|
85 html_str = "<table border=\""+border+"\" align=center><tr bgcolor=\""+TH_BG+"\" ><th colspan="+colspan+">"+name+"</th></tr>"
|
|
86 for r in rows:
|
|
87 cells = r.findall('cell')
|
|
88 html_str += "<tr>"
|
|
89 for c in cells:
|
|
90 #html_str += "<td width='"+c.get('size')+"' >" bug here
|
|
91 html_str += "<td >"
|
|
92 text = c.text
|
|
93 if text == None or text == '':
|
|
94 text = '<br />'
|
|
95 html_str += text + "</td>"
|
|
96 html_str += "</tr>"
|
|
97 html_str += "</table>"
|
|
98 return html_str
|
|
99
|
|
100 def get_design_panel(self,parent):
|
|
101 return rpg_grid_edit_panel(parent,self)
|
|
102
|
|
103 def get_use_panel(self,parent):
|
|
104 return rpg_grid_panel(parent,self)
|
|
105
|
|
106 def get_size_constraint(self):
|
|
107 return 1
|
|
108
|
|
109 def is_autosized(self):
|
|
110 return self.grid.get("autosize")
|
|
111
|
|
112 def set_autosize(self,autosize=1):
|
|
113 self.grid.set("autosize",str(autosize))
|
|
114
|
|
115 class grid_row_handler(node_handler):
|
|
116 """ Node Handler grid row.
|
|
117 """
|
|
118 def __init__(self,xml,tree_node,parent):
|
|
119 node_handler.__init__(self,xml,tree_node)
|
|
120 self.drag = False
|
|
121
|
|
122 def on_drop(self,evt):
|
|
123 pass
|
|
124
|
|
125 def can_clone(self):
|
|
126 return 0;
|
|
127
|
|
128 def tohtml(self):
|
|
129 cells = self.xml.findall('cell')
|
|
130 html_str = "<table border=1 align=center><tr >"
|
|
131 for c in cells: # should loop over rows first, then cells
|
|
132 html_str += "<td >"
|
|
133 text = c.text
|
|
134 if text == '' or text is None:
|
|
135 text = '<br />'
|
|
136 html_str += text + "</td>"
|
|
137 html_str += "</tr>"
|
|
138 html_str += "</table>"
|
|
139 return html_str
|
|
140
|
|
141 def get_value(self):
|
|
142 cells = self.xml.findall('cell')
|
|
143 if len(cells) == 2:
|
|
144 return getText(cells[1])
|
|
145 else:
|
|
146 return None
|
|
147
|
|
148 def set_value(self, new_value):
|
|
149 cells = self.xml.findall('cell')
|
|
150 if len(cells) == 2:
|
|
151 cells[1].text = new_value
|
|
152
|
|
153 class MyCellEditor(wx.grid.PyGridCellEditor):
|
|
154 """
|
|
155 This is a sample GridCellEditor that shows you how to make your own custom
|
|
156 grid editors. All the methods that can be overridden are show here. The
|
|
157 ones that must be overridden are marked with "*Must Override*" in the
|
|
158 docstring.
|
|
159
|
|
160 Notice that in order to call the base class version of these special
|
|
161 methods we use the method name preceded by "base_". This is because these
|
|
162 methods are "virtual" in C++ so if we try to call wxGridCellEditor.Create
|
|
163 for example, then when the wxPython extension module tries to call
|
|
164 ptr->Create(...) then it actually calls the derived class version which
|
|
165 looks up the method in this class and calls it, causing a recursion loop.
|
|
166 If you don't understand any of this, don't worry, just call the "base_"
|
|
167 version instead.
|
|
168
|
|
169 ----------------------------------------------------------------------------
|
|
170 This class is copied from the wxPython examples directory and was written by
|
|
171 Robin Dunn.
|
|
172
|
|
173 I have pasted it directly in and removed all references to "log"
|
|
174
|
|
175 -- Andrew
|
|
176
|
|
177 """
|
|
178 def __init__(self):
|
|
179 wx.grid.PyGridCellEditor.__init__(self)
|
|
180
|
|
181
|
|
182 def Create(self, parent, id, evtHandler):
|
|
183 """
|
|
184 Called to create the control, which must derive from wxControl.
|
|
185 *Must Override*
|
|
186 """
|
|
187 self._tc = wx.TextCtrl(parent, id, "", style=wx.TE_PROCESS_ENTER | wx.TE_PROCESS_TAB)
|
|
188 self._tc.SetInsertionPoint(0)
|
|
189 self.SetControl(self._tc)
|
|
190 if evtHandler:
|
|
191 self._tc.PushEventHandler(evtHandler)
|
|
192
|
|
193
|
|
194 def SetSize(self, rect):
|
|
195 """
|
|
196 Called to position/size the edit control within the cell rectangle.
|
|
197 If you don't fill the cell (the rect) then be sure to override
|
|
198 PaintBackground and do something meaningful there.
|
|
199 """
|
|
200 self._tc.SetDimensions(rect.x+1, rect.y+1, rect.width+2, rect.height+2)
|
|
201
|
|
202
|
|
203 #def Show(self, show, attr): #deprecated DeprecationWarning: Please use PyGridCellEditor.Show instead.
|
|
204 # """
|
|
205 # Show or hide the edit control. You can use the attr (if not None)
|
|
206 # to set colours or fonts for the control.
|
|
207 # """
|
|
208 # self.base_Show(show, attr) # Removed to prevent recursive error type.
|
|
209
|
|
210
|
|
211 def BeginEdit(self, row, col, grid):
|
|
212 """
|
|
213 Fetch the value from the table and prepare the edit control
|
|
214 to begin editing. Set the focus to the edit control.
|
|
215 *Must Override*
|
|
216 """
|
|
217 self.startValue = grid.GetTable().GetValue(row, col)
|
|
218 self._tc.SetValue(self.startValue)
|
|
219 self._tc.SetInsertionPointEnd()
|
|
220 self._tc.SetFocus()
|
|
221
|
|
222 # For this example, select the text
|
|
223 self._tc.SetSelection(0, self._tc.GetLastPosition())
|
|
224
|
|
225
|
|
226 def EndEdit(self, row, col, grid):
|
|
227 """
|
|
228 Complete the editing of the current cell. Returns True if the value
|
|
229 has changed. If necessary, the control may be destroyed.
|
|
230 *Must Override*
|
|
231 """
|
|
232 changed = False
|
|
233
|
|
234 val = self._tc.GetValue()
|
|
235 if val != self.startValue:
|
|
236 changed = True
|
|
237 grid.GetTable().SetValue(row, col, val) # update the table
|
|
238
|
|
239 self.startValue = ''
|
|
240 self._tc.SetValue('')
|
|
241 return changed
|
|
242
|
|
243
|
|
244 def Reset(self):
|
|
245 """
|
|
246 Reset the value in the control back to its starting value.
|
|
247 *Must Override*
|
|
248 """
|
|
249 self._tc.SetValue(self.startValue)
|
|
250 self._tc.SetInsertionPointEnd()
|
|
251
|
|
252
|
|
253 def IsAcceptedKey(self, evt):
|
|
254 """
|
|
255 Return True to allow the given key to start editing: the base class
|
|
256 version only checks that the event has no modifiers. F2 is special
|
|
257 and will always start the editor.
|
|
258 """
|
|
259
|
|
260 ## Oops, there's a bug here, we'll have to do it ourself..
|
|
261 ##return self.base_IsAcceptedKey(evt)
|
|
262
|
|
263 return (not (evt.ControlDown() or evt.AltDown()) and
|
|
264 evt.GetKeyCode() != wx.WXK_SHIFT)
|
|
265
|
|
266
|
|
267 def StartingKey(self, evt):
|
|
268 """
|
|
269 If the editor is enabled by pressing keys on the grid, this will be
|
|
270 called to let the editor do something about that first key if desired.
|
|
271 """
|
|
272 key = evt.GetKeyCode()
|
|
273 ch = None
|
|
274 if key in [wx.WXK_NUMPAD0, wx.WXK_NUMPAD1, wx.WXK_NUMPAD2, wx.WXK_NUMPAD3, wx.WXK_NUMPAD4,
|
|
275 wx.WXK_NUMPAD5, wx.WXK_NUMPAD6, wx.WXK_NUMPAD7, wx.WXK_NUMPAD8, wx.WXK_NUMPAD9]:
|
|
276 ch = ch = chr(ord('0') + key - wx.WXK_NUMPAD0)
|
|
277
|
|
278 elif key < 256 and key >= 0 and chr(key) in string.printable:
|
|
279 ch = chr(key)
|
|
280 if not evt.ShiftDown():
|
|
281 ch = string.lower(ch)
|
|
282
|
|
283 if ch is not None:
|
|
284 # For this example, replace the text. Normally we would append it.
|
|
285 self._tc.AppendText(ch)
|
|
286 else:
|
|
287 evt.Skip()
|
|
288
|
|
289
|
|
290
|
|
291 def Destroy(self):
|
|
292 """final cleanup"""
|
|
293 self.base_Destroy()
|
|
294
|
|
295
|
|
296 def Clone(self):
|
|
297 """
|
|
298 Create a new object which is the copy of this one
|
|
299 *Must Override*
|
|
300 """
|
|
301 return MyCellEditor()
|
|
302
|
|
303
|
|
304
|
|
305 class rpg_grid(wx.grid.Grid):
|
|
306 """grid for attacks"""
|
|
307 def __init__(self, parent, handler):
|
|
308 wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
|
|
309 self.parent = parent
|
|
310 self.handler = handler
|
|
311
|
|
312 # Registers a "custom" cell editor (really the example from Robin Dunn with minor mods
|
|
313 self.RegisterDataType(wx.grid.GRID_VALUE_STRING, wx.grid.GridCellStringRenderer(),MyCellEditor())
|
|
314
|
|
315 self.rows = handler.grid.findall('row')
|
|
316 rows = len(self.rows)
|
|
317 cols = len(self.rows[0].findall('cell'))
|
|
318 self.CreateGrid(rows,cols)
|
|
319 self.SetRowLabelSize(0)
|
|
320 self.SetColLabelSize(0)
|
|
321 self.set_col_widths()
|
|
322
|
|
323 for i in range(0,len(self.rows)):
|
|
324 self.refresh_row(i)
|
|
325
|
|
326 self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
|
|
327 self.Bind(wx.grid.EVT_GRID_COL_SIZE, self.on_col_size)
|
|
328 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.on_leftdclick)
|
|
329
|
|
330
|
|
331 def on_leftdclick(self,evt):
|
|
332 if self.CanEnableCellControl():
|
|
333 self.EnableCellEditControl()
|
|
334
|
|
335 def on_col_size(self, evt):
|
|
336 col = evt.GetRowOrCol()
|
|
337 cells = self.rows[0].findall('cell')
|
|
338 size = self.GetColSize(col)
|
|
339 cells[col].set('size',str(size))
|
|
340 evt.Skip()
|
|
341
|
|
342 def on_cell_change(self,evt):
|
|
343 row = evt.GetRow()
|
|
344 col = evt.GetCol()
|
|
345 value = self.GetCellValue(row,col)
|
|
346 cells = self.rows[row].findall('cell')
|
|
347 cells[col].text = value
|
|
348 if col == 0:
|
|
349 self.handler.refresh_rows()
|
|
350
|
|
351 def set_col_widths(self):
|
|
352 cells = self.rows[0].findall('cell')
|
|
353 for i in range(0,len(cells)):
|
|
354 try:
|
|
355 size = int(cells[i].get('size'))
|
|
356 self.SetColSize(i,size)
|
|
357 except:
|
|
358 continue
|
|
359
|
|
360 def refresh_row(self,rowi):
|
|
361 cells = self.rows[rowi].findall('cell')
|
|
362 for i in range(0,len(cells)):
|
|
363 text = cells[i].text
|
|
364 if text == None or text == '':
|
|
365 text = ''
|
|
366 cells[i].text = text
|
|
367 self.SetCellValue(rowi,i,text)
|
|
368
|
|
369 def add_row(self,evt=None):
|
|
370 cols = self.GetNumberCols()
|
|
371 row = Element('row')
|
|
372 for i in range(0,cols):
|
|
373 cell = Element('cell')
|
|
374 cell.text = ''
|
|
375 row.append(cell)
|
|
376 self.handler.grid.append(row)
|
|
377 self.AppendRows(1)
|
|
378 self.rows = self.handler.grid.findall('row')
|
|
379 self.handler.refresh_rows()
|
|
380
|
|
381 def add_col(self,evt=None):
|
|
382 for r in self.rows:
|
|
383 cell = Element('cell')
|
|
384 cell.text = ''
|
|
385 r.append(cell)
|
|
386 self.AppendCols(1)
|
|
387 self.set_col_widths()
|
|
388
|
|
389 def del_row(self,evt=None):
|
|
390 num = self.GetNumberRows()
|
|
391 if num == 1:
|
|
392 return
|
|
393 self.handler.grid.remove(self.handler.grid[num-1])#always remove last row -- nasty
|
|
394 self.DeleteRows(num-1,1)
|
|
395 self.rows = self.handler.grid.findall('row')
|
|
396 self.handler.refresh_rows()
|
|
397
|
|
398 def del_col(self,evt=None):
|
|
399 num = self.GetNumberCols()
|
|
400 if num == 1:
|
|
401 return
|
|
402 for r in self.rows:
|
|
403 cells = r.findall('cell')
|
|
404 r.remove(r[num-1])# always remove the last column -- nasty
|
|
405 self.DeleteCols(num-1,1)
|
|
406 self.set_col_widths()
|
|
407
|
|
408
|
|
409 G_TITLE = wx.NewId()
|
|
410 GRID_BOR = wx.NewId()
|
|
411 class rpg_grid_panel(wx.Panel):
|
|
412 def __init__(self, parent, handler):
|
|
413 wx.Panel.__init__(self, parent, -1)
|
|
414 self.handler = handler
|
|
415 self.grid = rpg_grid(self,handler)
|
|
416 label = handler.xml.get('name')
|
|
417 self.main_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
418 self.main_sizer.Add(wx.StaticText(self, -1, label+": "), 0, wx.EXPAND)
|
|
419 self.main_sizer.Add(self.grid,1,wx.EXPAND)
|
|
420 self.SetSizer(self.main_sizer)
|
|
421 self.SetAutoLayout(True)
|
|
422 self.Fit()
|
|
423 parent.SetSize(self.GetBestSize())
|
|
424
|
|
425
|
|
426 G_AUTO_SIZE = wx.NewId()
|
|
427 G_ADD_ROW = wx.NewId()
|
|
428 G_ADD_COL = wx.NewId()
|
|
429 G_DEL_ROW = wx.NewId()
|
|
430 G_DEL_COL = wx.NewId()
|
|
431
|
|
432 class rpg_grid_edit_panel(wx.Panel):
|
|
433 def __init__(self, parent, handler):
|
|
434 wx.Panel.__init__(self, parent, -1)
|
|
435 self.handler = handler
|
|
436 self.grid = rpg_grid(self,handler)
|
|
437 self.title = wx.TextCtrl(self, G_TITLE, handler.xml.get('name'))
|
|
438
|
|
439 radio_b = wx.RadioBox(self, GRID_BOR, "Border (HTML)", choices=["no","yes"])
|
|
440 border = handler.grid.get("border")
|
|
441 radio_b.SetSelection(int(border))
|
|
442
|
|
443 self.auto_size = wx.CheckBox(self, G_AUTO_SIZE, " Auto Size")
|
|
444 if handler.is_autosized() == '1':
|
|
445 self.auto_size.SetValue(True)
|
|
446 else:
|
|
447 self.auto_size.SetValue(False)
|
|
448
|
|
449 sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
450 sizer.Add(wx.Button(self, G_ADD_ROW, "Add Row"), 1, wx.EXPAND)
|
|
451 sizer.Add(wx.Size(10,10))
|
|
452 sizer.Add(wx.Button(self, G_DEL_ROW, "Remove Row"), 1, wx.EXPAND)
|
|
453 sizer.Add(wx.Size(10,10))
|
|
454 sizer.Add(wx.Button(self, G_ADD_COL, "Add Column"), 1, wx.EXPAND)
|
|
455 sizer.Add(wx.Size(10,10))
|
|
456 sizer.Add(wx.Button(self, G_DEL_COL, "Remove Column"), 1, wx.EXPAND)
|
|
457
|
|
458 self.main_sizer = wx.StaticBoxSizer(wx.StaticBox(self,-1,"Grid"), wx.VERTICAL)
|
|
459 self.main_sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND)
|
|
460 self.main_sizer.Add(self.title, 0, wx.EXPAND)
|
|
461 self.main_sizer.Add(radio_b, 0, 0)
|
|
462 self.main_sizer.Add(self.auto_size, 0, 0)
|
|
463 self.main_sizer.Add(self.grid,1,wx.EXPAND)
|
|
464 self.main_sizer.Add(sizer,0,wx.EXPAND)
|
|
465
|
|
466 self.SetSizer(self.main_sizer)
|
|
467 self.SetAutoLayout(True)
|
|
468 self.Fit()
|
|
469
|
|
470 self.Bind(wx.EVT_TEXT, self.on_text, id=G_TITLE)
|
|
471 self.Bind(wx.EVT_BUTTON, self.grid.add_row, id=G_ADD_ROW)
|
|
472 self.Bind(wx.EVT_BUTTON, self.grid.del_row, id=G_DEL_ROW)
|
|
473 self.Bind(wx.EVT_BUTTON, self.grid.add_col, id=G_ADD_COL)
|
|
474 self.Bind(wx.EVT_BUTTON, self.grid.del_col, id=G_DEL_COL)
|
|
475 self.Bind(wx.EVT_RADIOBOX, self.on_radio_box, id=GRID_BOR)
|
|
476 self.Bind(wx.EVT_CHECKBOX, self.on_auto_size, id=G_AUTO_SIZE)
|
|
477
|
|
478 def on_auto_size(self,evt):
|
|
479 self.handler.set_autosize(bool2int(evt.Checked()))
|
|
480
|
|
481 def on_radio_box(self,evt):
|
|
482 id = evt.GetId()
|
|
483 index = evt.GetInt()
|
|
484 if id == GRID_BOR:
|
|
485 self.handler.grid.set("border",str(index))
|
|
486
|
|
487 def on_text(self,evt):
|
|
488 txt = self.title.GetValue()
|
|
489 if txt != "":
|
|
490 self.handler.xml.set('name',txt)
|
|
491 self.handler.rename(txt)
|