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

Annotation of /pythonwimp/wimp.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 17 - (hide annotations) (download) (as text)
Tue Feb 11 09:59:30 2003 UTC (21 years, 3 months ago) by james
File MIME type: text/x-python
File size: 39852 byte(s)
Initial import.

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

  ViewVC Help
Powered by ViewVC 1.1.26