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

Contents of /pythonwimp/wimp.py

Parent Directory Parent Directory | Revision Log Revision Log


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

1 """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