diff options
-rw-r--r-- | Lib/lib-stdwin/mainloop.py | 171 | ||||
-rw-r--r-- | Lib/lib-stdwin/stdwinevents.py | 15 | ||||
-rw-r--r-- | Lib/nntplib.py | 20 | ||||
-rwxr-xr-x | Lib/stdwin/mainloop.py | 171 | ||||
-rwxr-xr-x | Lib/stdwin/stdwinevents.py | 15 | ||||
-rw-r--r-- | Lib/string.py | 5 | ||||
-rw-r--r-- | Lib/stringold.py | 5 |
7 files changed, 374 insertions, 28 deletions
diff --git a/Lib/lib-stdwin/mainloop.py b/Lib/lib-stdwin/mainloop.py index 0cf5bde..ab8ad3a 100644 --- a/Lib/lib-stdwin/mainloop.py +++ b/Lib/lib-stdwin/mainloop.py @@ -13,6 +13,11 @@ from stdwinevents import * windows = [] +# Last window that ever received an event +# +last_window = None + + # Function to register a window. # def register(win): @@ -28,6 +33,9 @@ def register(win): # (this is useful for cleanup actions). # def unregister(win): + global last_window + if win == last_window: + last_window = None if win in windows: windows.remove(win) # Not in 0.9.1 # 0.9.1 solution: @@ -49,6 +57,65 @@ def anywindow(): return None +# NEW: register any number of file descriptors +# +fdlist = [] +select_args = None +select_handlers = None +# +def registerfd(fd, mode, handler): + if mode not in ('r', 'w', 'x'): + raise ValueError, 'mode must be r, w or x' + if type(fd) <> type(0): + fd = fd.fileno() # If this fails it's not a proper select arg + for i in range(len(fdlist)): + if fdlist[i][:2] == (fd, mode): + raise ValueError, \ + '(fd, mode) combination already registered' + fdlist.append((fd, mode, handler)) + make_select_args() +# +def unregisterfd(fd, *args): + if type(fd) <> type(0): + fd = fd.fileno() # If this fails it's not a proper select arg + args = (fd,) + args + n = len(args) + for i in range(len(fdlist)): + if fdlist[i][:n] == args: + del fdlist[i] + make_select_args() +# +def make_select_args(): + global select_args, select_handlers + rlist, wlist, xlist = [], [], [] + rhandlers, whandlers, xhandlers = {}, {}, {} + for fd, mode, handler in fdlist: + if mode == 'r': + rlist.append(fd) + rhandlers[`fd`] = handler + if mode == 'w': + wlist.append(fd) + whandlers[`fd`] = handler + if mode == 'x': + xlist.append(fd) + xhandlers[`fd`] = handler + if rlist or wlist or xlist: + select_args = rlist, wlist, xlist + select_handlers = rhandlers, whandlers, xhandlers + else: + select_args = None + select_handlers = None +# +def do_select(): + import select + reply = apply(select.select, select_args) + for mode in 0, 1, 2: + list = reply[mode] + for fd in list: + handler = select_handlers[mode][`fd`] + handler(fd, 'rwx'[mode]) + + # Event processing main loop. # Return when there are no windows left, or when an unhandled # exception occurs. (It is safe to restart the main loop after @@ -57,17 +124,111 @@ def anywindow(): # into KeyboardInterrupt exceptions; these are turned back in events. # def mainloop(): - while windows: + stdwin_select_handler() # Process events already in stdwin queue + fd = stdwin.fileno() + while 1: + if windows: + registerfd(fd, 'r', stdwin_select_handler) + try: + while windows: + do_select() + stdwin_select_handler() + finally: + unregisterfd(fd) + elif fdlist: + while fdlist and not windows: + do_select() + else: + break + + +# Handle stdwin events until none are left +# +def stdwin_select_handler(*args): + while 1: + try: + event = stdwinq.pollevent() + except KeyboardInterrupt: + event = (WE_COMMAND, None, WC_CANCEL) + if event is None: + break + dispatch(event) + + +# Run a modal dialog loop for a window. The dialog window must have +# been registered first. This prohibits most events (except size/draw +# events) to other windows. The modal dialog loop ends when the +# dialog window unregisters itself. +# +passthrough = WE_SIZE, WE_DRAW +beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU +# +def modaldialog(window): + if window not in windows: + raise ValueError, 'modaldialog window not registered' + while window in windows: try: - dispatch(stdwinq.getevent()) + event = stdwinq.getevent() except KeyboardInterrupt: - dispatch(WE_COMMAND, stdwin.getactive(), WC_CANCEL) + event = WE_COMMAND, None, WC_CANCEL + etype, ewindow, edetail = event + if etype not in passthrough and ewindow <> window: + if etype in beeping: + stdwin.fleep() + continue + dispatch(event) # Dispatch a single event. +# Events for the no window in particular are sent to the active window +# or to the last window that received an event (these hacks are for the +# WE_LOST_SEL event, which is directed to no particular window). # Windows not in the windows list don't get their events: # events for such windows are silently ignored. # def dispatch(event): - if event[1] in windows: - event[1].dispatch(event) + global last_window + if event[1] == None: + active = stdwin.getactive() + if active: last_window = active + else: + last_window = event[1] + if last_window in windows: + last_window.dispatch(event) + + +# Dialog base class +# +class Dialog: + # + def init(self, title): + self.window = stdwin.open(title) + self.window.dispatch = self.dispatch + register(self.window) + return self + # + def close(self): + unregister(self.window) + del self.window.dispatch + self.window.close() + # + def dispatch(self, event): + etype, ewindow, edetail = event + if etype == WE_CLOSE: + self.close() + + +# Standard modal dialogs +# XXX implemented using stdwin dialogs for now +# +def askstr(prompt, default): + return stdwin.askstr(prompt, default) +# +def askync(prompt, yesorno): + return stdwin.askync(prompt, yesorno) +# +def askfile(prompt, default, new): + return stdwin.askfile(prompt, default, new) +# +def message(msg): + stdwin.message(msg) diff --git a/Lib/lib-stdwin/stdwinevents.py b/Lib/lib-stdwin/stdwinevents.py index 9f22dd9..62cf8d2 100644 --- a/Lib/lib-stdwin/stdwinevents.py +++ b/Lib/lib-stdwin/stdwinevents.py @@ -44,3 +44,18 @@ WC_RETURN = 9 # return or enter key WS_CLIPBOARD = 0 WS_PRIMARY = 1 WS_SECONDARY = 2 + +# Modifier masks in key and mouse events + +WM_SHIFT = (1 << 0) +WM_LOCK = (1 << 1) +WM_CONTROL = (1 << 2) +WM_META = (1 << 3) +WM_OPTION = (1 << 4) +WM_NUM = (1 << 5) + +WM_BUTTON1 = (1 << 8) +WM_BUTTON2 = (1 << 9) +WM_BUTTON3 = (1 << 10) +WM_BUTTON4 = (1 << 11) +WM_BUTTON5 = (1 << 12) diff --git a/Lib/nntplib.py b/Lib/nntplib.py index 18fa398..c448d48 100644 --- a/Lib/nntplib.py +++ b/Lib/nntplib.py @@ -4,7 +4,7 @@ # Example: # -# >>> from nntp import NNTP +# >>> from nntplib import NNTP # >>> s = NNTP().init('charon') # >>> resp, count, first, last, name = s.group('nlnet.misc') # >>> print 'Group', name, 'has', count, 'articles, range', first, 'to', last @@ -32,12 +32,12 @@ import socket import string -# Exception raiseds when an error or invalid response is received +# Exception raised when an error or invalid response is received -error_reply = 'nntp.error_reply' # unexpected [123]xx reply -error_function = 'nntp.error_function' # 4xx errors -error_form = 'nntp.error_form' # 5xx errors -error_protocol = 'nntp.error_protocol' # response does not begin with [1-5] +error_reply = 'nntplib.error_reply' # unexpected [123]xx reply +error_temp = 'nntplib.error_temp' # 4xx errors +error_perm = 'nntplib.error_perm' # 5xx errors +error_proto = 'nntplib.error_proto' # response does not begin with [1-5] # Standard port used by NNTP servers @@ -119,11 +119,11 @@ class NNTP: if self.debugging: print '*resp*', `resp` c = resp[:1] if c == '4': - raise error_function, resp + raise error_temp, resp if c == '5': - raise error_form, resp + raise error_perm, resp if c not in '123': - raise error_protocol, resp + raise error_proto, resp return resp # Internal: get a response plus following text from the server. @@ -342,7 +342,7 @@ class NNTP: def ihave(self, id, f): resp = self.shortcmd('IHAVE ' + id) - # Raises error_function if the server already has it + # Raises error_??? if the server already has it if resp[0] <> '3': raise error_reply, resp while 1: diff --git a/Lib/stdwin/mainloop.py b/Lib/stdwin/mainloop.py index 0cf5bde..ab8ad3a 100755 --- a/Lib/stdwin/mainloop.py +++ b/Lib/stdwin/mainloop.py @@ -13,6 +13,11 @@ from stdwinevents import * windows = [] +# Last window that ever received an event +# +last_window = None + + # Function to register a window. # def register(win): @@ -28,6 +33,9 @@ def register(win): # (this is useful for cleanup actions). # def unregister(win): + global last_window + if win == last_window: + last_window = None if win in windows: windows.remove(win) # Not in 0.9.1 # 0.9.1 solution: @@ -49,6 +57,65 @@ def anywindow(): return None +# NEW: register any number of file descriptors +# +fdlist = [] +select_args = None +select_handlers = None +# +def registerfd(fd, mode, handler): + if mode not in ('r', 'w', 'x'): + raise ValueError, 'mode must be r, w or x' + if type(fd) <> type(0): + fd = fd.fileno() # If this fails it's not a proper select arg + for i in range(len(fdlist)): + if fdlist[i][:2] == (fd, mode): + raise ValueError, \ + '(fd, mode) combination already registered' + fdlist.append((fd, mode, handler)) + make_select_args() +# +def unregisterfd(fd, *args): + if type(fd) <> type(0): + fd = fd.fileno() # If this fails it's not a proper select arg + args = (fd,) + args + n = len(args) + for i in range(len(fdlist)): + if fdlist[i][:n] == args: + del fdlist[i] + make_select_args() +# +def make_select_args(): + global select_args, select_handlers + rlist, wlist, xlist = [], [], [] + rhandlers, whandlers, xhandlers = {}, {}, {} + for fd, mode, handler in fdlist: + if mode == 'r': + rlist.append(fd) + rhandlers[`fd`] = handler + if mode == 'w': + wlist.append(fd) + whandlers[`fd`] = handler + if mode == 'x': + xlist.append(fd) + xhandlers[`fd`] = handler + if rlist or wlist or xlist: + select_args = rlist, wlist, xlist + select_handlers = rhandlers, whandlers, xhandlers + else: + select_args = None + select_handlers = None +# +def do_select(): + import select + reply = apply(select.select, select_args) + for mode in 0, 1, 2: + list = reply[mode] + for fd in list: + handler = select_handlers[mode][`fd`] + handler(fd, 'rwx'[mode]) + + # Event processing main loop. # Return when there are no windows left, or when an unhandled # exception occurs. (It is safe to restart the main loop after @@ -57,17 +124,111 @@ def anywindow(): # into KeyboardInterrupt exceptions; these are turned back in events. # def mainloop(): - while windows: + stdwin_select_handler() # Process events already in stdwin queue + fd = stdwin.fileno() + while 1: + if windows: + registerfd(fd, 'r', stdwin_select_handler) + try: + while windows: + do_select() + stdwin_select_handler() + finally: + unregisterfd(fd) + elif fdlist: + while fdlist and not windows: + do_select() + else: + break + + +# Handle stdwin events until none are left +# +def stdwin_select_handler(*args): + while 1: + try: + event = stdwinq.pollevent() + except KeyboardInterrupt: + event = (WE_COMMAND, None, WC_CANCEL) + if event is None: + break + dispatch(event) + + +# Run a modal dialog loop for a window. The dialog window must have +# been registered first. This prohibits most events (except size/draw +# events) to other windows. The modal dialog loop ends when the +# dialog window unregisters itself. +# +passthrough = WE_SIZE, WE_DRAW +beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU +# +def modaldialog(window): + if window not in windows: + raise ValueError, 'modaldialog window not registered' + while window in windows: try: - dispatch(stdwinq.getevent()) + event = stdwinq.getevent() except KeyboardInterrupt: - dispatch(WE_COMMAND, stdwin.getactive(), WC_CANCEL) + event = WE_COMMAND, None, WC_CANCEL + etype, ewindow, edetail = event + if etype not in passthrough and ewindow <> window: + if etype in beeping: + stdwin.fleep() + continue + dispatch(event) # Dispatch a single event. +# Events for the no window in particular are sent to the active window +# or to the last window that received an event (these hacks are for the +# WE_LOST_SEL event, which is directed to no particular window). # Windows not in the windows list don't get their events: # events for such windows are silently ignored. # def dispatch(event): - if event[1] in windows: - event[1].dispatch(event) + global last_window + if event[1] == None: + active = stdwin.getactive() + if active: last_window = active + else: + last_window = event[1] + if last_window in windows: + last_window.dispatch(event) + + +# Dialog base class +# +class Dialog: + # + def init(self, title): + self.window = stdwin.open(title) + self.window.dispatch = self.dispatch + register(self.window) + return self + # + def close(self): + unregister(self.window) + del self.window.dispatch + self.window.close() + # + def dispatch(self, event): + etype, ewindow, edetail = event + if etype == WE_CLOSE: + self.close() + + +# Standard modal dialogs +# XXX implemented using stdwin dialogs for now +# +def askstr(prompt, default): + return stdwin.askstr(prompt, default) +# +def askync(prompt, yesorno): + return stdwin.askync(prompt, yesorno) +# +def askfile(prompt, default, new): + return stdwin.askfile(prompt, default, new) +# +def message(msg): + stdwin.message(msg) diff --git a/Lib/stdwin/stdwinevents.py b/Lib/stdwin/stdwinevents.py index 9f22dd9..62cf8d2 100755 --- a/Lib/stdwin/stdwinevents.py +++ b/Lib/stdwin/stdwinevents.py @@ -44,3 +44,18 @@ WC_RETURN = 9 # return or enter key WS_CLIPBOARD = 0 WS_PRIMARY = 1 WS_SECONDARY = 2 + +# Modifier masks in key and mouse events + +WM_SHIFT = (1 << 0) +WM_LOCK = (1 << 1) +WM_CONTROL = (1 << 2) +WM_META = (1 << 3) +WM_OPTION = (1 << 4) +WM_NUM = (1 << 5) + +WM_BUTTON1 = (1 << 8) +WM_BUTTON2 = (1 << 9) +WM_BUTTON3 = (1 << 10) +WM_BUTTON4 = (1 << 11) +WM_BUTTON5 = (1 << 12) diff --git a/Lib/string.py b/Lib/string.py index b4e0d5e..aed3eaf 100644 --- a/Lib/string.py +++ b/Lib/string.py @@ -82,10 +82,7 @@ def splitfields(s, sep): # Join words with spaces between them def join(words): - res = '' - for w in words: - res = res + (' ' + w) - return res[1:] + return joinfields(words, ' ') # Join fields with separator def joinfields(words, sep): diff --git a/Lib/stringold.py b/Lib/stringold.py index b4e0d5e..aed3eaf 100644 --- a/Lib/stringold.py +++ b/Lib/stringold.py @@ -82,10 +82,7 @@ def splitfields(s, sep): # Join words with spaces between them def join(words): - res = '' - for w in words: - res = res + (' ' + w) - return res[1:] + return joinfields(words, ' ') # Join fields with separator def joinfields(words, sep): |