29 |
|
|
30 |
from wxPython.wx import * |
from wxPython.wx import * |
31 |
from VisualTable import * |
from VisualTable import * |
32 |
|
from gnue.common.schema.Objects import * |
33 |
|
from Incubator import * |
34 |
|
|
35 |
class VisualEditor (wxPanel): |
class VisualEditor (wxPanel): |
36 |
def __init__(self, instance, root, parent): |
def __init__(self, instance, root, parent): |
37 |
wxPanel.__init__(self,parent,-1) |
wxPanel.__init__(self,parent,-1) |
38 |
self.frame = instance |
self.frame = instance |
39 |
self.panelColor = wxWHITE #wxNamedColor("blue") # self.GetBackgroundColour() |
self.panelColor = self.GetBackgroundColour() |
40 |
self._instance = instance |
self._instance = instance |
41 |
self.rootObject = root |
self.rootObject = root |
42 |
self.current = root |
self.current = root |
43 |
self._app = instance._app |
self._app = instance._app |
44 |
self.dragmode = false |
self.dragmode = false |
45 |
self.tables=[] |
self.tables=[] |
46 |
|
self.hasNewObj = false |
47 |
self.rootObject.walk(self.inventoryObject) |
self.rootObject.walk(self.inventoryObject) |
48 |
|
|
49 |
self.diagram=wxScrolledWindow(self, -1, |
self.diagram=wxScrolledWindow(self, -1, |
50 |
style=wxSUNKEN_BORDER) |
style=wxSUNKEN_BORDER) |
51 |
|
self.panelheight=100 |
52 |
|
self.panelwidth=200 |
53 |
|
self.diagram.EnableScrolling(true, true) |
54 |
|
self.redrawBackground() |
55 |
|
#self.diagram.SetBackgroundColour(wxWHITE) |
56 |
self.calculateTablePositions() |
self.calculateTablePositions() |
57 |
|
|
|
#self.diagram.SetBackgroundColour(wxWHITE) |
|
58 |
|
|
|
self.diagram.EnableScrolling(true, true) |
|
|
|
|
|
self.diagram.SetScrollbars(20, 20, self.panelwidth / 20, |
|
|
self.panelheight / 20) |
|
59 |
|
|
60 |
EVT_PAINT(self.diagram, self.onPaintEvent) |
EVT_PAINT(self.diagram, self.onPaintEvent) |
61 |
# EVT_LEFT_DOWN(self.diagram, self.onLeftClickEvent) |
# EVT_LEFT_DOWN(self.diagram, self.onLeftClickEvent) |
63 |
EVT_LEFT_UP(self.diagram, self.onMouseEvent) |
EVT_LEFT_UP(self.diagram, self.onMouseEvent) |
64 |
EVT_LEFT_DOWN(self.diagram, self.onMouseEvent) |
EVT_LEFT_DOWN(self.diagram, self.onMouseEvent) |
65 |
EVT_LEFT_DCLICK(self.diagram, self.onDoubleClickEvent) |
EVT_LEFT_DCLICK(self.diagram, self.onDoubleClickEvent) |
66 |
|
EVT_RIGHT_DOWN(self.diagram, self.onRightClickEvent) |
67 |
|
|
68 |
Sizer = wxBoxSizer(wxHORIZONTAL) |
Sizer = wxBoxSizer(wxHORIZONTAL) |
69 |
#Sizer.Add(self.toolPalette, 0) |
#Sizer.Add(self.toolPalette, 0) |
73 |
self.SetSizer(Sizer) |
self.SetSizer(Sizer) |
74 |
|
|
75 |
def calculateTablePositions(self): |
def calculateTablePositions(self): |
76 |
dc = wxPaintDC(self.diagram) |
dc = wxMemoryDC() |
77 |
self.diagram.PrepareDC(dc) |
self.diagram.PrepareDC(dc) |
78 |
dc.BeginDrawing() |
dc.BeginDrawing() |
79 |
x=10 |
x=10 |
93 |
y=maxy+10 |
y=maxy+10 |
94 |
|
|
95 |
dc.EndDrawing() |
dc.EndDrawing() |
96 |
self.panelheight=maxy |
self.resizeCanvas(maxx,maxy) |
|
self.panelwidth=maxx |
|
97 |
|
|
98 |
# Rules for better positioning: |
# Rules for better positioning: |
99 |
# tables with most connections in the middle |
# tables with most connections in the middle |
100 |
# ... |
# ... |
101 |
|
|
102 |
|
|
103 |
def inventoryObject(self, object): |
def inventoryObject(self, object): |
104 |
if object._type=="GSTable": |
if object._type=="GSTable": |
105 |
self.tables.append(VisualTable(object)) |
self.tables.append(VisualTable(object)) |
110 |
self.current=object |
self.current=object |
111 |
self.onPaintEvent(None) |
self.onPaintEvent(None) |
112 |
|
|
113 |
|
def findFreePosition(self, x,y,w,h): |
114 |
|
conflict=true |
115 |
|
while conflict: |
116 |
|
conflict=false |
117 |
|
for i in self.tables: |
118 |
|
if i.isInRectangle(x,y,x+w,y+h): |
119 |
|
x=i.xpos+i.width+10 |
120 |
|
if x>500: |
121 |
|
x=10 |
122 |
|
y=i.ypos+i.height+10 |
123 |
|
conflict=true |
124 |
|
self.resizeCanvas(x+w,y+h) |
125 |
|
return (x,y) |
126 |
|
|
127 |
|
|
128 |
|
def resizeCanvas(self,xmax,ymax): |
129 |
|
# resize canvas |
130 |
|
if xmax>self.panelwidth: |
131 |
|
self.panelwidth=xmax+10 |
132 |
|
self.diagram.SetScrollbars(20, 20, self.panelwidth / 20, |
133 |
|
self.panelheight / 20) |
134 |
|
if ymax>self.panelheight: |
135 |
|
self.panelheight=ymax+10 |
136 |
|
self.diagram.SetScrollbars(20, 20, self.panelwidth / 20, |
137 |
|
self.panelheight / 20) |
138 |
|
|
139 |
|
|
140 |
def onCreateObject (self, object, handler): |
def onCreateObject (self, object, handler): |
141 |
pass |
if object._type=="GSTable": |
142 |
|
self.tables.append(VisualTable(object)) |
143 |
|
self.hasNewObj=true |
144 |
|
self.refresh() |
145 |
|
return |
146 |
|
vTbl=VisualTable(object) |
147 |
|
dc = wxClientDC(self.diagram) |
148 |
|
self.diagram.PrepareDC(dc) |
149 |
|
(w,h)=vTbl.calculateDimensions(dc) |
150 |
|
|
151 |
|
# position new Table |
152 |
|
(x,y)=self.findFreePosition(10,10,w,h) |
153 |
|
vTbl.moveTo(x,y) |
154 |
|
|
155 |
|
self.tables.append(vTbl) |
156 |
|
if object._type=="GSField": |
157 |
|
self.hasNewObj=true |
158 |
|
self.onModifyObject(object,None,None) |
159 |
|
|
160 |
|
|
161 |
def onModifyObject (self, object, handler, modifications): |
def onModifyObject (self, object, handler, modifications): |
162 |
pass |
self.refresh() |
163 |
|
return |
164 |
|
tbl=object.findParentOfType('GSTable') |
165 |
|
if tbl!=None: |
166 |
|
for i in self.tables: |
167 |
|
if i.instance==tbl: |
168 |
|
dc = wxClientDC(self.diagram) |
169 |
|
self.diagram.PrepareDC(dc) |
170 |
|
(w,h)=i.calculateDimensions(dc) |
171 |
|
# check if still freepos |
172 |
|
(x,y)=self.findFreePosition(i.xpos,i.ypos,w,h) |
173 |
|
i.moveTo(x,y) |
174 |
|
i.drawMe(dc,self.current) |
175 |
|
return |
176 |
|
|
177 |
|
def refresh(self): |
178 |
|
self.diagram.Refresh(true) |
179 |
|
|
180 |
def onDeleteObject (self, object, handler): |
def onDeleteObject (self, object, handler): |
181 |
pass |
if object._type=="GSTable": |
182 |
|
# search for GSTable and remove the VisualTable assigned to it |
183 |
|
for vTbl in self.tables: |
184 |
|
if vTbl.instance==object: |
185 |
|
self.tables.remove(vTbl) |
186 |
|
self.refresh() |
187 |
|
|
188 |
|
if object._type=="GSField": |
189 |
|
# search for GSTable and update corresponding vtable |
190 |
|
tbl=object.findParentOfType("GSTable") |
191 |
|
for vTbl in self.tables: |
192 |
|
if vTbl.instance==tbl: |
193 |
|
dc = wxMemoryDC() |
194 |
|
vTbl.calculateDimensions(dc) |
195 |
|
self.refresh() |
196 |
|
|
197 |
|
|
198 |
|
|
199 |
def onPaintEvent(self, event, clear=false): |
def onPaintEvent(self, event, clear=false): |
200 |
|
if self.hasNewObj: |
201 |
|
self.calculateTablePositions() |
202 |
|
self.hasNewObj=false |
203 |
dc = wxPaintDC(self.diagram) |
dc = wxPaintDC(self.diagram) |
204 |
self.diagram.PrepareDC(dc) |
self.diagram.PrepareDC(dc) |
205 |
dc.BeginDrawing() |
dc.BeginDrawing() |
206 |
|
#dc.Blit(0,0,self.panelheight,self.panelwidth,self.imagedc,0,0) |
207 |
|
#dc.EndDrawing() |
208 |
|
#return |
209 |
|
#dc.BeginDrawing() |
210 |
if clear: |
if clear: |
211 |
dc.SetBackground(wxLIGHT_GREY_BRUSH) |
dc.SetBackground(self.panelColor) |
212 |
dc.Clear() |
dc.Clear() |
213 |
for vTbl in self.tables: |
for vTbl in self.tables: |
214 |
vTbl.drawMe(dc,self.current) |
vTbl.drawMe(dc,self.current) |
215 |
|
|
216 |
dc.EndDrawing() |
dc.EndDrawing() |
217 |
|
|
218 |
|
def redrawBackground(self): |
219 |
|
btm=wxEmptyBitmap(self.panelheight,self.panelwidth,-1) |
220 |
|
dc=wxMemoryDC() |
221 |
|
dc.SelectObject(btm); |
222 |
|
for vTbl in self.tables: |
223 |
|
vTbl.drawMe(dc,self.current) |
224 |
|
self.imagedc=dc |
225 |
|
|
226 |
def calculateMousePos(self,event): |
def calculateMousePos(self,event): |
227 |
originX, originY = self.diagram.GetViewStart() |
originX, originY = self.diagram.GetViewStart() |
228 |
unitX, unitY = self.diagram.GetScrollPixelsPerUnit() |
unitX, unitY = self.diagram.GetScrollPixelsPerUnit() |
235 |
for vTbl in self.tables: |
for vTbl in self.tables: |
236 |
object=vTbl.getObjectAt(x,y) |
object=vTbl.getObjectAt(x,y) |
237 |
if object != None: |
if object != None: |
238 |
self.tables.reverse() |
self.tables.reverse() |
239 |
return object |
return object |
240 |
self.tables.reverse() |
self.tables.reverse() |
241 |
return None |
# in case, that no object is selected, choose root object |
242 |
|
return self.rootObject |
243 |
|
|
244 |
def onMouseEvent(self,event): |
def onMouseEvent(self,event): |
245 |
if not (event.LeftDown() or event.Dragging() or event.LeftUp()): |
if not (event.LeftDown() or event.Dragging() or event.LeftUp()): |
247 |
if event.LeftDown(): |
if event.LeftDown(): |
248 |
(x,y) = self.calculateMousePos(event) |
(x,y) = self.calculateMousePos(event) |
249 |
object=self.getObjectAt(x,y) |
object=self.getObjectAt(x,y) |
250 |
if object != None: |
if object._type!='VisualTable': |
251 |
if object._type!='VisualTable': |
self._instance.onSetCurrentObject(object, __name__) |
252 |
self._instance.onSetCurrentObject(object, __name__) |
return |
253 |
return |
else: |
254 |
else: |
self.dragx=x-object.xpos |
255 |
self.dragx=x-object.xpos |
self.dragy=y-object.ypos |
256 |
self.dragy=y-object.ypos |
self.dragobj=object |
257 |
self.dragobj=object |
dc = wxClientDC(self.diagram) |
258 |
self.dragmode=true |
self.diagram.PrepareDC(dc) |
259 |
|
self.spriteOn=false |
260 |
|
self.dragmode=true |
261 |
|
|
262 |
elif event.LeftUp() and self.dragmode: |
elif event.LeftUp() and self.dragmode: |
263 |
self.dragmode=false |
self.dragmode=false |
264 |
self.onPaintEvent(None,true) |
self.refresh() |
265 |
|
|
266 |
elif event.Dragging() and self.dragmode: |
elif event.Dragging() and self.dragmode: |
267 |
(x,y) = self.calculateMousePos(event) |
(x,y) = self.calculateMousePos(event) |
268 |
dc = wxPaintDC(self.diagram) |
dc = wxClientDC(self.diagram) |
269 |
self.diagram.PrepareDC(dc) |
self.diagram.PrepareDC(dc) |
270 |
dc.BeginDrawing() |
dc.BeginDrawing() |
271 |
dc.SetLogicalFunction(wxXOR) |
dc.SetLogicalFunction(wxXOR) |
272 |
self.dragobj.drawMe(dc,self.current) |
if self.spriteOn: |
273 |
|
self.dragobj.drawMe(dc,self.current,true) |
274 |
|
|
275 |
self.dragobj.moveTo(x-self.dragx,y-self.dragy) |
self.dragobj.moveTo(x-self.dragx,y-self.dragy) |
276 |
self.dragobj.drawMe(dc,self.current) |
self.spriteOn=true |
277 |
# dc.drawRec |
self.dragobj.drawMe(dc,self.current,true) |
278 |
# TODO: Begin to use a wxMemoryDC where the screen is drawn, and |
|
279 |
|
# TODO: implement a way to move tables as a whole and not |
280 |
|
# only showing the border of the window. |
281 |
|
# one way would be to use a wxMemoryDC to paint into, and |
282 |
# which is copied into the real window -> no flickering screen |
# which is copied into the real window -> no flickering screen |
283 |
# when a object should be moved, first delete it, and repaint the |
# when a object should be moved, first delete it, and repaint the |
284 |
# wxMemoryDC, then you can move it on the real DC and you can |
# wxMemoryDC, then you can move it on the real DC and you can |
286 |
|
|
287 |
dc.EndDrawing() |
dc.EndDrawing() |
288 |
|
|
289 |
|
# in case of a right mouse click show a context menu |
290 |
|
def onRightClickEvent(self,event): |
291 |
|
(x,y) = self.calculateMousePos(event) |
292 |
|
object=self.getObjectAt(x,y) |
293 |
|
menu = wxMenu() |
294 |
|
|
295 |
|
popup_NEWTABLE =1 |
296 |
|
popup_NEWFIELD =2 |
297 |
|
popup_NEWINDEX =3 |
298 |
|
popup_NEWUINDEX=4 |
299 |
|
popup_EDIT =5 |
300 |
|
popup_RENAME =6 |
301 |
|
popup_DUPPL =7 |
302 |
|
popup_DEL =8 |
303 |
|
|
304 |
|
# TODO: Use a generalized popup menu like in the forms designer |
305 |
|
# instead of this one. the popup menu in the treeview and |
306 |
|
# the visual editor should look the same |
307 |
|
|
308 |
|
if object._type=='VisualTable': |
309 |
|
object=object.instance |
310 |
|
|
311 |
|
self._instance.onSetCurrentObject(object, __name__) |
312 |
|
|
313 |
|
if object._type=='VisualTable' or object._type=="GSTable": |
314 |
|
menu.Append(popup_NEWFIELD, "Add Field") |
315 |
|
EVT_MENU(self,popup_NEWFIELD,self.onAddField) |
316 |
|
|
317 |
|
elif object._type=="GSField": |
318 |
|
menu.Append(popup_NEWINDEX, "Add Index") |
319 |
|
EVT_MENU(self,popup_NEWINDEX,self.onAddIndex) |
320 |
|
|
321 |
|
menu.Append(popup_NEWUINDEX, "Add Unique Index") |
322 |
|
EVT_MENU(self,popup_NEWUINDEX,self.onAddUniqueIndex) |
323 |
|
|
324 |
|
else: |
325 |
|
menu.Append(popup_NEWTABLE, "New Table") |
326 |
|
EVT_MENU(self,popup_NEWTABLE,self.onNewTable) |
327 |
|
|
328 |
|
if object._type!='GSSchema': |
329 |
|
menu.AppendSeparator() |
330 |
|
|
331 |
|
menu.Append(popup_EDIT, "Edit") |
332 |
|
EVT_MENU(self,popup_EDIT,self.onEdit) |
333 |
|
|
334 |
|
menu.Append(popup_RENAME, "Rename") |
335 |
|
EVT_MENU(self,popup_RENAME,self.onRename) |
336 |
|
|
337 |
|
menu.Append(popup_DUPPL, "Duplicate") |
338 |
|
EVT_MENU(self,popup_DUPPL,self.onDuplicate) |
339 |
|
|
340 |
|
menu.Append(popup_DEL, "Delete") |
341 |
|
EVT_MENU(self,popup_DEL,self.onDelete) |
342 |
|
|
343 |
|
|
344 |
|
clickPt = wxPoint(x + self.diagram.GetPosition().x, |
345 |
|
y + self.diagram.GetPosition().y) |
346 |
|
self.diagram.PopupMenu(menu, clickPt) |
347 |
|
menu.Destroy() |
348 |
|
|
349 |
|
def onNewTable(self,event): |
350 |
|
dialog = wxTextEntryDialog(self.diagram,'Name of the new table:') |
351 |
|
|
352 |
|
if dialog.ShowModal() == wxID_OK: |
353 |
|
tables=self.rootObject.findChildOfType("GSTables") |
354 |
|
if tables==None: |
355 |
|
tables = createObject(self._instance, self.rootObject, 'tables', |
356 |
|
None,None,self.rootObject) |
357 |
|
createObject(self._instance, self.rootObject, 'table', |
358 |
|
None,None,tables,{"name":dialog.GetValue()}) |
359 |
|
|
360 |
|
dialog.Destroy() |
361 |
|
|
362 |
|
def onAddField(self,event): |
363 |
|
dialog = wxTextEntryDialog(self.diagram,'Name of the new field:') |
364 |
|
|
365 |
|
if dialog.ShowModal() == wxID_OK: |
366 |
|
fields=self.current.findChildOfType("GSFields") |
367 |
|
if fields==None: |
368 |
|
tbl=self.current.findChildOfType("GSTable") |
369 |
|
fields=createObject(self._instance, self.rootObject,'fields', |
370 |
|
None,None,tbl) |
371 |
|
|
372 |
|
createObject(self._instance, self.rootObject, 'field', |
373 |
|
None,None,fields,{"name":dialog.GetValue(), |
374 |
|
"type":"varchar", |
375 |
|
"size":20}) |
376 |
|
|
377 |
|
dialog.Destroy() |
378 |
|
|
379 |
|
def onAddIndex(self,event): |
380 |
|
tbl=self.current.findParentOfType("GSTable") |
381 |
|
indexes=tbl.findChildOfType("GSIndexes") |
382 |
|
if indexes==None: |
383 |
|
indexes = createObject(self._instance, self.rootObject, 'indexes', |
384 |
|
None,None,tbl) |
385 |
|
idx=createObject(self._instance, self.rootObject, 'index', |
386 |
|
None,None,indexes, |
387 |
|
{"name":"%s_%s" % (tbl.name,self.current.name)}) |
388 |
|
createObject(self._instance, self.rootObject, 'index', |
389 |
|
None,None,idx, |
390 |
|
{"name":self.current.name}) |
391 |
|
self.refresh() |
392 |
|
|
393 |
|
def onAddUniqueIndex(self,event): |
394 |
|
tbl=self.current.findParentOfType("GSTable") |
395 |
|
indexes=tbl.findChildOfType("GSIndexes") |
396 |
|
if indexes==None: |
397 |
|
indexes = createObject(self._instance, self.rootObject, 'indexes', |
398 |
|
None,None,tbl) |
399 |
|
idx=createObject(self._instance, self.rootObject, 'index', |
400 |
|
None,None,indexes, |
401 |
|
{"name":"%s_%s" % (tbl.name,self.current.name), |
402 |
|
"unique":"Y"}) |
403 |
|
createObject(self._instance, self.rootObject, 'index', |
404 |
|
None,None,idx, |
405 |
|
{"name":self.current.name}) |
406 |
|
self.refresh() |
407 |
|
|
408 |
|
|
409 |
|
def onAddConstraint(self,event): |
410 |
|
pass |
411 |
|
|
412 |
|
def onRename(self,event): |
413 |
|
dialog = wxTextEntryDialog(self.diagram,'New Name') |
414 |
|
if hasattr(self.current,'name'): |
415 |
|
dialog.SetValue(self.current.name) |
416 |
|
|
417 |
|
if dialog.ShowModal() == wxID_OK: |
418 |
|
self.current.name=dialog.GetValue() |
419 |
|
self._instance.onModifyObject(self.current,"schema", |
420 |
|
[('name',self.current.name)]) |
421 |
|
self.refresh() |
422 |
|
|
423 |
|
dialog.Destroy() |
424 |
|
|
425 |
|
def onDuplicate(self,event): |
426 |
|
pass |
427 |
|
|
428 |
|
def onEdit(self,event): |
429 |
|
print "EDIT occured" |
430 |
|
pass |
431 |
|
|
432 |
|
def onDelete(self,event): |
433 |
|
deleteObject(self._instance, self.rootObject, self.current, firstRun=1) |
434 |
|
|
435 |
|
|
436 |
def onLeftClickEvent(self,event): |
def onLeftClickEvent(self,event): |
437 |
(x,y) = self.calculateMousePos(event) |
(x,y) = self.calculateMousePos(event) |