1 | # -*- coding: utf-8 -*- |
---|
2 | #Copyright (c) 2007, Playful Invention Company |
---|
3 | #Copyright (c) 2008-9, Walter Bender |
---|
4 | #Copyright (c) 2009, Raúl Gutiérrez Segalés |
---|
5 | |
---|
6 | #Permission is hereby granted, free of charge, to any person obtaining a copy |
---|
7 | #of this software and associated documentation files (the "Software"), to deal |
---|
8 | #in the Software without restriction, including without limitation the rights |
---|
9 | #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
---|
10 | #copies of the Software, and to permit persons to whom the Software is |
---|
11 | #furnished to do so, subject to the following conditions: |
---|
12 | |
---|
13 | #The above copyright notice and this permission notice shall be included in |
---|
14 | #all copies or substantial portions of the Software. |
---|
15 | |
---|
16 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
---|
17 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
---|
18 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
---|
19 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
---|
20 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
---|
21 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
---|
22 | #THE SOFTWARE. |
---|
23 | |
---|
24 | import pygtk |
---|
25 | pygtk.require('2.0') |
---|
26 | import gtk |
---|
27 | import pango |
---|
28 | import gobject |
---|
29 | import os |
---|
30 | import os.path |
---|
31 | import time |
---|
32 | |
---|
33 | # Import from Journal for these blocks |
---|
34 | importblocks = ['audiooff', 'descriptionoff','journal'] |
---|
35 | |
---|
36 | class taWindow: pass |
---|
37 | |
---|
38 | from math import atan2, pi |
---|
39 | DEGTOR = 2*pi/360 |
---|
40 | |
---|
41 | from tasetup import * |
---|
42 | from tasprites import * |
---|
43 | from talogo import * |
---|
44 | from taturtle import * |
---|
45 | from taproject import * |
---|
46 | try: |
---|
47 | from sugar.graphics.objectchooser import ObjectChooser |
---|
48 | except: |
---|
49 | pass |
---|
50 | |
---|
51 | from tahoverhelp import * |
---|
52 | from gettext import gettext as _ |
---|
53 | |
---|
54 | # dead key dictionaries |
---|
55 | dead_grave = {'A':192,'E':200,'I':204,'O':210,'U':217,'a':224,'e':232,'i':236,\ |
---|
56 | 'o':242,'u':249} |
---|
57 | dead_acute = {'A':193,'E':201,'I':205,'O':211,'U':218,'a':225,'e':233,'i':237,\ |
---|
58 | 'o':243,'u':250} |
---|
59 | dead_circumflex = {'A':194,'E':202,'I':206,'O':212,'U':219,'a':226,'e':234,\ |
---|
60 | 'i':238,'o':244,'u':251} |
---|
61 | dead_tilde = {'A':195,'O':211,'N':209,'U':360,'a':227,'o':245,'n':241,'u':361} |
---|
62 | dead_diaeresis = {'A':196,'E':203,'I':207,'O':211,'U':218,'a':228,'e':235,\ |
---|
63 | 'i':239,'o':245,'u':252} |
---|
64 | dead_abovering = {'A':197,'a':229} |
---|
65 | |
---|
66 | # Time out for triggering help |
---|
67 | timeout_tag = [0] |
---|
68 | |
---|
69 | |
---|
70 | # |
---|
71 | # Setup |
---|
72 | # |
---|
73 | |
---|
74 | def twNew(win, path, lang, parent=None): |
---|
75 | tw = taWindow() |
---|
76 | tw.window = win |
---|
77 | tw.path = os.path.join(path,'images') |
---|
78 | tw.path_lang = os.path.join(path,'images',lang) |
---|
79 | tw.path_en = os.path.join(path,'images/en') # en as fallback |
---|
80 | tw.load_save_folder = os.path.join(path,'samples') |
---|
81 | tw.save_folder = None |
---|
82 | tw.save_file_name = None |
---|
83 | win.set_flags(gtk.CAN_FOCUS) |
---|
84 | tw.width = gtk.gdk.screen_width() |
---|
85 | tw.height = gtk.gdk.screen_height() |
---|
86 | # starting from command line |
---|
87 | if parent is None: |
---|
88 | win.show_all() |
---|
89 | # starting from Sugar |
---|
90 | else: |
---|
91 | parent.show_all() |
---|
92 | win.add_events(gtk.gdk.BUTTON_PRESS_MASK) |
---|
93 | win.add_events(gtk.gdk.BUTTON_RELEASE_MASK) |
---|
94 | win.add_events(gtk.gdk.POINTER_MOTION_MASK) |
---|
95 | win.add_events(gtk.gdk.KEY_PRESS_MASK) |
---|
96 | win.connect("expose-event", expose_cb, tw) |
---|
97 | win.connect("button-press-event", buttonpress_cb, tw) |
---|
98 | win.connect("button-release-event", buttonrelease_cb, tw) |
---|
99 | win.connect("motion-notify-event", move_cb, tw) |
---|
100 | win.connect("key_press_event", keypress_cb, tw) |
---|
101 | tw.keypress = "" |
---|
102 | tw.keyvalue = 0 |
---|
103 | tw.dead_key = "" |
---|
104 | tw.area = win.window |
---|
105 | tw.gc = tw.area.new_gc() |
---|
106 | # on an OLPC-XO-1, there is a scaling factor |
---|
107 | if os.path.exists('/etc/olpc-release') or \ |
---|
108 | os.path.exists('/sys/power/olpc-pm'): |
---|
109 | tw.lead = 1.6 |
---|
110 | tw.scale = 1.0 |
---|
111 | else: |
---|
112 | tw.lead = 1.0 |
---|
113 | tw.scale = 1.6 |
---|
114 | tw.cm = tw.gc.get_colormap() |
---|
115 | tw.rgb = [255,0,0] |
---|
116 | tw.bgcolor = tw.cm.alloc_color('#fff8de') |
---|
117 | tw.msgcolor = tw.cm.alloc_color('black') |
---|
118 | tw.fgcolor = tw.cm.alloc_color('red') |
---|
119 | tw.textcolor = tw.cm.alloc_color('blue') |
---|
120 | tw.textsize = 32 |
---|
121 | tw.sprites = [] |
---|
122 | tw.selected_block = None |
---|
123 | tw.draggroup = None |
---|
124 | prep_selectors(tw) |
---|
125 | tw.myblock = None |
---|
126 | tw.nop = 'nop' |
---|
127 | tw.loaded = 0 |
---|
128 | for s in selectors: |
---|
129 | setup_selectors(tw,s) |
---|
130 | setup_misc(tw) |
---|
131 | tw.step_time = 0 |
---|
132 | tw.hide = False |
---|
133 | tw.palette = True |
---|
134 | select_category(tw, tw.selbuttons[0]) |
---|
135 | tw.coord_scale = 1 |
---|
136 | tw.turtle = tNew(tw,tw.width,tw.height) |
---|
137 | tw.lc = lcNew(tw) |
---|
138 | tw.buddies = [] |
---|
139 | tw.dx = 0 |
---|
140 | tw.dy = 0 |
---|
141 | tw.cartesian = False |
---|
142 | tw.polar = False |
---|
143 | tw.spr = None # "currently selected spr" |
---|
144 | return tw |
---|
145 | |
---|
146 | # |
---|
147 | # Button Press |
---|
148 | # |
---|
149 | |
---|
150 | def buttonpress_cb(win, event, tw): |
---|
151 | win.grab_focus() |
---|
152 | x, y = xy(event) |
---|
153 | button_press(tw, event.get_state()>k.gdk.CONTROL_MASK, x, y) |
---|
154 | # if sharing, send button press |
---|
155 | if hasattr(tw, 'activity') and \ |
---|
156 | hasattr(tw.activity, 'chattube') and tw.activity.chattube is not None: |
---|
157 | # print "sending button pressed" |
---|
158 | if event.get_state()>k.gdk.CONTROL_MASK is True: |
---|
159 | tw.activity._send_event("p:"+str(x)+":"+str(y)+":"+'T') |
---|
160 | else: |
---|
161 | tw.activity._send_event("p:"+str(x)+":"+str(y)+":"+'F') |
---|
162 | return True |
---|
163 | |
---|
164 | def button_press(tw, mask, x, y, verbose=False): |
---|
165 | if verbose: |
---|
166 | print "processing remote button press: " + str(x) + " " + str(y) |
---|
167 | tw.block_operation = 'click' |
---|
168 | if tw.selected_block!=None: |
---|
169 | unselect(tw) |
---|
170 | else: |
---|
171 | setlayer(tw.status_spr,400) |
---|
172 | spr = findsprite(tw,(x,y)) |
---|
173 | tw.x, tw.y = x,y |
---|
174 | tw.dx = 0 |
---|
175 | tw.dy = 0 |
---|
176 | if spr is None: |
---|
177 | return True |
---|
178 | if spr.type == "canvas": |
---|
179 | return True |
---|
180 | elif spr.type == 'selbutton': |
---|
181 | select_category(tw,spr) |
---|
182 | elif spr.type == 'category': |
---|
183 | block_selector_pressed(tw,x,y) |
---|
184 | elif spr.type == 'block': |
---|
185 | block_pressed(tw,mask,x,y,spr) |
---|
186 | elif spr.type == 'turtle': |
---|
187 | turtle_pressed(tw,x,y) |
---|
188 | tw.spr = spr |
---|
189 | |
---|
190 | def block_selector_pressed(tw,x,y): |
---|
191 | proto = get_proto_from_category(tw,x,y) |
---|
192 | if proto==None: |
---|
193 | return |
---|
194 | if proto is not 'hide': |
---|
195 | new_block_from_category(tw,proto,x,y) |
---|
196 | else: |
---|
197 | hideshow_palette(tw,False) |
---|
198 | |
---|
199 | def hideshow_palette(tw,state): |
---|
200 | if state is False: |
---|
201 | tw.palette == False |
---|
202 | if hasattr(tw,'activity'): |
---|
203 | # Use new toolbar design |
---|
204 | tw.activity.do_hidepalette() |
---|
205 | hide_palette(tw) |
---|
206 | else: |
---|
207 | tw.palette == True |
---|
208 | if hasattr(tw,'activity'): |
---|
209 | # Use new toolbar design |
---|
210 | tw.activity.do_showpalette() |
---|
211 | show_palette(tw) |
---|
212 | |
---|
213 | def show_palette(tw): |
---|
214 | for i in tw.selbuttons: setlayer(i,800) |
---|
215 | select_category(tw,tw.selbuttons[0]) |
---|
216 | tw.palette = True |
---|
217 | |
---|
218 | def hide_palette(tw): |
---|
219 | for i in tw.selbuttons: hide(i) |
---|
220 | setshape(tw.category_spr, tw.hidden_palette_icon) |
---|
221 | tw.palette = False |
---|
222 | |
---|
223 | def get_proto_from_category(tw,x,y): |
---|
224 | dx,dy = x-tw.category_spr.x, y-tw.category_spr.y, |
---|
225 | pixel = getpixel(tw.current_category.mask,dx,dy) |
---|
226 | index = ((pixel%256)>>3)-1 |
---|
227 | if index==0: |
---|
228 | return 'hide' |
---|
229 | index-=1 |
---|
230 | if index>len(tw.current_category.blockprotos): |
---|
231 | return None |
---|
232 | return tw.current_category.blockprotos[index] |
---|
233 | |
---|
234 | def select_category(tw, spr): |
---|
235 | if hasattr(tw, 'current_category'): |
---|
236 | setshape(tw.current_category, tw.current_category.offshape) |
---|
237 | setshape(spr, spr.onshape) |
---|
238 | tw.current_category = spr |
---|
239 | setshape(tw.category_spr,spr.group) |
---|
240 | |
---|
241 | def new_block_from_category(tw,proto,x,y): |
---|
242 | if proto is None: |
---|
243 | return True |
---|
244 | # load alternative image of nop block if python code is loaded |
---|
245 | if proto.name == 'nop' and tw.nop == 'pythonloaded': |
---|
246 | newspr = sprNew(tw,x-20,y-20,tw.media_shapes['pythonloaded']) |
---|
247 | else: |
---|
248 | newspr = sprNew(tw,x-20,y-20,proto.image) |
---|
249 | setlayer(newspr,2000) |
---|
250 | tw.dragpos = 20,20 |
---|
251 | newspr.type = 'block' |
---|
252 | newspr.proto = proto |
---|
253 | if tw.defdict.has_key(newspr.proto.name): |
---|
254 | newspr.label=tw.defdict[newspr.proto.name] |
---|
255 | newspr.connections = [None]*len(proto.docks) |
---|
256 | for i in range(len(proto.defaults)): |
---|
257 | dock = proto.docks[i+1] |
---|
258 | argproto = tw.protodict[tw.valdict[dock[0]]] |
---|
259 | argdock = argproto.docks[0] |
---|
260 | nx,ny = newspr.x+dock[2]-argdock[2],newspr.y+dock[3]-argdock[3] |
---|
261 | argspr = sprNew(tw,nx,ny,argproto.image) |
---|
262 | argspr.type = 'block' |
---|
263 | argspr.proto = argproto |
---|
264 | argspr.label = str(proto.defaults[i]) |
---|
265 | setlayer(argspr,2000) |
---|
266 | argspr.connections = [newspr,None] |
---|
267 | newspr.connections[i+1] = argspr |
---|
268 | tw.draggroup = findgroup(newspr) |
---|
269 | tw.block_operation = 'new' |
---|
270 | |
---|
271 | def block_pressed(tw,mask,x,y,spr): |
---|
272 | if spr is not None: |
---|
273 | if hasattr(spr,'proto'): |
---|
274 | print "block_pressed (%s)" % spr.proto.name |
---|
275 | tw.draggroup = findgroup(spr) |
---|
276 | for b in tw.draggroup: setlayer(b,2000) |
---|
277 | if spr.connections[0] != None and spr.proto.name == 'lock': |
---|
278 | b = find_top_block(spr) |
---|
279 | tw.dragpos = x-b.x,y-b.y |
---|
280 | else: |
---|
281 | tw.dragpos = x-spr.x,y-spr.y |
---|
282 | disconnect(spr) |
---|
283 | |
---|
284 | def turtle_pressed(tw,x,y): |
---|
285 | dx,dy = x-tw.turtle.spr.x-30,y-tw.turtle.spr.y-30 |
---|
286 | if dx*dx+dy*dy > 200: |
---|
287 | tw.dragpos = ('turn', \ |
---|
288 | tw.turtle.heading-atan2(dy,dx)/DEGTOR,0) |
---|
289 | else: |
---|
290 | tw.dragpos = ('move', x-tw.turtle.spr.x,y-tw.turtle.spr.y) |
---|
291 | tw.draggroup = [tw.turtle.spr] |
---|
292 | |
---|
293 | # |
---|
294 | # Mouse move |
---|
295 | # |
---|
296 | |
---|
297 | def move_cb(win, event, tw): |
---|
298 | x,y = xy(event) |
---|
299 | mouse_move(tw, x, y) |
---|
300 | return True |
---|
301 | |
---|
302 | def mouse_move(tw, x, y, verbose=False, mdx=0, mdy=0): |
---|
303 | if verbose: |
---|
304 | print "processing remote mouse move: " + str(x) + " " + str(y) |
---|
305 | if tw.draggroup is None: |
---|
306 | # popup help from RGS |
---|
307 | spr = findsprite(tw,(x,y)) |
---|
308 | if spr and spr.type == 'category': |
---|
309 | proto = get_proto_from_category(tw,x,y) |
---|
310 | if proto and proto!='hide': |
---|
311 | if timeout_tag[0] == 0: |
---|
312 | timeout_tag[0] = showPopup(proto.name,tw) |
---|
313 | tw.spr = spr |
---|
314 | return |
---|
315 | else: |
---|
316 | if timeout_tag[0] > 0: |
---|
317 | try: |
---|
318 | gobject.source_remove(timeout_tag[0]) |
---|
319 | timeout_tag[0] = 0 |
---|
320 | except: |
---|
321 | timeout_tag[0] = 0 |
---|
322 | elif spr and spr.type == 'selbutton': |
---|
323 | if timeout_tag[0] == 0: |
---|
324 | timeout_tag[0] = showPopup(spr.name,tw) |
---|
325 | tw.spr = spr |
---|
326 | else: |
---|
327 | if timeout_tag[0] > 0: |
---|
328 | try: |
---|
329 | gobject.source_remove(timeout_tag[0]) |
---|
330 | timeout_tag[0] = 0 |
---|
331 | except: |
---|
332 | timeout_tag[0] = 0 |
---|
333 | elif spr and spr.type == 'block': |
---|
334 | if timeout_tag[0] == 0: |
---|
335 | timeout_tag[0] = showPopup(spr.proto.name,tw) |
---|
336 | tw.spr = spr |
---|
337 | else: |
---|
338 | if timeout_tag[0] > 0: |
---|
339 | try: |
---|
340 | gobject.source_remove(timeout_tag[0]) |
---|
341 | timeout_tag[0] = 0 |
---|
342 | except: |
---|
343 | timeout_tag[0] = 0 |
---|
344 | else: |
---|
345 | if timeout_tag[0] > 0: |
---|
346 | try: |
---|
347 | gobject.source_remove(timeout_tag[0]) |
---|
348 | timeout_tag[0] = 0 |
---|
349 | except: |
---|
350 | timeout_tag[0] = 0 |
---|
351 | return |
---|
352 | tw.block_operation = 'move' |
---|
353 | spr = tw.draggroup[0] |
---|
354 | if spr.type == 'block': |
---|
355 | tw.spr = spr |
---|
356 | dragx, dragy = tw.dragpos |
---|
357 | if mdx != 0 or mdy != 0: |
---|
358 | dx,dy = mdx,mdy |
---|
359 | else: |
---|
360 | dx,dy = x-dragx-spr.x,y-dragy-spr.y |
---|
361 | # skip if there was a move of 0,0 |
---|
362 | if dx == 0 and dy == 0: |
---|
363 | return |
---|
364 | # drag entire stack if moving lock block |
---|
365 | if spr.proto.name == 'lock': |
---|
366 | tw.draggroup = findgroup(find_top_block(spr)) |
---|
367 | else: |
---|
368 | tw.draggroup = findgroup(spr) |
---|
369 | # check to see if any block ends up with a negative x |
---|
370 | for b in tw.draggroup: |
---|
371 | if b.x+dx < 0: |
---|
372 | dx += -(b.x+dx) |
---|
373 | # move the stack |
---|
374 | for b in tw.draggroup: |
---|
375 | move(b,(b.x+dx, b.y+dy)) |
---|
376 | elif spr.type=='turtle': |
---|
377 | type,dragx,dragy = tw.dragpos |
---|
378 | if type == 'move': |
---|
379 | if mdx != 0 or mdy != 0: |
---|
380 | dx,dy = mdx,mdy |
---|
381 | else: |
---|
382 | dx,dy = x-dragx-spr.x,y-dragy-spr.y |
---|
383 | move(spr, (spr.x+dx, spr.y+dy)) |
---|
384 | else: |
---|
385 | if mdx != 0 or mdy != 0: |
---|
386 | dx,dy = mdx,mdy |
---|
387 | else: |
---|
388 | dx,dy = x-spr.x-30,y-spr.y-30 |
---|
389 | seth(tw.turtle, int(dragx+atan2(dy,dx)/DEGTOR+5)/10*10) |
---|
390 | if mdx != 0 or mdy != 0: |
---|
391 | dx,dy = 0,0 |
---|
392 | else: |
---|
393 | tw.dx += dx |
---|
394 | tw.dy += dy |
---|
395 | |
---|
396 | # |
---|
397 | # Button release |
---|
398 | # |
---|
399 | |
---|
400 | def buttonrelease_cb(win, event, tw): |
---|
401 | x,y = xy(event) |
---|
402 | button_release(tw, x, y) |
---|
403 | if hasattr(tw, 'activity') and \ |
---|
404 | hasattr(tw.activity, 'chattube') and tw.activity.chattube is not None: |
---|
405 | # print "sending release button" |
---|
406 | tw.activity._send_event("r:"+str(x)+":"+str(y)) |
---|
407 | return True |
---|
408 | |
---|
409 | def button_release(tw, x, y, verbose=False): |
---|
410 | if tw.dx != 0 or tw.dy != 0: |
---|
411 | if hasattr(tw, 'activity') and \ |
---|
412 | hasattr(tw.activity, 'chattube') and \ |
---|
413 | tw.activity.chattube is not None: |
---|
414 | if verbose: |
---|
415 | print "processing move: " + str(tw.dx) + " " + str(tw.dy) |
---|
416 | tw.activity._send_event("m:"+str(tw.dx)+":"+str(tw.dy)) |
---|
417 | tw.dx = 0 |
---|
418 | tw.dy = 0 |
---|
419 | if verbose: |
---|
420 | print "processing remote button release: " + str(x) + " " + str(y) |
---|
421 | if tw.draggroup == None: |
---|
422 | return |
---|
423 | spr = tw.draggroup[0] |
---|
424 | if hasattr(spr,'proto'): |
---|
425 | print "processing %s block in button release" % (spr.proto.name) |
---|
426 | print "block operation is %s" % (tw.block_operation) |
---|
427 | if spr.type == 'turtle': |
---|
428 | tw.turtle.xcor = tw.turtle.spr.x-tw.turtle.canvas.x- \ |
---|
429 | tw.turtle.canvas.width/2+30 |
---|
430 | tw.turtle.ycor = tw.turtle.canvas.height/2-tw.turtle.spr.y+ \ |
---|
431 | tw.turtle.canvas.y-30 |
---|
432 | move_turtle(tw.turtle) |
---|
433 | display_coordinates(tw) |
---|
434 | tw.draggroup = None |
---|
435 | return |
---|
436 | if tw.block_operation=='move' and hit(tw.category_spr, (x,y)): |
---|
437 | for b in tw.draggroup: hide(b) |
---|
438 | tw.draggroup = None |
---|
439 | return |
---|
440 | if tw.block_operation=='new': |
---|
441 | for b in tw.draggroup: |
---|
442 | move(b, (b.x+200, b.y)) |
---|
443 | snap_to_dock(tw) |
---|
444 | for b in tw.draggroup: setlayer(b,650) |
---|
445 | tw.draggroup = None |
---|
446 | print "block operation: %s" % (tw.block_operation) |
---|
447 | if tw.block_operation=='click': |
---|
448 | print "block operation is click" |
---|
449 | if tw.spr.proto.name=='number': |
---|
450 | print "number block selected" |
---|
451 | tw.selected_block = spr |
---|
452 | move(tw.select_mask, (spr.x-5,spr.y-5)) |
---|
453 | setlayer(tw.select_mask, 660) |
---|
454 | print "setting selection mask" |
---|
455 | tw.firstkey = True |
---|
456 | elif tw.defdict.has_key(spr.proto.name): |
---|
457 | tw.selected_block = spr |
---|
458 | if tw.spr.proto.name=='string': |
---|
459 | print "string block selected" |
---|
460 | move(tw.select_mask_string, (spr.x-5,spr.y-5)) |
---|
461 | setlayer(tw.select_mask_string, 660) |
---|
462 | print "setting selection mask" |
---|
463 | tw.firstkey = True |
---|
464 | elif tw.spr.proto.name in importblocks: |
---|
465 | import_from_journal(tw, spr) |
---|
466 | elif tw.spr.proto.name=='nop' and tw.myblock==None: |
---|
467 | tw.activity.import_py() |
---|
468 | else: run_stack(tw, spr) |
---|
469 | |
---|
470 | def import_from_journal(tw, spr): |
---|
471 | if hasattr(tw,"activity"): |
---|
472 | chooser = ObjectChooser('Choose image', None,\ |
---|
473 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) |
---|
474 | try: |
---|
475 | result = chooser.run() |
---|
476 | if result == gtk.RESPONSE_ACCEPT: |
---|
477 | dsobject = chooser.get_selected_object() |
---|
478 | # change block graphic to indicate that object is "loaded" |
---|
479 | if spr.proto.name == 'journal': |
---|
480 | load_image(tw, dsobject, spr) |
---|
481 | elif spr.proto.name == 'audiooff': |
---|
482 | setimage(spr,tw.media_shapes['audioon']) |
---|
483 | else: |
---|
484 | setimage(spr, tw.media_shapes['decson']) |
---|
485 | spr.ds_id = dsobject.object_id |
---|
486 | dsobject.destroy() |
---|
487 | finally: |
---|
488 | chooser.destroy() |
---|
489 | del chooser |
---|
490 | else: |
---|
491 | print "Journal Object Chooser unavailable from outside of Sugar" |
---|
492 | |
---|
493 | # Replace Journal block graphic with preview image |
---|
494 | def load_image(tw, picture, spr): |
---|
495 | from talogo import get_pixbuf_from_journal |
---|
496 | pixbuf = get_pixbuf_from_journal(picture,spr.width,spr.height) |
---|
497 | if pixbuf is not None: |
---|
498 | setimage(spr, pixbuf) |
---|
499 | else: |
---|
500 | setimage(spr, tw.media_shapes['texton']) |
---|
501 | |
---|
502 | # change the icon for user-defined blocks after Python code is loaded |
---|
503 | def set_userdefined(tw): |
---|
504 | list = tw.sprites[:] |
---|
505 | for spr in list: |
---|
506 | if hasattr(spr,'proto') and spr.proto.name == 'nop': |
---|
507 | setimage(spr,tw.media_shapes['pythonloaded']) |
---|
508 | tw.nop = 'pythonloaded' |
---|
509 | |
---|
510 | def snap_to_dock(tw): |
---|
511 | d=200 |
---|
512 | me = tw.draggroup[0] |
---|
513 | for mydockn in range(len(me.proto.docks)): |
---|
514 | for you in blocks(tw): |
---|
515 | if you in tw.draggroup: |
---|
516 | continue |
---|
517 | for yourdockn in range(len(you.proto.docks)): |
---|
518 | thisxy = dock_dx_dy(you,yourdockn,me,mydockn) |
---|
519 | if magnitude(thisxy)>d: |
---|
520 | continue |
---|
521 | d=magnitude(thisxy) |
---|
522 | bestxy=thisxy |
---|
523 | bestyou=you |
---|
524 | bestyourdockn=yourdockn |
---|
525 | bestmydockn=mydockn |
---|
526 | if d<200: |
---|
527 | for b in tw.draggroup: |
---|
528 | move(b,(b.x+bestxy[0],b.y+bestxy[1])) |
---|
529 | blockindock=bestyou.connections[bestyourdockn] |
---|
530 | if blockindock!=None: |
---|
531 | for b in findgroup(blockindock): |
---|
532 | hide(b) |
---|
533 | bestyou.connections[bestyourdockn]=me |
---|
534 | me.connections[bestmydockn]=bestyou |
---|
535 | |
---|
536 | def dock_dx_dy(block1,dock1n,block2,dock2n): |
---|
537 | dock1 = block1.proto.docks[dock1n] |
---|
538 | dock2 = block2.proto.docks[dock2n] |
---|
539 | d1type,d1dir,d1x,d1y=dock1[0:4] |
---|
540 | d2type,d2dir,d2x,d2y=dock2[0:4] |
---|
541 | if (d2type!='num') or (dock2n!=0): |
---|
542 | if block1.connections[dock1n] != None: |
---|
543 | return (100,100) |
---|
544 | if block2.connections[dock2n] != None: |
---|
545 | return (100,100) |
---|
546 | if block1==block2: return (100,100) |
---|
547 | if d1type!=d2type: |
---|
548 | # some blocks can take strings or nums |
---|
549 | if block1.proto.name in ('write', 'plus2', 'equal', 'less', 'greater', \ |
---|
550 | 'template1', 'template2', 'template3', \ |
---|
551 | 'template4', 'template6', 'template7', 'nop', \ |
---|
552 | 'print', 'stack'): |
---|
553 | if block1.proto.name == 'write' and d1type == 'string': |
---|
554 | if d2type == 'num' or d2type == 'string': |
---|
555 | pass |
---|
556 | else: |
---|
557 | if d2type == 'num' or d2type == 'string': |
---|
558 | pass |
---|
559 | # some blocks can take strings, nums, or Journal |
---|
560 | elif block1.proto.name in ('show', 'push', 'storein', 'storeinbox1', \ |
---|
561 | 'storeinbox2'): |
---|
562 | if d2type == 'num' or d2type == 'string' or d2type == 'journal': |
---|
563 | pass |
---|
564 | # some blocks can take media, audio, movies, of descriptions |
---|
565 | elif block1.proto.name in ('containter'): |
---|
566 | if d1type == 'audiooff' or d1type == 'journal': |
---|
567 | pass |
---|
568 | else: |
---|
569 | return (100,100) |
---|
570 | if d1dir==d2dir: |
---|
571 | return (100,100) |
---|
572 | return (block1.x+d1x)-(block2.x+d2x),(block1.y+d1y)-(block2.y+d2y) |
---|
573 | |
---|
574 | def magnitude(pos): |
---|
575 | x,y = pos |
---|
576 | return x*x+y*y |
---|
577 | |
---|
578 | # |
---|
579 | # Repaint |
---|
580 | # |
---|
581 | |
---|
582 | def expose_cb(win, event, tw): |
---|
583 | redrawsprites(tw) |
---|
584 | return True |
---|
585 | |
---|
586 | # |
---|
587 | # Keyboard |
---|
588 | # |
---|
589 | |
---|
590 | def keypress_cb(area, event, tw): |
---|
591 | keyname = gtk.gdk.keyval_name(event.keyval) |
---|
592 | # keyunicode = unichr(gtk.gdk.keyval_to_unicode(event.keyval)).replace("\x00","") |
---|
593 | keyunicode = gtk.gdk.keyval_to_unicode(event.keyval) |
---|
594 | # print keyname |
---|
595 | # if keyunicode > 0: |
---|
596 | # print unichr(keyunicode) |
---|
597 | |
---|
598 | if event.get_state()>k.gdk.MOD1_MASK: |
---|
599 | alt_mask = True |
---|
600 | else: |
---|
601 | alt_mask = False |
---|
602 | results = key_press(tw, alt_mask, keyname, keyunicode) |
---|
603 | if keyname is not None and hasattr(tw,"activity") and \ |
---|
604 | hasattr(tw.activity, 'chattube') and tw.activity.chattube is not None: |
---|
605 | # print "key press" |
---|
606 | if alt_mask: |
---|
607 | tw.activity._send_event("k:"+'T'+":"+keyname+":"+str(keyunicode)) |
---|
608 | else: |
---|
609 | tw.activity._send_event("k:"+'F'+":"+keyname+":"+str(keyunicode)) |
---|
610 | return keyname |
---|
611 | ''' |
---|
612 | if len(keyname)>1: |
---|
613 | # print "(" + keyunicode.encode("utf-8") + ")" |
---|
614 | return keyname |
---|
615 | else: |
---|
616 | # print "[" + keyunicode.encode("utf-8") + "]" |
---|
617 | return keyunicode.encode("utf-8") |
---|
618 | ''' |
---|
619 | def key_press(tw, alt_mask, keyname, keyunicode, verbose=False): |
---|
620 | if keyname is None: |
---|
621 | return False |
---|
622 | print "processing key press: " + keyname |
---|
623 | tw.keypress = keyname |
---|
624 | if alt_mask is True and tw.selected_block==None: |
---|
625 | if keyname=="i" and hasattr(tw, 'activity'): |
---|
626 | tw.activity.waiting_for_blocks = True |
---|
627 | tw.activity._send_event("i") # request sync for sharing |
---|
628 | elif keyname=="p": |
---|
629 | hideshow_button(tw) |
---|
630 | elif keyname=='q': |
---|
631 | exit() |
---|
632 | return True |
---|
633 | if tw.selected_block is not None and \ |
---|
634 | tw.selected_block.proto.name == 'number': |
---|
635 | print "processing keyboard input for number block" |
---|
636 | if keyname in ['minus', 'period']: |
---|
637 | keyname = {'minus': '-', 'period': '.'}[keyname] |
---|
638 | oldnum = tw.selected_block.label |
---|
639 | selblock=tw.selected_block.proto |
---|
640 | if keyname == 'BackSpace': |
---|
641 | if len(oldnum) > 1: |
---|
642 | newnum = oldnum[:len(oldnum)-1] |
---|
643 | else: |
---|
644 | newnum = '' |
---|
645 | setlabel(tw.selected_block, selblock.check(newnum,oldnum)) |
---|
646 | if len(newnum) > 0: |
---|
647 | tw.firstkey = False |
---|
648 | else: |
---|
649 | tw.firstkey = True |
---|
650 | if len(keyname)>1: |
---|
651 | return True |
---|
652 | else: # gtk.keysyms.Left ... |
---|
653 | if keyname in ['Escape', 'Return', 'KP_Page_Up', |
---|
654 | 'Up', 'Down', 'Left', 'Right', 'KP_Home', 'KP_End', |
---|
655 | 'KP_Up', 'KP_Down', 'KP_Left', 'KP_Right', |
---|
656 | 'KP_Page_Down']: |
---|
657 | # move blocks (except number and text blocks only with arrows) |
---|
658 | # or click with Return |
---|
659 | if keyname == 'KP_End': |
---|
660 | run_button(tw, 0) |
---|
661 | elif tw.spr is not None: |
---|
662 | if tw.spr.type == 'turtle': # jog turtle with arrow keys |
---|
663 | if keyname == 'KP_Up' or keyname == 'Up': |
---|
664 | jog_turtle(tw,0,10) |
---|
665 | elif keyname == 'KP_Down' or keyname == 'Down': |
---|
666 | jog_turtle(tw,0,-10) |
---|
667 | elif keyname == 'KP_Left' or keyname == 'Left': |
---|
668 | jog_turtle(tw,-10,0) |
---|
669 | elif keyname == 'KP_Right' or keyname == 'Right': |
---|
670 | jog_turtle(tw,10,0) |
---|
671 | elif keyname == 'KP_Home': |
---|
672 | jog_turtle(tw,-1,-1) |
---|
673 | elif tw.spr.type == 'block': |
---|
674 | if keyname == 'Return' or keyname == 'KP_Page_Up': |
---|
675 | click_block(tw) |
---|
676 | elif keyname == 'KP_Up' or keyname == 'Up': |
---|
677 | jog_block(tw,0,10) |
---|
678 | elif keyname == 'KP_Down' or keyname == 'Down': |
---|
679 | jog_block(tw,0,-10) |
---|
680 | elif keyname == 'KP_Left' or keyname == 'Left': |
---|
681 | jog_block(tw,-10,0) |
---|
682 | elif keyname == 'KP_Right' or keyname == 'Right': |
---|
683 | jog_block(tw,10,0) |
---|
684 | elif keyname == 'KP_Page_Down': |
---|
685 | if tw.draggroup == None: |
---|
686 | tw.draggroup = findgroup(tw.spr) |
---|
687 | for b in tw.draggroup: hide(b) |
---|
688 | tw.draggroup = None |
---|
689 | elif tw.spr.type == 'selbutton': |
---|
690 | if keyname == 'Return' or keyname == 'KP_Page_Up': |
---|
691 | select_category(tw,tw.spr) |
---|
692 | elif tw.spr.type == 'category': |
---|
693 | if keyname == 'Return' or keyname == 'KP_Page_Up': |
---|
694 | (x,y) = tw.window.get_pointer() |
---|
695 | block_selector_pressed(tw,x,y) |
---|
696 | for b in tw.draggroup: |
---|
697 | move(b, (b.x+200, b.y)) |
---|
698 | tw.draggroup = None |
---|
699 | return True |
---|
700 | if tw.selected_block is None: |
---|
701 | return False |
---|
702 | if keyname in ['Shift_L', 'Shift_R', 'Control_L', 'Caps_Lock', \ |
---|
703 | 'Alt_L', 'Alt_R', 'KP_Enter', 'ISO_Level3_Shift']: |
---|
704 | keyname = '' |
---|
705 | keyunicode = 0 |
---|
706 | # Hack until I sort out input and unicode + dead keys |
---|
707 | if keyname[0:5] == 'dead_': |
---|
708 | tw.dead_key = keyname |
---|
709 | keyname = '' |
---|
710 | keyunicode = 0 |
---|
711 | if keyname == 'Tab': |
---|
712 | keyunicode = 32 # substitute a space for a tab |
---|
713 | oldnum = tw.selected_block.label |
---|
714 | selblock=tw.selected_block.proto |
---|
715 | if keyname == 'BackSpace': |
---|
716 | if len(oldnum) > 1: |
---|
717 | newnum = oldnum[:len(oldnum)-1] |
---|
718 | else: |
---|
719 | newnum = '' |
---|
720 | setlabel(tw.selected_block, selblock.check(newnum,oldnum)) |
---|
721 | if len(newnum) > 0: |
---|
722 | tw.firstkey = False |
---|
723 | else: |
---|
724 | tw.firstkey = True |
---|
725 | elif keyname is not '': |
---|
726 | # Hack until I sort out input and unicode + dead keys |
---|
727 | if tw.dead_key == 'dead_grave': |
---|
728 | keyunicode = dead_grave[keyname] |
---|
729 | elif tw.dead_key == 'dead_acute': |
---|
730 | keyunicode = dead_acute[keyname] |
---|
731 | elif tw.dead_key == 'dead_circumflex': |
---|
732 | keyunicode = dead_circumflex[keyname] |
---|
733 | elif tw.dead_key == 'dead_tilde': |
---|
734 | keyunicode = dead_tilde[keyname] |
---|
735 | elif tw.dead_key == 'dead_diaeresis': |
---|
736 | keyunicode = dead_diaeresis[keyname] |
---|
737 | elif tw.dead_key == 'dead_abovering': |
---|
738 | keyunicode = dead_abovering[keyname] |
---|
739 | tw.dead_key = "" |
---|
740 | if tw.firstkey: |
---|
741 | newnum = selblock.check(unichr(keyunicode), \ |
---|
742 | tw.defdict[selblock.name]) |
---|
743 | elif keyunicode > 0: |
---|
744 | if unichr(keyunicode) is not '\x00': |
---|
745 | newnum = oldnum+unichr(keyunicode) |
---|
746 | else: |
---|
747 | newnum = oldnum |
---|
748 | else: |
---|
749 | newnum = "" |
---|
750 | setlabel(tw.selected_block, selblock.check(newnum,oldnum)) |
---|
751 | tw.firstkey = False |
---|
752 | return True |
---|
753 | |
---|
754 | def unselect(tw): |
---|
755 | if tw.selected_block.label in ['-', '.', '-.']: |
---|
756 | setlabel(tw.selected_block,'0') |
---|
757 | |
---|
758 | # put an upper and lower bound on numbers to prevent OverflowError |
---|
759 | if tw.selected_block.proto.name == 'number' and \ |
---|
760 | tw.selected_block.label is not None: |
---|
761 | try: |
---|
762 | i = float(tw.selected_block.label) |
---|
763 | if i > 1000000: |
---|
764 | setlabel(tw.selected_block,'1') |
---|
765 | showlabel(tw.lc,"#overflowerror") |
---|
766 | elif i < -1000000: |
---|
767 | setlabel(tw.selected_block,'-1') |
---|
768 | showlabel(tw.lc,"#overflowerror") |
---|
769 | except ValueError: |
---|
770 | pass |
---|
771 | |
---|
772 | hide(tw.select_mask) |
---|
773 | hide(tw.select_mask_string) |
---|
774 | tw.selected_block = None |
---|
775 | |
---|
776 | def jog_turtle(tw,dx,dy): |
---|
777 | if dx == -1 and dy == -1: |
---|
778 | tw.turtle.xcor = 0 |
---|
779 | tw.turtle.ycor = 0 |
---|
780 | else: |
---|
781 | tw.turtle.xcor += dx |
---|
782 | tw.turtle.ycor += dy |
---|
783 | move_turtle(tw.turtle) |
---|
784 | display_coordinates(tw) |
---|
785 | tw.draggroup = None |
---|
786 | |
---|
787 | def jog_block(tw,dx,dy): |
---|
788 | # drag entire stack if moving lock block |
---|
789 | if tw.spr.proto.name == 'lock': |
---|
790 | tw.draggroup = findgroup(find_top_block(tw.spr)) |
---|
791 | else: |
---|
792 | tw.draggroup = findgroup(tw.spr) |
---|
793 | # check to see if any block ends up with a negative x |
---|
794 | for b in tw.draggroup: |
---|
795 | if b.x+dx < 0: |
---|
796 | dx += -(b.x+dx) |
---|
797 | # move the stack |
---|
798 | for b in tw.draggroup: |
---|
799 | move(b,(b.x+dx, b.y-dy)) |
---|
800 | snap_to_dock(tw) |
---|
801 | tw.draggroup = None |
---|
802 | |
---|
803 | def click_block(tw): |
---|
804 | if tw.spr.proto.name=='number': |
---|
805 | tw.selected_block = tw.spr |
---|
806 | move(tw.select_mask, (tw.spr.x-5,tw.spr.y-5)) |
---|
807 | setlayer(tw.select_mask, 660) |
---|
808 | tw.firstkey = True |
---|
809 | elif tw.defdict.has_key(tw.spr.proto.name): |
---|
810 | tw.selected_block = tw.spr |
---|
811 | if tw.spr.proto.name=='string': |
---|
812 | move(tw.select_mask_string, (tw.spr.x-5,tw.spr.y-5)) |
---|
813 | setlayer(tw.select_mask_string, 660) |
---|
814 | tw.firstkey = True |
---|
815 | elif tw.spr.proto.name in importblocks: |
---|
816 | import_from_journal(tw, tw.spr) |
---|
817 | elif tw.spr.proto.name=='nop' and tw.myblock==None: |
---|
818 | tw.activity.import_py() |
---|
819 | else: run_stack(tw, tw.spr) |
---|
820 | |
---|
821 | # |
---|
822 | # Block utilities |
---|
823 | # |
---|
824 | |
---|
825 | def disconnect(b): |
---|
826 | if b.connections[0]==None: |
---|
827 | return |
---|
828 | b2=b.connections[0] |
---|
829 | b2.connections[b2.connections.index(b)] = None |
---|
830 | b.connections[0] = None |
---|
831 | |
---|
832 | def run_stack(tw,spr): |
---|
833 | tw.lc.ag = None |
---|
834 | top = find_top_block(spr) |
---|
835 | run_blocks(tw.lc, top, blocks(tw), True) |
---|
836 | gobject.idle_add(doevalstep, tw.lc) |
---|
837 | |
---|
838 | def findgroup(b): |
---|
839 | group=[b] |
---|
840 | for b2 in b.connections[1:]: |
---|
841 | if b2!=None: group.extend(findgroup(b2)) |
---|
842 | return group |
---|
843 | |
---|
844 | def find_top_block(spr): |
---|
845 | b = spr |
---|
846 | while b.connections[0]!=None: |
---|
847 | b=b.connections[0] |
---|
848 | return b |
---|
849 | |
---|
850 | def runtool(tw, spr, cmd, *args): |
---|
851 | cmd(*(args)) |
---|
852 | |
---|
853 | def eraser_button(tw): |
---|
854 | # hide status block |
---|
855 | setlayer(tw.status_spr,400) |
---|
856 | clear(tw.lc) |
---|
857 | display_coordinates(tw) |
---|
858 | |
---|
859 | def stop_button(tw): |
---|
860 | stop_logo(tw) |
---|
861 | |
---|
862 | def run_button(tw, time): |
---|
863 | print "you better run, turtle, run!!" |
---|
864 | # look for the start block |
---|
865 | for b in blocks(tw): |
---|
866 | if find_start_stack(tw, b): |
---|
867 | tw.step_time = time |
---|
868 | if hasattr(tw,'activity'): |
---|
869 | tw.activity.recenter() |
---|
870 | run_stack(tw, b) |
---|
871 | return |
---|
872 | # no start block, so run a stack that isn't a hat |
---|
873 | for b in blocks(tw): |
---|
874 | if find_block_to_run(tw, b): |
---|
875 | print "running " + b.proto.name |
---|
876 | tw.step_time = time |
---|
877 | run_stack(tw, b) |
---|
878 | return |
---|
879 | |
---|
880 | def hideshow_button(tw): |
---|
881 | if tw.hide is False: |
---|
882 | for b in blocks(tw): setlayer(b,100) |
---|
883 | hide_palette(tw) |
---|
884 | hide(tw.select_mask) |
---|
885 | hide(tw.select_mask_string) |
---|
886 | tw.hide = True |
---|
887 | else: |
---|
888 | for b in blocks(tw): setlayer(b,650) |
---|
889 | show_palette(tw) |
---|
890 | tw.hide = False |
---|
891 | inval(tw.turtle.canvas) |
---|
892 | |
---|
893 | # find start stack |
---|
894 | def find_start_stack(tw, spr): |
---|
895 | top = find_top_block(spr) |
---|
896 | if spr.proto.name == 'start': |
---|
897 | return True |
---|
898 | else: |
---|
899 | return False |
---|
900 | |
---|
901 | # find a stack to run (any stack without a hat) |
---|
902 | def find_block_to_run(tw, spr): |
---|
903 | top = find_top_block(spr) |
---|
904 | if spr == top and spr.proto.name[0:3] != 'hat': |
---|
905 | return True |
---|
906 | else: |
---|
907 | return False |
---|
908 | |
---|
909 | def blocks(tw): |
---|
910 | return [spr for spr in tw.sprites if spr.type == 'block'] |
---|
911 | |
---|
912 | def xy(event): |
---|
913 | return map(int, event.get_coords()) |
---|
914 | |
---|
915 | def showPopup(block_name,tw): |
---|
916 | if blocks_dict.has_key(block_name): |
---|
917 | block_name_s = _(blocks_dict[block_name]) |
---|
918 | else: |
---|
919 | block_name_s = _(block_name) |
---|
920 | if hover_dict.has_key(block_name): |
---|
921 | label = block_name_s + ": " + hover_dict[block_name] |
---|
922 | else: |
---|
923 | label = block_name_s |
---|
924 | if hasattr(tw, "activity"): |
---|
925 | tw.activity.hover_help_label.set_text(label) |
---|
926 | tw.activity.hover_help_label.show() |
---|
927 | elif hasattr(tw, "win"): |
---|
928 | tw.win.set_title(_("Turtle Art") + " â " + label) |
---|
929 | return 0 |
---|