/[james]/pythonwimp/wimp.py
ViewVC logotype

Annotation of /pythonwimp/wimp.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 75 - (hide annotations) (download) (as text)
Sun May 15 12:10:21 2005 UTC (19 years ago) by james
File MIME type: text/x-python
File size: 43505 byte(s)
Place under MIT license.

1 james 17 """Interface with RISC OS window manager (wimp) and related functions.
2    
3 james 75 Licensed under the MIT License,
4     http://www.opensource.org/licenses/mit-license
5     Copyright 2005 James Bursa <james@semichrome.net>
6    
7 james 17 Class hierarchy:
8    
9     window -- base window class
10     iconbar_icon -- iconbar icon
11     info_window -- info box
12     save_window -- save box
13     menu -- menu class
14     content -- window contents class
15     rectangle -- example content
16     font -- font class
17    
18     Functions:
19    
20     task() -- initialise the task with the wimp
21     quit() -- tidy up and exit
22    
23     poll() -- get a wimp event
24     handle() -- handle a wimp event
25    
26     screen_size() -- return screen mode dimensions
27     mem_string() -- read or write a string to a buffer
28    
29     hourglass_on() -- switch on the hourglass
30     hourglass_off() -- switch off the hourglass
31     hourglass_percentage() -- display a percentage underneath
32    
33     warning() -- display a wimp error box
34     lookup() -- convert a message token to a message
35    
36     send_message() -- send a wimp message
37     close_menus() -- close any open menus
38     set_colour() -- set the colour for plotting
39     plot() -- plot to the screen
40    
41     Variables:
42    
43     task_handle -- wimp task handle
44     task_name -- wimp task name
45     poll_mask -- default wimp poll event mask
46    
47     Comments:
48    
49     All window coordinates behave as follows:
50     - the origin is assumed to be in the top left, so ensure that
51     your templates have this to avoid problems
52     - x coordinates are 0 at the origin and increase across the window
53     - y coordinates are 0 at the origin and increase down the window
54     - scroll offsets are also always positive
55    
56     Coordinates are usually in os units. However, the document classes
57     and fonts use millipoints. At 100%, 400 millipoints = 1 os unit.
58     Font sizes are in 1/16 points.
59    
60     """
61    
62     import os
63     import swi
64     import string
65     import sys
66     import types
67 james 74 import rufl
68 james 17
69     task_handle = 0
70     task_name = 'Application'
71     poll_mask = 1
72     null_reason_handler = None
73     user_drag_handler = None
74     message_handler = {}
75     windows = {}
76     fonts = {}
77    
78     _depth = 0
79    
80     def task(name, dir, task, messages = (), debug = 0, profile = 0, mask = None):
81     "Initialise the task with the wimp and load the messages file"
82     global task_name, directory, b, wimp_version, task_handle, territory, m, task_module, _trace
83 james 74
84 james 17 hourglass_on()
85     task_name = name
86     directory = dir
87     task_module = task
88     if mask:
89     poll_mask = mask
90    
91     # set up debugging stuff
92     if debug:
93     import traceback
94     sys.stderr = sys.stdout = open(directory + '.^.Output', 'w')
95     if debug == 2:
96     def _trace(frame, event, arg):
97     global _depth
98     if event == 'call':
99     print _depth * ' ', frame.f_code.co_name + '(', arg, ')'
100     _depth = _depth + 1
101     elif event == 'return':
102     print _depth * ' ', 'return', arg
103     _depth = _depth - 1
104     return _trace
105     sys.settrace(_trace)
106     elif debug == 3:
107     def _trace(frame, event, arg):
108     global _depth
109     if event == 'line':
110     print _depth * ' ', frame.f_lineno
111     elif event == 'call':
112     print _depth * ' ', frame.f_code.co_name + '(', arg, ')'
113     _depth = _depth + 1
114     elif event == 'return':
115     print _depth * ' ', 'return', arg
116     _depth = _depth - 1
117     else:
118     print _depth * ' ', event + ':', arg
119     return _trace
120     sys.settrace(_trace)
121 james 74
122 james 17 # buffer for various stuff
123     b = swi.block(64)
124 james 74
125 james 17 # initialise with wimp
126     # place the message list in the buffer
127     b[0] = 0x502 # Message_HelpRequest
128     b[1] = 0x400c9 # Message_MenusDeleted
129     index = 2
130     for message in messages:
131     b[index] = message
132     index = index + 1
133     b[index] = 0
134     # Wimp_Initialise
135     wimp_version, task_handle = swi.swi(0x400c0, 'iisb;ii', 310, 0x4b534154, name, b)
136    
137     # load messages
138     # Territory_NumberToName, Territory_Number
139     territory = swi.swi(0x43043, 'ibi;.s', swi.swi(0x43040, ';i'), b, 256)
140    
141     try:
142     message_file = open(directory + '.Resources.' + territory + '.Messages', 'r')
143     except IOError:
144     message_file = open(directory + '.Resources.UK.Messages', 'r')
145     m = {}
146     for line in message_file.readlines():
147     if line[0] != '#' and ':' in line:
148     message = string.split(line, ':', 1)
149     m[message[0]] = message[1][:-1]
150     message_file.close()
151    
152     try:
153     if profile:
154     import profile
155     profile.run('wimp._go()', directory + '.^.Profile')
156     else:
157     _go()
158     except SystemExit:
159     hourglass_on()
160     task_module.quit()
161     except RuntimeError, value:
162     swi.swi(0x400df, 'sis', ' ' + str(value), 1, task_name)
163     except:
164     if debug > 1:
165     sys.settrace(None)
166 james 74
167 james 17 type, value, traceb = sys.exc_info()
168     swi.swi(0x400df, 'sis',
169     ' Internal error - must exit (' + str(type) + ': ' + str(value) + ')',
170     1, task_name)
171 james 74
172 james 17 if debug:
173     traceback.print_exc()
174     trace = traceback.extract_tb(traceb)
175     try:
176     swi.swi(0x42587, '')
177     swi.swi(0x42588, '0.s', trace[-1][0])
178     swi.swi(0x42588, '1.si1s', trace[-1][0], trace[-1][1],
179     str(type) + ': ' + trace[-1][2] + ': ' + trace[-1][3])
180     swi.swi(0x42589, '')
181     except:
182     pass
183 james 74
184 james 17 if sys.stderr.tell() > 0:
185     sys.stderr.close()
186     os.system('Filer_Run ' + directory + '.^.Output')
187     else:
188     sys.stderr.close()
189 james 74
190 james 17 # Wimp_CloseDown
191     swi.swi(0x400dd, 'ii', task_handle, 0x4b534154)
192     hourglass_off()
193    
194 james 74
195 james 17 def _go():
196     "Run the program"
197     task_module.init()
198     hourglass_off()
199     while 1:
200     handle(poll(poll_mask))
201 james 74
202    
203 james 17 def quit():
204     "Trigger quitting"
205     for f in fonts.values():
206     f.use = 1
207     f.lose()
208     raise SystemExit
209    
210    
211     def poll(mask = None):
212     "Poll wimp once and return data"
213     # Wimp_Poll
214     if mask is None:
215     mask = poll_mask
216     event = swi.swi(0x400c7, 'ib;i', mask, b)
217     if event == 2: # Open_Window_Request
218     return 2, (windows[b[0]], b[1], b[2], b[3], b[4], b[5], b[6], b[7])
219     elif event == 3: # Close_Window_Request
220     return 3, windows[b[0]]
221     elif event == 6: # Mouse_Click
222     x, y, z, w, i = b[0], b[1], b[2], b[3], b[4]
223     b[0] = w
224     # Wimp_GetWindowState
225     swi.swi(0x400cb, '.b', b)
226     return 6, (windows[w], i, z, x - (b[1] - b[5]), (b[4] - b[6]) - y, x, y)
227     elif event == 7: # User_Drag_Box
228     return 7, (b[0], b[1], b[2], b[3])
229     elif event == 8: # Key_Pressed
230     return 8, (windows[b[0]], b[1], b[6])
231     # User_Message, User_Message_Recorded, User_Message_Acknowledge
232     elif event == 17 or event == 18 or event == 19:
233     return event, (b[4])
234     return event, b
235    
236    
237     def handle((event, details)):
238     "Handle events as returned by poll()"
239     # try:
240     if event == 0: # Null_Reason_Code
241     if null_reason_handler:
242     null_reason_handler()
243     elif event == 1: # Redraw_Window_Request
244     windows[b[0]].do_redraw()
245     elif event == 2: # Open_Window_Request
246     details[0].open(details[1], details[2], details[3], details[4], details[5], details[6], details[7])
247     elif event == 3: # Close_Window_Request
248     details.close()
249     elif event == 4: # Pointer_Leaving_Window
250     if windows.has_key(b[0]):
251     windows[b[0]].leaving()
252     elif event == 5: # Pointer_Entering_Window
253     windows[b[0]].entering()
254     elif event == 6: # Mouse_Click
255     details[0].click(details[1], details[2], details[3], details[4], details[5], details[6])
256     elif event == 7: # User_Drag_Box
257     if user_drag_handler:
258     user_drag_handler(details[0], details[1], details[2], details[3])
259     elif event == 8: # Key_Pressed
260     details[0].key(details[1], details[2])
261     # User_Message, User_Message_Recorded, User_Message_Acknowledge
262     elif event == 17 or event == 18 or event == 19:
263     if details == 0: # Message_Quit
264     quit()
265     elif details == 0x502: # Message_HelpRequest
266     _help()
267     elif message_handler.has_key(details):
268     message_handler[details]()
269     # except KeyError:
270     # pass
271    
272    
273     def _help():
274     "Supply interactive help"
275     help = None
276     try:
277     help = windows[b[8]].help(b[9])
278     except KeyError:
279     # must be a menu
280     bb = swi.block(20)
281     # Wimp_GetMenuState
282     swi.swi(0x400f4, '0b', bb)
283     submenus = ''
284     for selection in bb:
285     if selection == -1:
286     break
287     submenus = submenus + '.' + str(selection)
288     if m.has_key('help.menu.' + current_menu.name + submenus):
289     help = m['help.menu.' + current_menu.name + submenus]
290     elif m.has_key('help.menu.' + current_menu.name):
291     help = m['help.menu.' + current_menu.name]
292     if help is None:
293     help = m['help.default']
294     b[0] = 256
295     b[3] = b[2]
296     b[4] = 0x503
297     b.padstring(help, '\0', 20, 256)
298     # Wimp_SendMessage
299     swi.swi(0x400e7, 'ibi', 17, b, b[1])
300    
301    
302     def screen_size():
303     "Return the screen width and height"
304     bb = swi.block(20)
305     bb[0] = 130
306     bb[1] = 131
307     bb[2] = -1
308     # OS_ReadVduVariables
309     swi.swi(0x31, 'bb', bb, bb)
310     # OS_ReadModeVariable
311     width = (bb[0] + 1) * 2 ** swi.swi(0x35, '-4;..i')
312     height = (bb[1] + 1) * 2 ** swi.swi(0x35, '-5;..i')
313     return width, height
314    
315    
316     def mem_string(address, write = None):
317     "Read or write a string in memory - dangerous"
318     if write is None:
319     bb = swi.register(256, address)
320     return bb.ctrlstring()
321     else:
322     bb = swi.register((len(write) + 4) / 4, address)
323     bb.padstring(write, '\0', 0, len(write) + 1)
324    
325    
326     def hourglass_on():
327     "Start the hourglass"
328     # Hourglass_On
329     swi.swi(0x406c0, '')
330    
331     def hourglass_off():
332     "Stop the hourglass"
333     # Hourglass_Off
334     swi.swi(0x406c1, '')
335    
336     def hourglass_percentage(value):
337     "Show a percentage under the hourglass"
338     # Hourglass_Percentage
339     swi.swi(0x406c4, 'i', value)
340    
341    
342     def warning(message):
343     "Give a warning error box"
344     # Wimp_ReportError
345     swi.swi(0x400df, 's1s', ' ' + message, task_name)
346    
347    
348     def lookup(message):
349     "Lookup message if possible"
350     try:
351     return m[message]
352     except KeyError:
353     return message
354    
355    
356     def send_message(event, action, receiver, data, icon = -1, ref = 0):
357     "Send a wimp message"
358     b[3] = ref
359     b[4] = action
360     offset = 5
361     for thing in data:
362     if isinstance(thing, types.IntType):
363     b[offset] = thing
364     offset = offset + 1
365     else:
366     length = int((len(thing) + 4) / 4)
367     b.padstring(thing, '\0', offset * 4, (offset + length) * 4)
368     offset = offset + length
369     break
370     b[0] = offset * 4
371     # Wimp_SendMessage
372     swi.swi(0x400e7, 'ibii', event, b, receiver, icon)
373    
374    
375     def close_menus():
376     # Wimp_CreateMenu
377     swi.swi(0x400d4, '.-')
378    
379    
380     def set_colour(colour):
381     swi.swi('ColourTrans_SetGCOL', 'i..00', colour << 8)
382    
383    
384     def plot(type, x, y):
385     # OS_Plot
386     swi.swi(0x45, 'iii', type, x, y)
387    
388    
389     def pointer():
390     bb = swi.block(20)
391     # Wimp_GetPointerInfo
392     swi.swi(0x400cf, '.b', bb)
393     return bb[0], bb[1], bb[2], bb[3], bb[4]
394    
395    
396     class window:
397     "Wimp window class"
398    
399     def __init__(self, id):
400     "Load window from template file"
401     # Wimp_OpenTemplate
402     try:
403     swi.swi(0x400d9, '.s', directory + '.Resources.' + territory + '.Templates')
404     except swi.error:
405     swi.swi(0x400d9, '.s', directory + '.Resources.UK.Templates')
406     # Wimp_LoadTemplate
407     buffer_size, workspace_size, found = swi.swi(0x400db, '.0..-s0;.ii...i', id)
408     if not found:
409     raise RuntimeError, m['error.template'] % id
410     buffer = swi.block(buffer_size / 4 + 1)
411     workspace = swi.block(workspace_size / 4 + 1)
412     swi.swi(0x400db, '.bbe-s0', buffer, workspace, workspace, id)
413     # Wimp_CreateWindow
414     self.handle = swi.swi(0x400c1, '.b;i', buffer)
415     self.workspace = workspace
416     self.name = id
417     self.title_pointer = buffer[18]
418     self.icons = buffer[21]
419     self.is_open = 0
420     # Wimp_CloseTemplate
421     swi.swi(0x400da, '')
422     windows[self.handle] = self
423     self.contents = []
424    
425     def remove(self):
426     del windows[self.handle]
427     bb = swi.block(20)
428     bb[0] = self.handle
429     # Wimp_DeleteWindow
430     swi.swi(0x400c3, '.b', bb)
431    
432     def do_redraw(self):
433     # Wimp_RedrawWindow
434     more = swi.swi(0x400c8, '.b;i', b)
435     self.ox = b[1] - b[5]
436     self.oy = b[4] - b[6]
437     self.redraw_start()
438     while more:
439     self.gx0 = b[7] - self.ox
440     self.gy0 = self.oy - b[10]
441     self.gx1 = b[9] - self.ox
442     self.gy1 = self.oy - b[8]
443     self.redraw()
444     # Wimp_GetRectangle
445     more = swi.swi(0x400ca, '.b;i', b)
446     self.redraw_stop()
447    
448     def redraw_start(self):
449     "Get ready to redraw the window"
450    
451     def redraw(self):
452     "Redraw a rectangle of the window"
453     if self.contents:
454     for content in self.contents:
455     if not ((content.pos[2] < self.gx0) or (content.pos[0] > self.gx1) or (content.pos[3] < self.gy0) or (content.pos[1] > self.gy1)):
456     content.redraw(self.ox, self.oy)
457    
458     def redraw_stop(self):
459     "Tidy up after redrawing the window"
460    
461     def open(self, minx = None, miny = None, maxx = None, maxy = None,
462     scrollx = None, scrolly = None, behind = None):
463     "Open the window"
464     bb = swi.block(20)
465     bb[0] = self.handle
466     if minx is None:
467     # Wimp_GetWindowState
468     swi.swi(0x400cb, '.b', bb)
469     else:
470     bb[1] = minx
471     bb[2] = miny
472     bb[3] = maxx
473     bb[4] = maxy
474     bb[5] = scrollx
475     bb[6] = scrolly
476     bb[7] = behind
477     # Wimp_OpenWindow
478     swi.swi(0x400c5, '.b', bb)
479     self.is_open = 1
480    
481     def open_centred(self, behind = None):
482     "Open the window centred in the screen"
483     bb = swi.block(20)
484     bb[0] = self.handle
485     # Wimp_GetWindowState
486     swi.swi(0x400cb, '.b', bb)
487     screen_width, screen_height = screen_size()
488     width = bb[3] - bb[1]
489     height = bb[4] - bb[2]
490     if behind is None:
491     behind = bb[7]
492     self.open((screen_width - width) / 2, (screen_height - height) / 2,
493     (screen_width + width) / 2, (screen_height + height) / 2,
494     bb[5], bb[6], behind)
495    
496 james 74 def open_full_size(self, behind = None):
497     bb = swi.block(20)
498     bb[0] = self.handle
499     swi.swi('Wimp_GetWindowState', '.b', bb)
500     bb[3] = 10000
501     bb[2] = -10000
502     if behind:
503     bb[7] = behind
504     swi.swi('Wimp_OpenWindow', '.b', bb)
505     self.is_open = 1
506    
507 james 17 def open_as_menu(self):
508     "Open the window as a menu centred on the pointer"
509     bb = swi.block(20)
510     bb[0] = self.handle
511     # Wimp_GetWindowState
512     swi.swi(0x400cb, '.b', bb)
513     width = bb[3] - bb[1]
514     height = bb[4] - bb[2]
515     # Wimp_GetPointerInfo
516     swi.swi(0x400cf, '.b', bb)
517     # Wimp_CreateMenu
518     swi.swi(0x400d4, '.iii', self.handle, bb[0] - width / 2, bb[1] + height / 2)
519    
520     def close(self):
521     "Close the window"
522     bb = swi.block(20)
523     bb[0] = self.handle
524     # Wimp_CloseWindow
525     swi.swi(0x400c6, '.b', bb)
526     self.is_open = 0
527 james 74
528 james 17 def entering(self):
529     "Pointer entering window"
530 james 74
531 james 17 def leaving(self):
532     "Pointer leaving window"
533 james 74
534 james 17 def click(self, icon, buttons, x, y, sx, sy):
535     "Mouse click in window"
536     self.contents.reverse()
537     if self.contents:
538     for content in self.contents:
539     if content.pos[0] <= x <= content.pos[2] and content.pos[1] <= y <= content.pos[3]:
540     if content.click(buttons, x - content.pos[0], y - content.pos[1]):
541     self.contents.reverse()
542     return 1
543     self.contents.reverse()
544     return 0
545 james 74
546 james 17 def help(self, icon):
547     "Return interactive help message"
548     if m.has_key('help.' + self.name + '.' + str(icon)):
549     return m['help.' + self.name + '.' + str(icon)]
550     elif m.has_key('help.' + self.name):
551     return m['help.' + self.name]
552     return None
553 james 74
554 james 17 def key(self, icon, key):
555     "Handle key presses"
556     # Wimp_ProcessKey
557     swi.swi(0x400dc, 'i', key)
558 james 74
559 james 17 def icon_text(self, icon, text = None):
560     "Read or write an icon's text contents"
561     if icon >= self.icons:
562     raise RuntimeError, 'Icon does not exist in icon_text()'
563     bb = swi.block(20)
564     bb[0] = self.handle
565     bb[1] = icon
566     # Wimp_GetIconState
567     swi.swi(0x400ce, '.b', bb)
568     indirected = bb[6] & 0x100
569     if text is None:
570     if indirected:
571     return mem_string(bb[7])
572     else:
573     return mem_string(bb.start + 28)
574     else:
575     if not indirected:
576     raise RuntimeError, 'Icon not indirected in icon_text()'
577     mem_string(bb[7], text)
578     # Wimp_SetIconState
579     bb[2] = bb[3] = 0
580     swi.swi(0x400cd, '.b', bb)
581     # Wimp_GetCaretPosition
582     swi.swi(0x400d3, '.b', bb)
583     if bb[0] == self.handle and bb[1] == icon:
584     self.put_caret(icon)
585 james 74
586 james 17 def icon_crement(self, icon, min, max, step = 1):
587     "Increment or decrement a numerical icon"
588     number = self.icon_text(icon)
589     if number == '':
590     number = (step < 0) * min + (step > 0) * max
591     else:
592     number = eval(number)
593     if number == (step < 0) * min + (step > 0) * max:
594     return
595     if number > max:
596     number = max
597     elif number < min:
598     number = min
599     else:
600     number = number + step
601     self.icon_text(0, str(number))
602 james 74
603 james 17 def icon_set(self, icon, state = None):
604     "Return the state or set the state of an icon"
605     if icon >= self.icons:
606     raise RuntimeError, 'Icon does not exist in icon_set()'
607     bb = swi.block(20)
608     bb[0] = self.handle
609     bb[1] = icon
610     # Wimp_GetIconState
611     swi.swi(0x400ce, '.b', bb)
612     if state is None:
613     return (bb[6] & 0x200000) == 0x200000
614     else:
615     bb[6] = bb[6] & ~0x200000
616     if state:
617     bb[6] = bb[6] | 0x200000
618     # Wimp_SetIconState
619     bb[2] = bb[3] = bb[6]
620     swi.swi(0x400cd, '.b', bb)
621 james 74
622 james 17 def put_caret(self, icon = -1, x = 0, y = 0, height = 0x2000000):
623     "Place the caret in the window"
624     if icon == -1:
625     # Wimp_SetCaretPosition
626     swi.swi(0x400d2, 'iiiii0', self.handle, icon, x, y, height)
627     else:
628     # Wimp_SetCaretPosition
629     swi.swi(0x400d2, 'ii00-i', self.handle, icon, len(self.icon_text(icon)))
630    
631     def icon_grey(self, icon, grey = 1):
632     "(Un)grey out an icon"
633     if icon >= self.icons:
634     raise RuntimeError, 'Icon does not exist in icon_grey()'
635     bb = swi.block(20)
636     bb[0] = self.handle
637     bb[1] = icon
638     bb[2] = 0x400000 * grey
639     bb[3] = 0x400000
640     swi.swi(0x400cd, '.b', bb)
641    
642     def icon_ungrey(self, icon):
643     "Ungrey an icon"
644     self.icon_grey(icon, 0)
645 james 74
646 james 17 def add_content(self, content):
647     "Add a content class to the window"
648     self.contents.append(content)
649 james 74
650 james 17 def remove_content(self, content):
651     "Remove a content class from the window"
652     self.contents.remove(content)
653 james 74
654 james 17 def refresh(self, x0, y0, x1, y1):
655     "Force a redraw of an area of the window"
656     swi.swi('Wimp_ForceRedraw', 'iiiii', self.handle, x0, -y1, x1, -y0)
657 james 74
658 james 17 def update(self, x0, y0, x1, y1):
659     "Update an area of the window"
660     bb = swi.block(20)
661     bb[0] = self.handle
662     bb[1] = x0
663     bb[2] = -y1
664     bb[3] = x1
665     bb[4] = -y0
666     # Wimp_UpdateWindow
667     more = swi.swi(0x400c9, '.b;i', bb)
668     self.ox = bb[1] - bb[5]
669     self.oy = bb[4] - bb[6]
670     self.redraw_start()
671     while more:
672     self.gx0 = bb[7] - self.ox
673     self.gy0 = self.oy - bb[10]
674     self.gx1 = bb[9] - self.ox
675     self.gy1 = self.oy - bb[8]
676     self.redraw()
677     # Wimp_GetRectangle
678     more = swi.swi(0x400ca, '.b;i', bb)
679     self.redraw_stop()
680 james 74
681 james 17 def title(self, title = None):
682     "Change or return the window title"
683     if title is None:
684     return mem_string(self.title_pointer, None)
685     else:
686     mem_string(self.title_pointer, title)
687     if self.is_open:
688     bb = swi.block(20)
689     # Wimp_GetCaretPosition
690     swi.swi(0x400d3, '.b', bb)
691     if bb[0] == self.handle:
692     # Wimp_SetCaretPosition
693     swi.swi(0x400d2, '-000--')
694     swi.swi(0x400d2, 'iiiiii', bb[0], bb[1], bb[2], bb[3], bb[4], bb[5])
695     else:
696     # Wimp_SetCaretPosition
697     swi.swi(0x400d2, 'i-00i-', self.handle, 1<<25)
698     swi.swi(0x400d2, 'iiiiii', bb[0], bb[1], bb[2], bb[3], bb[4], bb[5])
699    
700     def set_extent(self, x0, y0, x1, y1):
701     "Set the work area extent in os units"
702     bb = swi.block(20)
703     bb[0] = x0
704     bb[1] = -y1
705     bb[2] = x1
706     bb[3] = -y0
707     # Wimp_SetExtent
708     swi.swi(0x400d7, 'ib', self.handle, bb)
709 james 74
710 james 17 def origin(self):
711     "Return the window origin position on the screen"
712     bb = swi.block(20)
713     bb[0] = self.handle
714     swi.swi('Wimp_GetWindowState', '.b', bb)
715     self.ox = b[1] - b[5]
716     self.oy = b[4] - b[6]
717     return self.ox, self.oy
718 james 74
719 james 17 def icon_box(self, icon):
720     "Return the bbox of an icon relative to the window"
721     bb = swi.block(20)
722     bb[0] = self.handle
723     bb[1] = icon
724     swi.swi('Wimp_GetIconState', '.b', bb)
725     return bb[2], -bb[5], bb[4], -bb[3]
726 james 74
727 james 17 def icon_drag(self, icon):
728     "Start to drag the specified icon"
729     self.origin()
730     x0, y0, x1, y1 = self.icon_box(icon)
731     bb = swi.block(20)
732     bb[0] = self.ox + x0
733     bb[1] = self.oy - y1
734     bb[2] = self.ox + x1
735     bb[3] = self.oy - y0
736     swi.swi('DragASprite_Start', 'i1sb', 0xc5, self.icon_text(icon), bb)
737    
738    
739     class iconbar_icon(window):
740 james 74
741 james 17 def __init__(self, sprite, handler, indirected = 0):
742     "Create an iconbar icon with the specified sprite"
743     bb = swi.block(20)
744     bb[0] = -1
745     bb[1] = bb[2] = 0
746     bb[3] = bb[4] = 68
747     if indirected:
748     bb[5] = 0x311a
749     self.buffer = swi.block(5)
750     self.buffer.padstring(sprite, '\0')
751     bb[6] = self.buffer.start
752     bb[7] = 1
753     bb[8] = len(sprite)
754     else:
755     bb[5] = 0x301a
756     bb.padstring(sprite, '\0', 24, 36)
757     # Wimp_CreateIcon
758     self.icon = swi.swi(0x400c2, '0b;i', bb)
759     self.icons = self.icon + 1
760     self.handle = -2
761     self.name = 'iconbar'
762     self.handler = handler
763     windows[-2] = self
764 james 74
765 james 17 def click(self, icon, buttons, x, y, sx, sy):
766     "Handle a click on the iconbar"
767     self.handler(buttons, sx)
768 james 74
769 james 17 def change(self, sprite):
770     self.icon_text(self.icon, sprite)
771    
772    
773     class info_window(window):
774     "Info box window including email/web buttons"
775 james 74
776 james 17 def __init__(self):
777     "Load the window template"
778     window.__init__(self, 'info')
779 james 74
780 james 17 def click(self, icon, buttons, x, y, sx, sy):
781     "Handle mouse clicks"
782     try:
783     if icon == 8 or icon == 9:
784     if os.path.exists('System:Modules.Network.URI'):
785     # Wimp_StartTask
786     swi.swi(0x400de, 's', 'RMEnsure AcornURI 0.00 RMRun System:Modules.Network.URI')
787     if icon == 8:
788     # URI_Dispatch
789     swi.swi(0x4e381, '0s0', m['email'])
790     elif icon == 9:
791     # URI_Dispatch
792     swi.swi(0x4e381, '0s0', m['www'])
793     except swi.error:
794     swi.swi(0x107, '')
795    
796    
797     class save_window(window):
798     "Standard RISC OS save window"
799 james 74
800 james 17 def __init__(self, handler, type):
801     "Load the window template"
802     window.__init__(self, 'save')
803     self.handler = handler
804     self.type = type
805 james 74
806 james 17 def click(self, icon, buttons = 0, x = 0, y = 0, sx = 0, sy = 0):
807     "Handle mouse clicks"
808     if icon == 0 and buttons > 15:
809     if self.icon_text(1) == '':
810     warning(m['error.save.empty'])
811     else:
812     self.drag_save()
813     elif icon == 2:
814     close_menus()
815     self.close()
816     elif icon == 3:
817     path = self.icon_text(1)
818     if os.path.isabs(path):
819     self.handler(path, 0)
820     close_menus()
821     self.close()
822     else:
823     warning(m['error.save.tosave'])
824    
825     def key(self, icon, key):
826     "Handle key presses"
827     if key == 0xd:
828     self.click(3)
829     elif key == 0x1b:
830     self.click(2)
831     else:
832     # Wimp_ProcessKey
833     swi.swi(0x400dc, 'i', key)
834    
835     def path(self, path):
836     "Set the save path"
837     self.icon_text(1, path)
838    
839     def drag_save(self):
840     "Save the object by dragging the icon"
841     self.icon_drag(0)
842     while 1:
843     event, details = poll()
844     if event == 7:
845     swi.swi('DragASprite_Stop', '')
846     mouse = pointer()
847     path = self.icon_text(1)
848     # Message_DataSave
849     send_message(18, 1, mouse[3],
850     (mouse[3], mouse[4], mouse[0], mouse[1], 0, self.type, os.path.basename(path)),
851     mouse[4])
852     break
853     else:
854     handle((event, details))
855     event, details = poll()
856     if event == 19 and details == 1:
857     # Message_DataSave bounced
858     pass
859     elif event in (17, 18) and details == 2:
860     path = b.nullstring(44, b[0])
861     # Message_DataSaveAck
862     if self.handler(path, b[9] == -1):
863     # Message_DataLoad
864     send_message(17, 3, b[1], (b[5], b[6], b[7], b[8], b[9], b[10], path), ref = b[2])
865     close_menus()
866     self.close()
867     else:
868     handle((event, details))
869    
870    
871     class menu:
872     "Wimp menu class"
873 james 74
874 james 17 def __init__(self, name, items):
875     "Load menu"
876     self.blocks = []
877     self.menu_block = self._load(items)
878     self.name = name
879 james 74
880 james 17 def menu(self, x, y):
881     "Display menu and return the selection"
882     global current_menu
883     current_menu = self
884     swi.swi(0x400d4, '.bii', self.menu_block, x, y)
885     while 1:
886     event, details = poll(1)
887     # Menu_Selection
888     if event == 9:
889     bb = swi.block(20)
890     # Wimp_GetPointerInfo
891     swi.swi(0x400cf, '.b', bb)
892     return details, (bb[2] == 1)
893     # User_Message, User_Message_Recorded: Message_MenusDeleted
894     elif (event == 17 or event == 18) and details == 0x400c9:
895     return None, 0
896     else:
897     handle((event, details))
898    
899     def popup(self, window, icon):
900     "Popup the menu next to the icon"
901     bb = swi.block(20)
902     bb[0] = window.handle
903     bb[1] = icon
904     # Wimp_GetIconState
905     swi.swi(0x400ce, '.b', bb)
906     x = bb[4]
907     y = bb[5]
908     # Wimp_GetWindowState
909     swi.swi(0x400cb, '.b', bb)
910     return self.menu(bb[1] - bb[5] + x, bb[4] - bb[6] + y)
911    
912     def _load(self, items):
913     "Create a wimp menu structure (recursively)"
914     menu_block = swi.block(7 + 6 * (len(items) - 1))
915     self.blocks.append(menu_block)
916 james 74
917 james 17 title_block = swi.block(len(lookup(items[0])) / 4 + 1)
918     self.blocks.append(title_block)
919     title_block.padstring(lookup(items[0]), '\0')
920     menu_block[0] = title_block.start
921 james 74
922 james 17 menu_block[1] = menu_block[2] = -1
923     menu_block[3] = 0x00070207
924     menu_block[4] = 300
925     menu_block[5] = 44
926     menu_block[6] = 0
927 james 74
928 james 17 offset = 7
929 james 74
930 james 17 for item in items[1:]:
931     if isinstance(item, types.StringType):
932     text = item
933     submenu = -1
934     else:
935     text = item[0]
936     if isinstance(item[1], window):
937     submenu = item[1].handle
938     else:
939     submenu = self._load(item[1]).start
940 james 74 grey = False
941     if text[0] == '-':
942     grey = True
943     text = text[1:]
944 james 17
945     item_block = swi.block(len(lookup(text)) / 4 + 1)
946     self.blocks.append(item_block)
947     item_block.padstring(lookup(text), '\0')
948     menu_block[offset] = (0x100 * (offset == 7))
949     menu_block[offset + 1] = submenu
950     menu_block[offset + 2] = 0x07000111
951 james 74 if grey:
952     menu_block[offset + 2] |= 1 << 22
953 james 17 menu_block[offset + 3] = item_block.start
954     menu_block[offset + 4] = menu_block[offset + 5] = -1
955 james 74
956 james 17 offset = offset + 6
957 james 74
958 james 17 menu_block[offset - 6] = menu_block[offset - 6] | 0x80
959 james 74
960 james 17 return menu_block
961    
962    
963     class content:
964     "Base window content class"
965 james 74
966 james 17 def __init__(self, w, x0, y0, x1, y1):
967     "Initialise a window content"
968     self.window = w
969     self.pos = [x0, y0, x1, y1]
970 james 74
971 james 17 def redraw(self, ox, oy):
972     "Redraw content"
973     pass
974 james 74
975 james 17 def click(self, buttons, x, y):
976     "Handle clicks in the content"
977     return 0
978 james 74
979 james 17 def refresh(self):
980     "Force redraw of the content"
981 james 74 self.window.refresh(self.pos[0] - 4, self.pos[1] - 4,
982     self.pos[2] + 4, self.pos[3] + 4)
983    
984 james 17 def update(self):
985     "Update the content"
986 james 74 self.window.update(self.pos[0] - 4, self.pos[1] - 4,
987     self.pos[2] + 4, self.pos[3] + 4)
988 james 17
989    
990     class rectangle(content):
991     "Example window content"
992    
993 james 74 def __init__(self, w, x0, y0, x1, y1, fill = None, border = None,
994     text = None, font = None, text_colour = 0x000000):
995 james 17 content.__init__(self, w, x0, y0, x1, y1)
996     self.fill = fill
997     self.border = border
998 james 74 self.text = text
999     self.font = font
1000     self.text_colour = text_colour
1001    
1002 james 17 def redraw(self, ox, oy):
1003     if not self.fill is None:
1004     # ColourTrans_SetGCOL
1005     swi.swi(0x40743, 'i..i0', self.fill << 8, 1 << 8)
1006     # OS_Plot
1007     swi.swi(0x45, '4ii', ox + self.pos[0], oy - self.pos[3])
1008     swi.swi(0x45, 'iii', 96 + 5, ox + self.pos[2], oy - self.pos[1])
1009     if not self.border is None:
1010     # ColourTrans_SetGCOL
1011     swi.swi(0x40743, 'i..i0', self.border << 8, 1 << 8)
1012     # OS_Plot
1013     swi.swi(0x45, '4ii', ox + self.pos[0], oy - self.pos[3])
1014     swi.swi(0x45, '5ii', ox + self.pos[2], oy - self.pos[3])
1015     swi.swi(0x45, '5ii', ox + self.pos[2], oy - self.pos[1])
1016     swi.swi(0x45, '5ii', ox + self.pos[0], oy - self.pos[1])
1017     swi.swi(0x45, '5ii', ox + self.pos[0], oy - self.pos[3])
1018 james 74 if not self.text is None and not self.font is None:
1019     self.font.colour(0xdddddd, self.text_colour)
1020     self.font.paint(self.text, 400 * (ox + self.pos[0]),
1021     400 * (oy - (self.pos[1] + self.pos[3] * 3) / 4))
1022 james 17
1023     def click(self, buttons, x, y):
1024 james 74 #self.window.remove_content(self)
1025     #self.refresh()
1026     return 0
1027 james 17
1028    
1029 james 74 class paragraph(content):
1030     "A paragraph of text"
1031    
1032     def __init__(self, w, x0, y0, x1, font_family, font_style, font_size,
1033     text, text_colour = 0x000000, fill = None, padding = 10):
1034     self.font_family = font_family
1035     self.font_style = font_style
1036     self.font_size = font_size
1037     self.line_height = int(font_size * 0.2)
1038     self.text = []
1039     self.text_colour = text_colour
1040     self.fill = fill
1041     self.padding = padding
1042     text = text.encode('utf8')
1043     width = x1 - x0 - padding - padding
1044     while text:
1045     (c, x) = rufl.split(font_family, font_style, font_size, text,
1046     width)
1047     s = text[:c]
1048     space = s.rfind(' ')
1049     if c != len(text) and space != -1:
1050     c = space
1051     s = text[:c + 1]
1052     text = text[c + 1:]
1053     self.text.append(s)
1054     y1 = y0 + len(self.text) * self.line_height + padding + padding
1055     content.__init__(self, w, x0, y0, x1, y1)
1056     self.y1 = y1
1057    
1058     def redraw(self, ox, oy):
1059     if not self.fill is None:
1060     swi.swi('ColourTrans_SetGCOL', 'i..i0', self.fill << 8, 1 << 8)
1061     swi.swi('OS_Plot', '4ii', ox + self.pos[0], oy - self.pos[3])
1062     swi.swi('OS_Plot', 'iii', 96 + 5, ox + self.pos[2],
1063     oy - self.pos[1])
1064     swi.swi('ColourTrans_SetFontColours', 'iiii', 0,
1065     0xffffff << 8, self.text_colour << 8, 14)
1066     y = oy - self.pos[1] - self.padding - self.line_height * 0.75
1067     for line in self.text:
1068     rufl.paint(self.font_family, self.font_style, self.font_size,
1069     line, ox + self.pos[0] + self.padding, y, rufl.blend)
1070     y -= self.line_height
1071    
1072    
1073     def font(name, size_x, size_y, no_encoding = False):
1074 james 17 f = (name, size_x, size_y)
1075     if fonts.has_key(f):
1076     font = fonts[f]
1077     font.use = font.use + 1
1078     return font
1079     else:
1080 james 74 fonts[f] = _font(name, size_x, size_y, no_encoding)
1081 james 17 return fonts[f]
1082    
1083     class _font:
1084    
1085 james 74 def __init__(self, name, size_x, size_y, no_encoding):
1086 james 17 self.name = name
1087     self.size_x = size_x
1088     self.size_y = size_y
1089     self.use = 1
1090 james 74 if no_encoding:
1091     self.handle = swi.swi('XFont_FindFont', '.sii00;i',
1092     self.name, self.size_x, self.size_y)
1093     self.utf8 = False
1094     else:
1095     try:
1096     self.handle = swi.swi('XFont_FindFont', '.sii00;i',
1097     self.name + '\EUTF8', self.size_x, self.size_y)
1098     self.utf8 = True
1099     except swi.error:
1100     self.handle = swi.swi('XFont_FindFont', '.sii00;i',
1101     self.name + '\ELatin1', self.size_x, self.size_y)
1102     self.utf8 = False
1103    
1104 james 17 def lose(self):
1105     self.use = self.use - 1
1106     if self.use == 0:
1107     # Font_LoseFont
1108     swi.swi(0x40082, 'i', self.handle)
1109    
1110     def paint(self, string, x, y, spacex = 0, spacey = 0, offx = 0, offy = 0,
1111     x0 = 0, y0 = 0, x1 = 0, y1 = 0):
1112 james 74 if self.utf8:
1113     s = string.encode('utf8')
1114     else:
1115     s = string.encode('latin1', 'replace')
1116 james 17 if x0:
1117     bb = swi.block(20)
1118     bb[0] = spacex
1119     bb[1] = spacey
1120     bb[2] = offx
1121     bb[3] = offy
1122     bb[4] = int(x0)
1123     bb[5] = int(y0)
1124     bb[6] = int(x1)
1125     bb[7] = int(y1)
1126 james 74 swi.swi('Font_Paint', 'isiiib', self.handle, s, 0x322, x, y, bb)
1127     else:
1128     swi.swi('Font_Paint', 'isiii', self.handle, s, 0x300, x, y)
1129 james 17
1130     def charbbox(self, char):
1131     return swi.swi('Font_CharBBox', 'ii0;.iiii', self.handle, ord(char))
1132 james 74
1133 james 17 def colour(self, back, fore):
1134     # ColourTrans_SetFontColours
1135     swi.swi(0x4074f, 'iiii', self.handle, back << 8, fore << 8, 14)
1136    
1137     def stringbox(self, string):
1138     if string == '':
1139     return (0, 0, 0, 0)
1140     if string[-1] == ' ':
1141     string = string + ' '
1142 james 74 if self.utf8:
1143     s = string.encode('utf8')
1144     else:
1145     s = string.encode('latin1', 'replace')
1146 james 17 bb = swi.block(20)
1147     bb[0] = bb[1] = bb[2] = bb[3] = 0
1148     bb[4] = -1
1149     # Font_ScanString
1150 james 74 swi.swi(0x400a1, 'isiiib', self.handle, s, 0x40320, 0x7fffffff,
1151     0x7fffffff, bb)
1152 james 17 return bb[5], bb[6], bb[7], bb[8]
1153    
1154     def caretpos(self, string, x, y):
1155 james 74 if self.utf8:
1156     s = string.encode('utf8')
1157     else:
1158     s = string.encode('latin1', 'replace')
1159     bb = swi.block(len(s) / 4 + 10)
1160     bb.padstring(s, chr(0))
1161 james 17 # Font_ScanString
1162 james 74 pos, cx, cy = swi.swi(0x400a1, 'ibiii;.i.ii', self.handle, bb,
1163     0x20300, x, y)
1164 james 17 return pos - bb.start, cx, cy
1165 james 74
1166 james 17 def caretxy(self, string, pos):
1167 james 74 if self.utf8:
1168     s = string.encode('utf8')
1169     else:
1170     s = string.encode('latin1', 'replace')
1171 james 17 # Font_ScanString
1172 james 74 return swi.swi(0x400a1, 'isiii..i;...ii', self.handle, s,
1173     0x20380, 0x7fffffff, 0x7fffffff, pos)
1174    
1175 james 17 def fontbox(self):
1176     return swi.swi('Font_ReadInfo', 'i;.iiii', self.handle)
1177 james 74
1178 james 17 def split(self, string, x, y):
1179 james 74 if self.utf8:
1180     s = string.encode('utf8')
1181     else:
1182     s = string.encode('latin1', 'replace')
1183     bb = swi.block(len(s) / 4 + 10)
1184     bb.padstring(s, chr(0))
1185 james 17 # Font_ScanString
1186     pos = swi.swi(0x400a1, 'ibiii;.i', self.handle, bb, 0x300, x, y)
1187     return pos - bb.start
1188    
1189    
1190     class document:
1191    
1192     def __init__(self, width, height, margin):
1193     self.contents = []
1194     self.views = []
1195     self.extent = [width, height]
1196     self.margin = margin
1197     self.fonts = {}
1198     self.fonts_use = {}
1199    
1200     def add_content(self, content):
1201     self.contents.append(content)
1202     for view in self.views:
1203     content.add_view(view)
1204    
1205     def remove_content(self, content):
1206     self.contents.remove(content)
1207    
1208     def add_view(self, view):
1209     self.views.append(view)
1210     for content in self.contents:
1211     content.add_view(view)
1212    
1213     def remove_view(self, view):
1214     self.views.remove(view)
1215     for content in self.contents:
1216     content.remove_view(view)
1217 james 74
1218 james 17 def rescaled(self, view):
1219     for content in self.contents:
1220     content.remove_view(view)
1221     content.add_view(view)
1222    
1223     def redraw_start(self, view, scale):
1224     pass
1225    
1226     def redraw(self, view, scale):
1227     if self.margin:
1228     self.redraw_margin(view)
1229     for content in self.contents:
1230     content.scaled_pos = map(lambda z, scale = scale: scale * z / 400, content.pos)
1231     if not ((content.scaled_pos[2] < view.gx0) or (content.scaled_pos[0] > view.gx1) or (content.scaled_pos[3] < view.gy0) or (content.scaled_pos[1] > view.gy1)):
1232     content.redraw_box = [view.gx0 * 400 / scale - content.pos[0],
1233     view.gy0 * 400 / scale - content.pos[1],
1234     view.gx1 * 400 / scale - content.pos[0],
1235     view.gy1 * 400 / scale - content.pos[1]]
1236     content.redraw(view, view.ox, view.oy, scale)
1237 james 74
1238 james 17 def redraw_stop(self, view, scale):
1239     pass
1240 james 74
1241 james 17 def redraw_margin(self, view):
1242     set_colour(0x777777)
1243     plot(4, view.ox + view.gx0, view.oy)
1244     plot(97, view.gx1 - view.gx0, self.margin)
1245     plot(4, view.ox + view.gx0, view.oy - view.extent[1])
1246     plot(97, view.gx1 - view.gx0, -self.margin)
1247     plot(4, view.ox, view.oy - view.gy1)
1248     plot(97, -self.margin, view.gy1 - view.gy0)
1249     plot(4, view.ox + view.extent[0], view.oy - view.gy1)
1250     plot(97, self.margin, view.gy1 - view.gy0)
1251    
1252     def click(self, view, buttons, x, y):
1253     for content in self.contents:
1254     if content.pos[0] <= x <= content.pos[2] and content.pos[1] <= y <= content.pos[3]:
1255     content.click(view, buttons, x - content.pos[0], y - content.pos[1])
1256 james 74
1257 james 17 def refresh(self, x0, y0, x1, y1):
1258     for view in self.views:
1259     view.update(int(view.scale * x0 / 400 - 4), int(view.scale * y0 / 400 - 4),
1260     int(view.scale * x1 / 400 + 4), int(view.scale * y1 / 400 + 4))
1261 james 74
1262 james 17 def add_font(self, name, size_x, size_y):
1263     f = (name, size_x, size_y)
1264     if self.fonts.has_key(f):
1265     self.fonts_use[f] = self.fonts_use[f] + 1
1266     else:
1267     self.fonts[f] = font(name, size_x, size_y)
1268     self.fonts_use[f] = 1
1269    
1270     def remove_font(self, name, size_x, size_y):
1271     f = (name, size_x, size_y)
1272     self.fonts_use[f] = self.fonts_use[f] - 1
1273     if self.fonts_use[f] == 0:
1274     self.fonts[f].lose()
1275     del self.fonts[f]
1276     del self.fonts_use[f]
1277    
1278    
1279     class document_view(window):
1280 james 74
1281 james 17 def __init__(self, document, id, scale):
1282     window.__init__(self, id)
1283     self.document = document
1284     self.fonts = {}
1285     self.rescale(scale)
1286     document.add_view(self)
1287     self.caret = None
1288    
1289     def redraw_start(self):
1290     self.document.redraw_start(self, self.scale)
1291    
1292     def redraw(self):
1293     self.document.redraw(self, self.scale)
1294 james 74
1295 james 17 def redraw_stop(self):
1296     self.document.redraw_stop(self, self.scale)
1297    
1298     def click(self, icon, buttons, x, y, sx, sy):
1299     if 0 <= x <= self.extent[0] and 0 <= y <= self.extent[1]:
1300     self.document.click(self, buttons, 400 * x / self.scale, 400 * y / self.scale)
1301    
1302     def key(self, icon, key):
1303     if self.caret:
1304     self.caret[0].key(self, key)
1305     else:
1306     swi.swi(0x400dc, 'i', key)
1307    
1308     def rescale(self, scale):
1309     for f in self.fonts.values():
1310     f.lose()
1311     self.fonts = {}
1312     for f in self.document.fonts.keys():
1313     self.fonts[f] = font(f[0], f[1] * scale, f[2] * scale)
1314     self.scale = scale
1315     self.extent = (int(scale * self.document.extent[0] / 400), int(scale * self.document.extent[1] / 400))
1316     self.set_extent(-self.document.margin, -self.document.margin,
1317     self.extent[0] + self.document.margin, self.extent[1] + self.document.margin)
1318     self.document.rescaled(self)
1319    
1320    
1321     class document_content:
1322 james 74
1323 james 17 def __init__(self, document, x0, y0, x1, y1):
1324     self.pos = [x0, y0, x1, y1]
1325     self.colour = 0x0000ff
1326     self.document = document
1327     document.add_content(self)
1328    
1329     def add_view(self, view):
1330     pass
1331    
1332     def remove_view(self, view):
1333     pass
1334 james 74
1335 james 17 def redraw(self, view, x, y, scale):
1336     set_colour(self.colour)
1337     plot(4, x + self.scaled_pos[0], y - self.scaled_pos[1])
1338     plot(21, x + self.scaled_pos[2], y - self.scaled_pos[1])
1339     plot(21, x + self.scaled_pos[2], y - self.scaled_pos[3])
1340     plot(21, x + self.scaled_pos[0], y - self.scaled_pos[3])
1341     plot(21, x + self.scaled_pos[0], y - self.scaled_pos[1])
1342     plot(21, x + self.scaled_pos[2], y - self.scaled_pos[3])
1343     plot(4, x + self.scaled_pos[2], y - self.scaled_pos[1])
1344     plot(21, x + self.scaled_pos[0], y - self.scaled_pos[3])
1345 james 74
1346 james 17 def click(self, view, buttons, x, y):
1347     self.colour = 0xffffff - self.colour
1348     self.document.refresh(self.pos[0], self.pos[1], self.pos[2], self.pos[3])

  ViewVC Help
Powered by ViewVC 1.1.26