Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 1 | # Standard main loop for *all* STDWIN applications. |
| 2 | # This requires that applications: |
| 3 | # - register their windows on creation and unregister them when closed |
| 4 | # - have a 'dispatch' function as a window member |
| 5 | |
| 6 | |
| 7 | import stdwin, stdwinq |
| 8 | from stdwinevents import * |
| 9 | |
| 10 | |
| 11 | # List of windows known to the main loop. |
| 12 | # |
| 13 | windows = [] |
| 14 | |
| 15 | |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 16 | # Last window that ever received an event |
| 17 | # |
| 18 | last_window = None |
| 19 | |
| 20 | |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 21 | # Function to register a window. |
| 22 | # |
| 23 | def register(win): |
| 24 | # First test the dispatch function by passing it a null event -- |
| 25 | # this catches registration of unconforming windows. |
| 26 | win.dispatch(WE_NULL, win, None) |
| 27 | if win not in windows: |
| 28 | windows.append(win) |
| 29 | |
| 30 | |
| 31 | # Function to unregister a window. |
| 32 | # It is not an error to unregister an already unregistered window |
| 33 | # (this is useful for cleanup actions). |
| 34 | # |
| 35 | def unregister(win): |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 36 | global last_window |
| 37 | if win == last_window: |
| 38 | last_window = None |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 39 | if win in windows: |
| 40 | windows.remove(win) # Not in 0.9.1 |
Guido van Rossum | 3f4f917 | 1991-11-19 20:41:07 +0000 | [diff] [blame] | 41 | # 0.9.1 solution: |
| 42 | #for i in range(len(windows)): |
| 43 | # if windows[i] = win: |
| 44 | # del windows[i] |
| 45 | # break |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 46 | |
| 47 | |
| 48 | # Interfaces used by WindowSched. |
| 49 | # |
| 50 | def countwindows(): |
| 51 | return len(windows) |
| 52 | # |
| 53 | def anywindow(): |
| 54 | if windows: |
| 55 | return windows[0] |
| 56 | else: |
| 57 | return None |
| 58 | |
| 59 | |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 60 | # NEW: register any number of file descriptors |
| 61 | # |
| 62 | fdlist = [] |
| 63 | select_args = None |
| 64 | select_handlers = None |
| 65 | # |
| 66 | def registerfd(fd, mode, handler): |
| 67 | if mode not in ('r', 'w', 'x'): |
| 68 | raise ValueError, 'mode must be r, w or x' |
| 69 | if type(fd) <> type(0): |
| 70 | fd = fd.fileno() # If this fails it's not a proper select arg |
| 71 | for i in range(len(fdlist)): |
| 72 | if fdlist[i][:2] == (fd, mode): |
| 73 | raise ValueError, \ |
| 74 | '(fd, mode) combination already registered' |
| 75 | fdlist.append((fd, mode, handler)) |
| 76 | make_select_args() |
| 77 | # |
| 78 | def unregisterfd(fd, *args): |
| 79 | if type(fd) <> type(0): |
| 80 | fd = fd.fileno() # If this fails it's not a proper select arg |
| 81 | args = (fd,) + args |
| 82 | n = len(args) |
| 83 | for i in range(len(fdlist)): |
| 84 | if fdlist[i][:n] == args: |
| 85 | del fdlist[i] |
| 86 | make_select_args() |
| 87 | # |
| 88 | def make_select_args(): |
| 89 | global select_args, select_handlers |
| 90 | rlist, wlist, xlist = [], [], [] |
| 91 | rhandlers, whandlers, xhandlers = {}, {}, {} |
| 92 | for fd, mode, handler in fdlist: |
| 93 | if mode == 'r': |
| 94 | rlist.append(fd) |
| 95 | rhandlers[`fd`] = handler |
| 96 | if mode == 'w': |
| 97 | wlist.append(fd) |
| 98 | whandlers[`fd`] = handler |
| 99 | if mode == 'x': |
| 100 | xlist.append(fd) |
| 101 | xhandlers[`fd`] = handler |
| 102 | if rlist or wlist or xlist: |
| 103 | select_args = rlist, wlist, xlist |
| 104 | select_handlers = rhandlers, whandlers, xhandlers |
| 105 | else: |
| 106 | select_args = None |
| 107 | select_handlers = None |
| 108 | # |
| 109 | def do_select(): |
| 110 | import select |
| 111 | reply = apply(select.select, select_args) |
| 112 | for mode in 0, 1, 2: |
| 113 | list = reply[mode] |
| 114 | for fd in list: |
| 115 | handler = select_handlers[mode][`fd`] |
| 116 | handler(fd, 'rwx'[mode]) |
| 117 | |
| 118 | |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 119 | # Event processing main loop. |
| 120 | # Return when there are no windows left, or when an unhandled |
| 121 | # exception occurs. (It is safe to restart the main loop after |
| 122 | # an unsuccessful exit.) |
| 123 | # Python's stdwin.getevent() turns WE_COMMAND/WC_CANCEL events |
| 124 | # into KeyboardInterrupt exceptions; these are turned back in events. |
| 125 | # |
| 126 | def mainloop(): |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 127 | stdwin_select_handler() # Process events already in stdwin queue |
| 128 | fd = stdwin.fileno() |
| 129 | while 1: |
| 130 | if windows: |
| 131 | registerfd(fd, 'r', stdwin_select_handler) |
| 132 | try: |
| 133 | while windows: |
| 134 | do_select() |
| 135 | stdwin_select_handler() |
| 136 | finally: |
| 137 | unregisterfd(fd) |
| 138 | elif fdlist: |
| 139 | while fdlist and not windows: |
| 140 | do_select() |
| 141 | else: |
| 142 | break |
| 143 | |
| 144 | |
| 145 | # Handle stdwin events until none are left |
| 146 | # |
| 147 | def stdwin_select_handler(*args): |
| 148 | while 1: |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 149 | try: |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 150 | event = stdwinq.pollevent() |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 151 | except KeyboardInterrupt: |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 152 | event = (WE_COMMAND, None, WC_CANCEL) |
| 153 | if event is None: |
| 154 | break |
| 155 | dispatch(event) |
| 156 | |
| 157 | |
| 158 | # Run a modal dialog loop for a window. The dialog window must have |
| 159 | # been registered first. This prohibits most events (except size/draw |
| 160 | # events) to other windows. The modal dialog loop ends when the |
| 161 | # dialog window unregisters itself. |
| 162 | # |
| 163 | passthrough = WE_SIZE, WE_DRAW |
| 164 | beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU |
| 165 | # |
| 166 | def modaldialog(window): |
| 167 | if window not in windows: |
| 168 | raise ValueError, 'modaldialog window not registered' |
| 169 | while window in windows: |
| 170 | try: |
| 171 | event = stdwinq.getevent() |
| 172 | except KeyboardInterrupt: |
| 173 | event = WE_COMMAND, None, WC_CANCEL |
| 174 | etype, ewindow, edetail = event |
| 175 | if etype not in passthrough and ewindow <> window: |
| 176 | if etype in beeping: |
| 177 | stdwin.fleep() |
| 178 | continue |
| 179 | dispatch(event) |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 180 | |
| 181 | |
| 182 | # Dispatch a single event. |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 183 | # Events for the no window in particular are sent to the active window |
| 184 | # or to the last window that received an event (these hacks are for the |
| 185 | # WE_LOST_SEL event, which is directed to no particular window). |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 186 | # Windows not in the windows list don't get their events: |
| 187 | # events for such windows are silently ignored. |
| 188 | # |
| 189 | def dispatch(event): |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 190 | global last_window |
| 191 | if event[1] == None: |
| 192 | active = stdwin.getactive() |
| 193 | if active: last_window = active |
| 194 | else: |
| 195 | last_window = event[1] |
| 196 | if last_window in windows: |
| 197 | last_window.dispatch(event) |
| 198 | |
| 199 | |
| 200 | # Dialog base class |
| 201 | # |
| 202 | class Dialog: |
| 203 | # |
| 204 | def init(self, title): |
| 205 | self.window = stdwin.open(title) |
| 206 | self.window.dispatch = self.dispatch |
| 207 | register(self.window) |
| 208 | return self |
| 209 | # |
| 210 | def close(self): |
| 211 | unregister(self.window) |
| 212 | del self.window.dispatch |
| 213 | self.window.close() |
| 214 | # |
| 215 | def dispatch(self, event): |
| 216 | etype, ewindow, edetail = event |
| 217 | if etype == WE_CLOSE: |
| 218 | self.close() |
| 219 | |
| 220 | |
| 221 | # Standard modal dialogs |
| 222 | # XXX implemented using stdwin dialogs for now |
| 223 | # |
| 224 | def askstr(prompt, default): |
| 225 | return stdwin.askstr(prompt, default) |
| 226 | # |
| 227 | def askync(prompt, yesorno): |
| 228 | return stdwin.askync(prompt, yesorno) |
| 229 | # |
| 230 | def askfile(prompt, default, new): |
| 231 | return stdwin.askfile(prompt, default, new) |
| 232 | # |
| 233 | def message(msg): |
| 234 | stdwin.message(msg) |