summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/lib-stdwin/mainloop.py171
-rw-r--r--Lib/lib-stdwin/stdwinevents.py15
-rw-r--r--Lib/nntplib.py20
-rwxr-xr-xLib/stdwin/mainloop.py171
-rwxr-xr-xLib/stdwin/stdwinevents.py15
-rw-r--r--Lib/string.py5
-rw-r--r--Lib/stringold.py5
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):