[Spambayes-checkins]
spambayes/Outlook2000/dialogs dlgcore.py, NONE,
1.1.2.1 dlgutils.py, NONE, 1.1.2.1 processors.py, NONE,
1.1.2.1 opt_processors.py, NONE, 1.1.2.1
Mark Hammond
mhammond at users.sourceforge.net
Sun Aug 3 19:15:49 EDT 2003
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1:/tmp/cvs-serv22917
Added Files:
Tag: outlook-dialog-branch
dlgcore.py dlgutils.py processors.py opt_processors.py
Log Message:
New data driven dialogs loaded from Windows .rc scripts.
--- NEW FILE: dlgcore.py ---
# A core, data-driven dialog.
# Driven completely by "Control Processor" objects.
# This module is part of the spambayes project, which is Copyright 2002
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
import win32gui, win32api, win32con
import commctrl
import struct, array
from dlgutils import *
# Isolate the nasty stuff for tooltips somewhere.
class TooltipManager:
def __init__(self, dialog):
self.dialog = dialog
self.hwnd_tooltip = None
self.tooltip_tools = {}
def HideTooltip(self):
if self.hwnd_tooltip is not None:
win32gui.SendMessage(self.hwnd_tooltip, commctrl.TTM_TRACKACTIVATE, 0, 0)
def ShowTooltipForControl(self, control_id, text):
# Note sure this tooltip stuff is quite right!
# Hide an existing one, so the new one gets created.
# (new one empty is no big deal, but hiding the old one is, so
# we get re-queried for the text.
hwnd_dialog = self.dialog.hwnd
self.HideTooltip()
if self.hwnd_tooltip is None:
TTS_BALLOON = 0x40
self.hwnd_tooltip = win32gui.CreateWindowEx(0, "tooltips_class32", None,
win32con.WS_POPUP | TTS_BALLOON,
win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
hwnd_dialog, 0, 0, None)
# 80 chars max for our tooltip
# hrm - how to measure this in pixels!
win32gui.SendMessage(self.hwnd_tooltip,
commctrl.TTM_SETMAXTIPWIDTH,
0, 300)
format = "iiiiiiiiiii"
tt_size = struct.calcsize(format)
buffer = array.array("c", text + "\0")
text_address, size = buffer.buffer_info()
uID = control_id
flags = commctrl.TTF_TRACK | commctrl.TTF_ABSOLUTE
data = struct.pack(format, tt_size, flags, hwnd_dialog, uID, 0,0,0,0, 0, text_address, 0)
# Add a tool for this control only if we haven't already
if control_id not in self.tooltip_tools:
win32gui.SendMessage(self.hwnd_tooltip,
commctrl.TTM_ADDTOOL,
0, data)
self.tooltip_tools[control_id] = 1
control = win32gui.GetDlgItem(hwnd_dialog, control_id)
child_rect = win32gui.GetWindowRect(control)
xOff = yOff = 15 # just below and right of the control
win32gui.SendMessage(self.hwnd_tooltip,
commctrl.TTM_TRACKPOSITION,
0,
MAKELONG(child_rect[0]+xOff, child_rect[1]+yOff))
win32gui.SendMessage(self.hwnd_tooltip,
commctrl.TTM_TRACKACTIVATE,
1,data)
class Dialog:
def __init__(self, parent, manager, idd, option_handlers):
parser = manager.dialog_parser
self.parent = parent
self.manager = manager
self.tt = TooltipManager(self)
self.dialog_def = parser.dialogs[idd]
self.template = self.dialog_def.createDialogTemplate()
win32gui.InitCommonControls()
self.hinst = win32api.GetModuleHandle(None)
self.options = manager.options
self.command_processors = {}
self.processor_message_map = {} # WM_MESSAGE : [processors_who_want_it]
self.all_processors = []
for data in option_handlers:
klass = data[0]
id_names = data[1]
rest = data[2:]
ids = id_names.split()
int_ids = [ parser.ids[id] for id in ids]
instance = klass(self,int_ids, *rest)
self.all_processors.append(instance)
for int_id in int_ids:
self.command_processors[int_id] = instance
for message in instance.GetMessages():
existing = self.processor_message_map.setdefault(message, [])
existing.append(instance)
def CreateWindow(self):
self._DoCreate(win32gui.CreateDialogIndirect)
def DoModal(self):
return self._DoCreate(win32gui.DialogBoxIndirect)
def OnCommandProcessorMessage(self, hwnd, msg, wparam, lparam):
for p in self.processor_message_map[msg]:
p.OnMessage(msg, wparam, lparam)
def GetMessageMap(self):
ret = {
#win32con.WM_SIZE: self.OnSize,
win32con.WM_COMMAND: self.OnCommand,
win32con.WM_NOTIFY: self.OnNotify,
win32con.WM_INITDIALOG: self.OnInitDialog,
win32con.WM_CLOSE: self.OnClose,
win32con.WM_HELP: self.OnHelp,
win32con.WM_DESTROY: self.OnDestroy,
win32con.WM_LBUTTONDOWN: self.OnLButtonDown,
win32con.WM_ACTIVATE: self.OnActivate,
}
for key in self.processor_message_map.keys():
if key in ret:
print "*** WARNING: Overwriting message!!!"
ret[key] = self.OnCommandProcessorMessage
return ret
def _DoCreate(self, fn):
message_map = self.GetMessageMap()
return win32gui.DialogBoxIndirect(self.hinst, self.template, self.parent, message_map)
def OnInitDialog(self, hwnd, msg, wparam, lparam):
self.hwnd = hwnd
self.LoadAllControls()
# centre the dialog
desktop = win32gui.GetDesktopWindow()
l,t,r,b = win32gui.GetWindowRect(self.hwnd)
dt_l, dt_t, dt_r, dt_b = win32gui.GetWindowRect(desktop)
centre_x, centre_y = win32gui.ClientToScreen( desktop, ( (dt_r-dt_l)/2, (dt_b-dt_t)/2) )
win32gui.MoveWindow(hwnd, centre_x-(r/2), centre_y-(b/2), r-l, b-t, 0)
l,t,r,b = win32gui.GetClientRect(self.hwnd)
self._DoSize(r-l,b-t, 1)
def OnDestroy(self, hwnd, msg, wparam, lparam):
print "OnDestroy"
self.command_processors = None
self.all_processors = None
self.processor_message_map = None
def _DoSize(self, cx, cy, repaint = 1):
print "resize"
def OnLButtonDown(self, hwnd, msg, wparam, lparam):
self.tt.HideTooltip()
def OnActivate(self, hwnd, msg, wparam, lparam):
self.tt.HideTooltip()
def OnHelp(self, hwnd, msg, wparam, lparam):
format = "iiiiiii"
buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam)
cbSize, iContextType, iCtrlId, hItemHandle, dwContextID, x, y = \
struct.unpack(format, buf)
#print "OnHelp", cbSize, iContextType, iCtrlId, hItemHandle, dwContextID, x, y
cp = self.command_processors.get(iCtrlId)
tt_text = None
if cp is not None:
tt_text = cp.GetPopupHelpText(iCtrlId)
else:
print "Can not get command processor for", iCtrlId
if tt_text:
self.tt.ShowTooltipForControl(iCtrlId, tt_text)
else:
self.tt.HideTooltip()
return 1
def LoadAllControls(self):
for p in self.all_processors:
p.Init()
def SaveAllControls(self):
for p in self.all_processors:
try:
p.Done(True)
except ValueError, why:
mb_flags = win32con.MB_ICONEXCLAMATION | win32con.MB_OK
win32gui.MessageBox(self.hwnd, str(why),
self.dialog_def.caption, mb_flags)
win32gui.SetFocus(p.GetControl())
return False
return True
def OnClose(self, hwnd, msg, wparam, lparam):
print "OnClose"
if not self.SaveAllControls():
return 1
win32gui.EndDialog(hwnd, 0)
def OnSize(self, hwnd, msg, wparam, lparam):
x = win32api.LOWORD(lparam)
y = win32api.HIWORD(lparam)
self._DoSize(x,y)
return 1
def OnNotify(self, hwnd, msg, wparam, lparam):
#print "OnNotify", hwnd, msg, wparam, lparam
# Parse the NMHDR
format = "iii"
buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam)
hwndFrom, idFrom, code = struct.unpack(format, buf)
code += 0x4f0000 # hrm - wtf - commctrl uses this, and it works with mfc. *sigh*
# delegate rest to our commands.
handler = self.command_processors.get(idFrom)
if handler is None:
print "Ignoring OnNotify for", idFrom
return
handler.OnNotify( (hwndFrom, idFrom, code), wparam, lparam)
def OnCommand(self, hwnd, msg, wparam, lparam):
self.tt.HideTooltip()
id = win32api.LOWORD(wparam)
handler = self.command_processors.get(id)
if handler is None:
print "Ignoring OnCommand for", id
return
handler.OnCommand(wparam, lparam)
--- NEW FILE: dlgutils.py ---
# Generic utilities for dialog functions.
# This module is part of the spambayes project, which is Copyright 2002
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
def MAKELONG(l,h):
return ((h & 0xFFFF) << 16) | (l & 0xFFFF)
def SetWaitCursor(wait):
import win32gui
if wait:
hCursor = win32gui.LoadCursor(0, win32con.IDC_WAIT)
else:
hCursor = win32gui.LoadCursor(0, 0)
win32gui.SetCursor(hCursor)
--- NEW FILE: processors.py ---
# Control Processors for our dialog.
# This module is part of the spambayes project, which is Copyright 2002
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
import win32gui, win32api, win32con
import commctrl
import struct, array
from dlgutils import *
# A generic set of "ControlProcessors". A control processor by itself only
# does a few basic things.
class ControlProcessor:
def __init__(self, window, control_ids):
self.control_id = control_ids[0]
self.other_ids = control_ids[1:]
self.window = window
def Init(self):
pass
def Done(self, saving):
pass
def GetControl(self, control_id = None):
control_id = control_id or self.control_id
return win32gui.GetDlgItem(self.window.hwnd, control_id)
def GetPopupHelpText(self, idFrom):
return None
def OnCommand(self, wparam, lparam):
pass
def OnNotify(self, nmhdr, wparam, lparam):
pass
def GetMessages(self):
return []
def OnMessage(self, msg, wparam, lparam):
raise RuntimeError, "I don't hook any messages, so I shouldn't be called"
class ButtonProcessor(ControlProcessor):
def OnCommand(self, wparam, lparam):
code = win32api.HIWORD(wparam)
id = win32api.LOWORD(wparam)
if code == win32con.BN_CLICKED:
self.OnClicked(id)
class CloseButtonProcessor(ButtonProcessor):
def OnClicked(self, id):
print "clicked"
win32gui.SendMessage(self.window.hwnd, win32con.WM_CLOSE, 0, 0)
def GetPopupHelpText(self, ctrlid):
return "Closes this dialog"
class CommandButtonProcessor(ButtonProcessor):
def __init__(self, window, control_ids, func, args):
assert len(control_ids)==1
self.func = func
self.args = args
ControlProcessor.__init__(self, window, control_ids)
def OnClicked(self, id):
# Bit of a hack - always pass the manager as the first arg.
args = (self.window.manager,) + self.args
self.func(*args)
def GetPopupHelpText(self, ctrlid):
assert ctrlid == self.control_id
return " ".join(self.func.__doc__.split())
class DialogCommand(ButtonProcessor):
def __init__(self, window, control_ids, idd):
self.idd = idd
ButtonProcessor.__init__(self, window, control_ids)
def OnClicked(self, id):
parent = self.window.hwnd
# Thos form and the other form may "share" options, or at least
# depend on others. So we must save the current form back to the
# options object, display the new dialog, then reload the current
# form from the options object/
self.window.SaveAllControls()
ShowDialog(parent, self.window.manager, self.idd)
self.window.LoadAllControls()
def GetPopupHelpText(self, id):
dd = self.window.manager.dialog_parser.dialogs[self.idd]
return "Displays the %s dialog" % dd.caption
--- NEW FILE: opt_processors.py ---
# Option Control Processors for our dialog.
# These are extensions to basic Control Processors that are linked with
# SpamBayes options.
# This module is part of the spambayes project, which is Copyright 2002
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
import win32gui, win32api, win32con
import commctrl
import struct, array
from dlgutils import *
import processors
# A ControlProcessor that is linked up with options. These get a bit smarter.
class OptionControlProcessor(processors.ControlProcessor):
def __init__(self, window, control_ids, option):
processors.ControlProcessor.__init__(self, window, control_ids)
if option:
sect_name, option_name = option.split(".")
self.option = window.options.get_option(sect_name, option_name)
else:
self.option = None
self.value = None
def GetPopupHelpText(self, idFrom):
return " ".join(self.option.doc().split())
# We override Init, and break it into 2 steps.
# - Load option into self.value
# - set the control from self.value.
def Init(self):
self.LoadOptionValue()
self.UpdateControl_FromValue()
# We override Done into 2 similar steps
# - Update self.value from the current contents of the control.
# - Write self.value back to the option.
def Done(self, saving):
if saving:
self.UpdateValue_FromControl()
self.StoreOptionValue()
def LoadOptionValue(self):
self.value = self.option.get()
def StoreOptionValue(self):
print "Setting", self.option.name, "to", self.value
self.option.set(self.value)
# Only sub-classes know how to update their controls from the value.
def UpdateControl_FromValue(self):
raise NotImplementedError
def UpdateValue_FromControl(self):
raise NotImplementedError
# "Bool" buttons are simple - just toggle self.value on the click.
# (Little more complex to handle "radio buttons" that are also boolean
# where we must "uncheck" the other button.
class BoolButtonProcessor(OptionControlProcessor):
def OnCommand(self, wparam, lparam):
code = win32api.HIWORD(wparam)
if code == win32con.BN_CLICKED:
self.UpdateValue_FromControl()
def UpdateControl_FromValue(self):
win32gui.SendMessage(self.GetControl(), win32con.BM_SETCHECK, self.value)
for other in self.other_ids:
win32gui.SendMessage(self.GetControl(other), win32con.BM_SETCHECK, not self.value)
def UpdateValue_FromControl(self):
check = win32gui.SendMessage(self.GetControl(), win32con.BM_GETCHECK)
check = not not check # force bool!
self.value = check
# A "Combo" processor, that loads valid strings from the option.
class ComboProcessor(OptionControlProcessor):
def UpdateControl_FromValue(self):
# First load the combo options.
combo = self.GetControl()
index = sel_index = 0
for s in self.option.valid_input():
win32gui.SendMessage(combo, win32con.CB_ADDSTRING, 0, s)
if self.value.startswith(s):
sel_index = index
index += 1
win32gui.SendMessage(combo, win32con.CB_SETCURSEL, sel_index, 0)
def UpdateValue_FromControl(self):
combo = self.GetControl()
sel = win32gui.SendMessage(combo, win32con.CB_GETCURSEL)
len = win32gui.SendMessage(combo, win32con.CB_GETLBTEXTLEN, sel)
buffer = array.array("c", "\0" * (len + 1))
win32gui.SendMessage(combo, win32con.CB_GETLBTEXT, sel, buffer)
# Trim the \0 from the end.
self.value = buffer.tostring()[:-1]
print "Combo gave me", self.value
class EditNumberProcessor(OptionControlProcessor):
def __init__(self, window, control_ids, option):
self.slider_id = control_ids and control_ids[1]
OptionControlProcessor.__init__(self, window, control_ids, option)
def GetPopupHelpText(self, id):
if id == self.slider_id:
return "As you drag this slider, the value to the right will " \
"automatically adjust"
return OptionControlProcessor.GetPopupHelpText(self, id)
def GetMessages(self):
return [win32con.WM_HSCROLL]
def OnMessage(self, msg, wparam, lparam):
slider = self.GetControl(self.slider_id)
if slider == lparam:
slider_pos = win32gui.SendMessage(slider, commctrl.TBM_GETPOS, 0, 0)
slider_pos = float(slider_pos)
str_val = str(slider_pos)
edit = self.GetControl()
win32gui.SendMessage(edit, win32con.WM_SETTEXT, 0, str_val)
def OnCommand(self, wparam, lparam):
code = win32api.HIWORD(wparam)
if code==win32con.EN_CHANGE:
try:
self.UpdateValue_FromControl()
self.UpdateSlider_FromEdit()
except ValueError:
# They are typing - value may be currently invalid
pass
def Init(self):
OptionControlProcessor.Init(self)
if self.slider_id:
self.InitSlider()
def InitSlider(self):
slider = self.GetControl(self.slider_id)
win32gui.SendMessage(slider, commctrl.TBM_SETRANGE, 0, MAKELONG(0, 100))
win32gui.SendMessage(slider, commctrl.TBM_SETLINESIZE, 0, 1)
win32gui.SendMessage(slider, commctrl.TBM_SETPAGESIZE, 0, 5)
win32gui.SendMessage(slider, commctrl.TBM_SETTICFREQ, 10, 0)
def UpdateControl_FromValue(self):
win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT, 0, str(self.value))
self.UpdateSlider_FromEdit()
def UpdateSlider_FromEdit(self):
slider = self.GetControl(self.slider_id)
try:
# Get as float so we dont fail should the .0 be there, but
# then convert to int as the slider only works with ints
val = int(float(self.value))
except ValueError:
return
win32gui.SendMessage(slider, commctrl.TBM_SETPOS, 1, val)
def UpdateValue_FromControl(self):
buf_size = 100
buf = win32gui.PyMakeBuffer(buf_size)
nchars = win32gui.SendMessage(self.GetControl(), win32con.WM_GETTEXT,
buf_size, buf)
str_val = buf[:nchars]
val = float(str_val)
if val < 0 or val > 100:
raise ValueError, "Value must be between 0 and 100"
self.value = val
# Folder IDs, and the "include_sub" option, if applicable.
class FolderIDProcessor(OptionControlProcessor):
def __init__(self, window, control_ids, option, option_include_sub = None):
self.button_id = control_ids[1]
if option_include_sub:
incl_sub_sect_name, incl_sub_option_name = \
option_include_sub.split(".")
self.option_include_sub = \
window.options.get_option(incl_sub_sect_name,
incl_sub_option_name)
else:
self.option_include_sub = None
OptionControlProcessor.__init__(self, window, control_ids, option)
def LoadOptionValue(self):
self.value = self.option.get()
if self.option_include_sub:
self.value_include_sub = self.option_include_sub.get()
def StoreOptionValue(self):
self.option.set(self.value)
if self.option_include_sub:
self.option_include_sub.set(self.value_include_sub)
def OnCommand(self, wparam, lparam):
mgr = self.window.manager
id = win32api.LOWORD(wparam)
if id == self.button_id:
is_multi = self.option.multiple_values_allowed()
if is_multi:
ids = self.value
else:
ids = [self.value]
from dialogs import FolderSelector
if self.option_include_sub:
cb_state = self.value_include_sub
else:
cb_state = None # don't show it.
d = FolderSelector.FolderSelector(mgr,
ids,
single_select=not is_multi,
checkbox_state=cb_state)
if d.DoModal() == win32con.IDOK:
ids, include_sub = d.GetSelectedIDs()
if is_multi:
self.value = ids
else:
self.value = ids[0]
if self.option_include_sub:
self.value_include_sub = include_sub
self.UpdateControl_FromValue()
def GetPopupHelpText(self, idFrom):
if idFrom == self.button_id:
return "Displays a list from which you can select folders."
return OptionControlProcessor.GetPopupHelpText(self, idFrom)
def UpdateControl_FromValue(self):
# Set the static to folder names
mgr = self.window.manager
if self.option.multiple_values_allowed():
ids = self.value
else:
ids = [self.value]
names = []
for eid in ids:
folder = mgr.message_store.GetFolder(eid)
if folder is None:
name = "<unknown folder>"
else:
name = folder.name
names.append(name)
win32gui.SetWindowText(self.GetControl(), "; ".join(names))
def UpdateValue_FromControl(self):
# We only update our self.value via the dialog, so
# no need to copy control value to self.value.
pass
More information about the Spambayes-checkins
mailing list