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

Contents of /pythonwimp/wimp.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 74 - (show annotations) (download) (as text)
Sun May 15 12:08:06 2005 UTC (19 years ago) by james
File MIME type: text/x-python
File size: 43360 byte(s)
Implement paragraph class using rufl module.

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

  ViewVC Help
Powered by ViewVC 1.1.26