# 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 2003 # 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 verbose = 0 # set to 1 to see option values fetched and set. # 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.config.get_option(sect_name, option_name) else: self.option = None def GetPopupHelpText(self, idFrom): doc = " ".join(self.option.doc().split()) if self.option.default_value: doc += " (the default value is %s)" % self.option.default_value return doc # We override Init, and break it into 2 steps. def Init(self): self.UpdateControl_FromValue() def Done(self): self.UpdateValue_FromControl() return True def NotifyOptionChanged(self, option = None): if option is None: option = self.option self.window.OnOptionChanged(self, option) def SetOptionValue(self, value, option = None): if option is None: option = self.option if verbose: print "Setting option '%s' (%s) -> %s" % \ (option.display_name(), option.name, value) option.set(value) self.NotifyOptionChanged(option) def GetOptionValue(self, option = None): if option is None: option = self.option ret = option.get() if verbose: print "Got option '%s' (%s) -> %s" % \ (option.display_name(), option.name, ret) return ret # 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 the 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 __init__(self, window, control_ids, option, disable_when_false_ids=""): OptionControlProcessor.__init__(self, window, control_ids, option) self.disable_ids = [window.manager.dialog_parser.ids[id] for id in disable_when_false_ids.split()] def OnCommand(self, wparam, lparam): code = win32api.HIWORD(wparam) if code == win32con.BN_CLICKED: self.UpdateValue_FromControl() def UpdateEnabledStates(self, enabled): for other in self.disable_ids: win32gui.EnableWindow(self.GetControl(other), enabled) def UpdateControl_FromValue(self): value = self.GetOptionValue() win32gui.SendMessage(self.GetControl(), win32con.BM_SETCHECK, value) for other in self.other_ids: win32gui.SendMessage(self.GetControl(other), win32con.BM_SETCHECK, not value) self.UpdateEnabledStates(value) def UpdateValue_FromControl(self): check = win32gui.SendMessage(self.GetControl(), win32con.BM_GETCHECK) check = not not check # force bool! self.SetOptionValue(check) self.UpdateEnabledStates(check) class RadioButtonProcessor(OptionControlProcessor): def OnCommand(self, wparam, lparam): code = win32api.HIWORD(wparam) if code == win32con.BN_CLICKED: self.UpdateValue_FromControl() def UpdateControl_FromValue(self): value = self.GetOptionValue() i = 0 first = chwnd = self.GetControl() while chwnd: if i==value: win32gui.SendMessage(chwnd, win32con.BM_SETCHECK, 1) break chwnd = win32gui.GetNextDlgGroupItem(self.window.hwnd, chwnd, False) assert chwnd!=first, "Back where I started!" i += 1 else: assert 0, "Could not find control for value %s" % value def UpdateValue_FromControl(self): all_ids = [self.control_id] + self.other_ids chwnd = self.GetControl() i = 0 while chwnd: checked = win32gui.SendMessage(chwnd, win32con.BM_GETCHECK) if checked: self.SetOptionValue(i) break chwnd = win32gui.GetNextDlgGroupItem(self.window.hwnd, chwnd, False) i += 1 else: assert 0, "Couldn't find a checked button" # A "Combo" processor, that loads valid strings from the option. class ComboProcessor(OptionControlProcessor): def __init__(self, window, control_ids, option,text=None): OptionControlProcessor.__init__(self, window, control_ids, option) if text: temp = text.split(",") self.option_to_text = zip(self.option.valid_input(), temp) self.text_to_option = dict(zip(temp, self.option.valid_input())) else: self.option_to_text = zip(self.option.valid_input(),self.option.valid_input()) self.text_to_option = dict(self.option_to_text) def OnCommand(self, wparam, lparam): code = win32api.HIWORD(wparam) if code == win32con.CBN_SELCHANGE: self.UpdateValue_FromControl() def UpdateControl_FromValue(self): # First load the combo options. combo = self.GetControl() index = sel_index = 0 value = self.GetOptionValue() for opt,text in self.option_to_text: win32gui.SendMessage(combo, win32con.CB_ADDSTRING, 0, text) if value.startswith(opt): 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. text = buffer.tostring()[:-1] self.SetOptionValue(self.text_to_option[text]) class EditNumberProcessor(OptionControlProcessor): def __init__(self, window, control_ids, option, min_val = 0, max_val = 100, ticks = 100): self.slider_id = control_ids and control_ids[1] self.min_val = min_val self.max_val = max_val self.ticks = ticks 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) * self.max_val / self.ticks 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) # xxx - this wont be right if min <> 0 :( assert self.min_val == 0, "sue me" win32gui.SendMessage(slider, commctrl.TBM_SETRANGE, 0, MAKELONG(0, self.ticks)) # sigh - these values may not be right win32gui.SendMessage(slider, commctrl.TBM_SETLINESIZE, 0, 1) win32gui.SendMessage(slider, commctrl.TBM_SETPAGESIZE, 0, self.ticks/20) win32gui.SendMessage(slider, commctrl.TBM_SETTICFREQ, self.ticks/10, 0) def UpdateControl_FromValue(self): win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT, 0, str(self.GetOptionValue())) self.UpdateSlider_FromEdit() def UpdateSlider_FromEdit(self): slider = self.GetControl(self.slider_id) # done as the user is typing into the edit control, so we must not # complain here about invalid values as it is likely to only be # temporarily invalid until they finish. 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 = float(self.GetOptionValue()) # Convert it to our range. val *= float(self.ticks) / self.max_val val = int(val) 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 < self.min_val or val > self.max_val: raise ValueError, "Value must be between %d and %d" % (self.min_val, self.max_val) self.SetOptionValue(val) # Folder IDs, and the "include_sub" option, if applicable. class FolderIDProcessor(OptionControlProcessor): def __init__(self, window, control_ids, option, option_include_sub = None, use_fqn = False, name_joiner = "; "): self.button_id = control_ids[1] self.use_fqn = use_fqn self.name_joiner = name_joiner if option_include_sub: incl_sub_sect_name, incl_sub_option_name = \ option_include_sub.split(".") self.option_include_sub = \ window.config.get_option(incl_sub_sect_name, incl_sub_option_name) else: self.option_include_sub = None OptionControlProcessor.__init__(self, window, control_ids, option) def DoBrowse(self): mgr = self.window.manager is_multi = self.option.multiple_values_allowed() if is_multi: ids = self.GetOptionValue() else: ids = [self.GetOptionValue()] from dialogs import FolderSelector if self.option_include_sub: cb_state = self.option_include_sub.get() else: cb_state = None # don't show it. d = FolderSelector.FolderSelector(self.window.hwnd, 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.SetOptionValue(ids) else: self.SetOptionValue(ids[0]) if self.option_include_sub: self.SetOptionValue(include_sub, self.option_include_sub) self.UpdateControl_FromValue() return True return False def OnCommand(self, wparam, lparam): id = win32api.LOWORD(wparam) if id == self.button_id: self.DoBrowse() 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.GetOptionValue() else: ids = [self.GetOptionValue()] names = [] for eid in ids: if eid is not None: try: folder = mgr.message_store.GetFolder(eid) if self.use_fqn: name = folder.GetFQName() else: name = folder.name except mgr.message_store.MsgStoreException: name = "" names.append(name) win32gui.SetWindowText(self.GetControl(), self.name_joiner.join(names)) def UpdateValue_FromControl(self): pass