provides a single "unified" interface for masked controls. Those for
masked.TextCtrl, masked.ComboBox and masked.IpAddrCtrl are all documented
below; the others have their own demo pages and interface descriptions.
- (See end of following discussion for how to configure the wxMaskedCtrl()
+ (See end of following discussion for how to configure the wx.MaskedCtrl()
to select the above control types.)
the function for getting the start and end of the
current text selection. The reason for this is
that not all controls have the same function name for
- doing this; eg. wxTextCtrl uses .GetSelection(),
+ doing this; eg. wx.TextCtrl uses .GetSelection(),
whereas we had to write a .GetMark() function for
wxComboBox, because .GetSelection() for the control
gets the currently selected list item from the combo
from wx.tools.dbg import Logger
dbg = Logger()
-##dbg(enable=0)
+##dbg(enable=1)
## ---------- ---------- ---------- ---------- ---------- ---------- ----------
class MaskedEditMixin:
"""
This class allows us to abstract the masked edit functionality that could
- be associated with any text entry control. (eg. wxTextCtrl, wxComboBox, etc.)
+ be associated with any text entry control. (eg. wx.TextCtrl, wx.ComboBox, etc.)
"""
valid_ctrl_params = {
'mask': 'XXXXXXXXXXXXX', ## mask string for formatting this control
self._prevValue = newvalue # disallow undo of sign type
if self._autofit:
-## dbg('setting client size to:', self._CalcSize())
- size = self._CalcSize()
- self.SetSizeHints(size)
- self.SetClientSize(size)
+## dbg('calculated size:', self._CalcSize())
+ self.SetClientSize(self._CalcSize())
+ width = self.GetSize().width
+ height = self.GetBestSize().height
+## dbg('setting client size to:', (width, height))
+ self.SetBestFittingSize((width, height))
# Set value/type-specific formatting
self._applyFormatting()
self._SetInitialValue()
if self._autofit:
- size = self._CalcSize()
- self.SetSizeHints(size)
- self.SetClientSize(size)
+ # this is tricky, because, as Robin explains:
+ # "Basically there are two sizes to deal with, that are potentially
+ # different. The client size is the inside size and may, depending
+ # on platform, exclude the borders and such. The normal size is
+ # the outside size that does include the borders. What you are
+ # calculating (in _CalcSize) is the client size, but the sizers
+ # deal with the full size and so that is the minimum size that
+ # we need to set with SetBestFittingSize. The root of the problem is
+ # that in _calcSize the current client size height is returned,
+ # instead of a height based on the current font. So I suggest using
+ # _calcSize to just get the width, and then use GetBestSize to
+ # get the height."
+ self.SetClientSize(self._CalcSize())
+ width = self.GetSize().width
+ height = self.GetBestSize().height
+ self.SetBestFittingSize((width, height))
+
# Set value/type-specific formatting
self._applyFormatting()
sizing_text += 'M'
#### dbg('len(sizing_text):', len(sizing_text), 'sizing_text: "%s"' % sizing_text)
w, h = self.GetTextExtent(sizing_text)
- size = (w+4, self.GetClientSize().height)
+ size = (w+4, self.GetSize().height)
#### dbg('size:', size, indent=0)
return size
## dbg(indent=0)
return bValid
- ##! WS: For some inexplicable reason, every wxTextCtrl.SetValue
- ## call is generating two (2) EVT_TEXT events.
+ ##! WS: For some inexplicable reason, every wx.TextCtrl.SetValue
+ ## call is generating two (2) EVT_TEXT events. On certain platforms,
+ ## (eg. linux/GTK) the 1st is an empty string value.
## This is the only mechanism I can find to mask this problem:
- if newvalue == self._curValue:
+ if newvalue == self._curValue or len(newvalue) == 0:
## dbg('ignoring bogus text change event', indent=0)
pass
else:
-## dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue))
+## dbg('curvalue: "%s", newvalue: "%s", len(newvalue): %d' % (self._curValue, newvalue, len(newvalue)))
if self._Change():
if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1:
## dbg('clearing self._isNeg')
if newfield != field and newfield._selectOnFieldEntry:
## dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
wx.CallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
+ else:
+ wx.CallAfter(self._SetSelection, newpos, new_select_to)
keep_processing = False
elif keep_processing:
def _OnCtrl_A(self,event=None):
""" Handles ctrl-a keypress in control. Should return False to skip other processing. """
end = self._goEnd(getPosOnly=True)
- if not event or event.ShiftDown():
+ if not event or (isinstance(event, wx.KeyEvent) and event.ShiftDown()):
wx.CallAfter(self._SetInsertionPoint, 0)
wx.CallAfter(self._SetSelection, 0, self._masklength)
else:
return False
- def _OnErase(self, event=None):
+ def _OnErase(self, event=None, just_return_value=False):
""" Handles backspace and delete keypress in control. Should return False to skip other processing."""
## dbg("MaskedEditMixin::_OnErase", indent=1)
sel_start, sel_to = self._GetSelection() ## check for a range of selected text
## dbg(indent=0)
return False
+ if just_return_value:
+## dbg(indent=0)
+ return newstr
+
+ # else...
## dbg('setting value (later) to', newstr)
wx.CallAfter(self._SetValue, newstr)
## dbg('setting insertion point (later) to', pos)
def _OnReturn(self, event):
"""
- Changes the event to look like a tab event, so we can then call
- event.Skip() on it, and have the parent form "do the right thing."
+ Swallows the return, issues a Navigate event instead, since
+ masked controls are "single line" by defn.
"""
## dbg('MaskedEditMixin::OnReturn')
- event.m_keyCode = wx.WXK_TAB
- event.Skip()
+ self.Navigate(True)
+ return False
def _OnHome(self,event):
"""
Primarily handles TAB events, but can be used for any key that
designer wants to change fields within a masked edit control.
- NOTE: at the moment, although coded to handle shift-TAB and
- control-shift-TAB, these events are not sent to the controls
- by the framework.
"""
## dbg('MaskedEditMixin::_OnChangeField', indent = 1)
# determine end of current field:
self._AdjustField(pos)
if event.GetKeyCode() == wx.WXK_TAB:
## dbg('tab to next ctrl')
- event.Skip()
+ # As of 2.5.2, you don't call event.Skip() to do
+ # this, but instead force explicit navigation, if
+ # wx.TE_PROCESS_TAB is used (like in the masked edits)
+ self.Navigate(True)
#else: do nothing
## dbg(indent=0)
return False
self._AdjustField(pos)
if event.GetKeyCode() == wx.WXK_TAB:
## dbg('tab to previous ctrl')
- event.Skip()
+ # As of 2.5.2, you don't call event.Skip() to do
+ # this, but instead force explicit navigation, if
+ # wx.TE_PROCESS_TAB is used (like in the masked edits)
+ self.Navigate(False)
else:
## dbg('position at beginning')
wx.CallAfter(self._SetInsertionPoint, field_start)
self._AdjustField(pos)
if event.GetKeyCode() == wx.WXK_TAB:
## dbg('tab to next ctrl')
- event.Skip()
+ # As of 2.5.2, you don't call event.Skip() to do
+ # this, but instead force explicit navigation, if
+ # wx.TE_PROCESS_TAB is used (like in the masked edits)
+ self.Navigate(True)
else:
## dbg('position at end')
wx.CallAfter(self._SetInsertionPoint, field_end)
self._AdjustField(pos)
if event.GetKeyCode() == wx.WXK_TAB:
## dbg('tab to next ctrl')
- event.Skip()
+ # As of 2.5.2, you don't call event.Skip() to do
+ # this, but instead force explicit navigation, if
+ # wx.TE_PROCESS_TAB is used (like in the masked edits)
+ self.Navigate(True)
#else: do nothing
## dbg(indent=0)
return False
if fraction._selectOnFieldEntry:
## dbg('queuing selection after decimal point to:', (start, end))
wx.CallAfter(self._SetSelection, start, end)
+ else:
+ wx.CallAfter(self._SetSelection, start, start)
keep_processing = False
if self._isInt: ## handle integer value, truncate from current position
if newstr.find(')') != -1:
newpos -= 1 # (don't move past right paren)
wx.CallAfter(self._SetInsertionPoint, newpos)
+ wx.CallAfter(self._SetSelection, newpos, newpos)
keep_processing = False
## dbg(indent=0)
pos = pos+2
if newvalue != value:
+## dbg('old value: "%s"\nnew value: "%s"' % (value, newvalue))
self._SetValue(newvalue)
self._SetInsertionPoint(pos)
self._SetInsertionPoint(pos)
if pos < sel_to: # restore selection
self._SetSelection(pos, sel_to)
+ else:
+ self._SetSelection(pos, pos)
## dbg('adjusted pos:', pos, indent=0)
return pos
newtext = ""
newpos = pos
+ # if >= 2 chars selected in a right-insert field, do appropriate erase on field,
+ # then set selection to end, and do usual right insert.
+ if sel_start != sel_to and sel_to >= sel_start+2:
+ field = self._FindField(sel_start)
+ if( field._insertRight # if right-insert
+ and field._allowInsert # and allow insert at any point in field
+ and field == self._FindField(sel_to) ): # and selection all in same field
+ text = self._OnErase(just_return_value=True) # remove selection before insert
+## dbg('text after (left)erase: "%s"' % text)
+ pos = sel_start = sel_to
+
if pos != sel_start and sel_start == sel_to:
# adjustpos must have moved the position; make selection match:
sel_start = sel_to = pos
## dbg('field._insertRight?', field._insertRight)
+## dbg('field._allowInsert?', field._allowInsert)
+## dbg('sel_start, end', sel_start, end)
+ if sel_start < end:
+## dbg('text[sel_start] != field._fillChar?', text[sel_start] != field._fillChar)
+ pass
+
if( field._insertRight # field allows right insert
and ((sel_start, sel_to) == field._extent # and whole field selected
or (sel_start == sel_to # or nothing selected
if match_index is not None and partial_match:
matched_str = newtext
newtext = self._ctrl_constraints._choices[match_index]
- new_select_to = self._ctrl_constraints._extent[1]
+ edit_end = self._ctrl_constraints._extent[1]
+ new_select_to = min(edit_end, len(newvalue.rstrip()))
match_field = self._ctrl_constraints
if self._ctrl_constraints._insertRight:
# adjust position to just after partial match in control:
"""
This event handler is currently necessary to work around new default
behavior as of wxPython2.3.3;
- The TAB key auto selects the entire contents of the wxTextCtrl *after*
+ The TAB key auto selects the entire contents of the wx.TextCtrl *after*
the EVT_SET_FOCUS event occurs; therefore we can't query/adjust the selection
*here*, because it hasn't happened yet. So to prevent this behavior, and
preserve the correct selection when the focus event is not due to tab,
we need to pull the following trick:
"""
## dbg('MaskedEditMixin::_OnFocus')
+ if self.IsBeingDeleted() or self.GetParent().IsBeingDeleted():
+ return
wx.CallAfter(self._fixSelection)
event.Skip()
self.Refresh()
if self._isFloat and groupcharpos > self._decimalpos:
# 1st one found on right-hand side is past decimal point
## dbg('groupchar in fraction; illegal')
- valid = False
+ return False
elif self._isFloat:
integer = value[:self._decimalpos].strip()
else:
The trouble is that, a priori, there's no explicit notification of
why the focus event we received. However, the whole reason we need to
- do this is because the default behavior on TAB traveral in a wxTextCtrl is
+ do this is because the default behavior on TAB traveral in a wx.TextCtrl is
now to select the entire contents of the window, something we don't want.
So we can *now* test the selection range, and if it's "the whole text"
we can assume the cause, change the insertion point to the start of
the control, and deselect.
"""
## dbg('MaskedEditMixin::_fixSelection', indent=1)
- if not self._mask or not self._IsEditable():
+ # can get here if called with wx.CallAfter after underlying
+ # control has been destroyed on close, but after focus
+ # events
+ if not self or not self._mask or not self._IsEditable():
## dbg(indent=0)
return
## dbg('current value: "%s"' % value)
sel_start, sel_to = self._GetSelection() ## check for a range of selected text
## dbg('selected text: "%s"' % value[sel_start:sel_to].strip())
- do = wxTextDataObject()
+ do = wx.TextDataObject()
do.SetText(value[sel_start:sel_to].strip())
- wxTheClipboard.Open()
- wxTheClipboard.SetData(do)
- wxTheClipboard.Close()
+ wx.TheClipboard.Open()
+ wx.TheClipboard.SetData(do)
+ wx.TheClipboard.Close()
if sel_to - sel_start != 0:
self._OnErase()
#
## def _Copy( self ):
## """
-## Override the wxTextCtrl's .Copy function, with our own
+## Override the wx.TextCtrl's .Copy function, with our own
## that does validation. Need to strip trailing spaces.
## """
## sel_start, sel_to = self._GetSelection()
## select_len = sel_to - sel_start
-## textval = wxTextCtrl._GetValue(self)
+## textval = wx.TextCtrl._GetValue(self)
##
-## do = wxTextDataObject()
+## do = wx.TextDataObject()
## do.SetText(textval[sel_start:sel_to].strip())
-## wxTheClipboard.Open()
-## wxTheClipboard.SetData(do)
-## wxTheClipboard.Close()
+## wx.TheClipboard.Open()
+## wx.TheClipboard.SetData(do)
+## wx.TheClipboard.Close()
def _getClipboardContents( self ):
""" Subroutine for getting the current contents of the clipboard.
"""
- do = wxTextDataObject()
- wxTheClipboard.Open()
- success = wxTheClipboard.GetData(do)
- wxTheClipboard.Close()
+ do = wx.TextDataObject()
+ wx.TheClipboard.Open()
+ success = wx.TheClipboard.GetData(do)
+ wx.TheClipboard.Close()
if not success:
return None
field = self._FindField(sel_start)
edit_start, edit_end = field._extent
new_pos = None
- if field._allowInsert and sel_to <= edit_end and sel_start + len(paste_text) < edit_end:
+ if field._allowInsert and sel_to <= edit_end and (sel_start + len(paste_text) < edit_end or field._insertRight):
+ if field._insertRight:
+ # want to paste to the left; see if it will fit:
+ left_text = self._GetValue()[edit_start:sel_start].lstrip()
+## dbg('len(left_text):', len(left_text))
+## dbg('len(paste_text):', len(paste_text))
+## dbg('sel_start - (len(left_text) + len(paste_text)) >= edit_start?', sel_start - (len(left_text) + len(paste_text)) >= edit_start)
+ if sel_start - (len(left_text) - (sel_to - sel_start) + len(paste_text)) >= edit_start:
+ # will fit! create effective paste text, and move cursor back to do so:
+ paste_text = left_text + paste_text
+ sel_start -= len(left_text)
+ paste_text = paste_text.rjust(sel_to - sel_start)
+## dbg('modified paste_text to be: "%s"' % paste_text)
+## dbg('modified selection to:', (sel_start, sel_to))
+ else:
+## dbg("won't fit left;", 'paste text remains: "%s"' % paste_text)
+ pass
+ else:
+ paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip()
+## dbg("allow insert, but not insert right;", 'paste text set to: "%s"' % paste_text)
+
+
new_pos = sel_start + len(paste_text) # store for subsequent positioning
- paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip()
## dbg('paste within insertable field; adjusted paste_text: "%s"' % paste_text, 'end:', edit_end)
- sel_to = sel_start + len(paste_text)
+## dbg('expanded selection to:', (sel_start, sel_to))
# Another special case: paste won't fit, but it's a right-insert field where entire
# non-empty value is selected, and there's room if the selection is expanded leftward:
if not wx.Validator_IsSilent():
wx.Bell()
## dbg(indent=0)
- return False
+ return None, -1
# else...
text = self._eraseSelection()
wx.CallAfter(self._SetInsertionPoint, new_pos)
else:
## dbg(indent=0)
- return new_text
+ return new_text, replace_to
elif just_return_value:
## dbg(indent=0)
- return self._GetValue()
+ return self._GetValue(), sel_to
## dbg(indent=0)
- def _Undo(self):
+ def _Undo(self, value=None, prev=None, just_return_results=False):
""" Provides an Undo() method in base controls. """
## dbg("MaskedEditMixin::_Undo", indent=1)
- value = self._GetValue()
- prev = self._prevValue
+ if value is None:
+ value = self._GetValue()
+ if prev is None:
+ prev = self._prevValue
## dbg('current value: "%s"' % value)
## dbg('previous value: "%s"' % prev)
if prev is None:
for next_op in range(len(code_5tuples)-1, -1, -1):
op, i1, i2, j1, j2 = code_5tuples[next_op]
## dbg('value[i1:i2]: "%s"' % value[i1:i2], 'template[i1:i2] "%s"' % self._template[i1:i2])
+ field = self._FindField(i2)
if op == 'insert' and prev[j1:j2] != self._template[j1:j2]:
## dbg('insert found: selection =>', (j1, j2))
sel_start = j1
diff_found = True
break
elif op == 'delete' and value[i1:i2] != self._template[i1:i2]:
- field = self._FindField(i2)
edit_start, edit_end = field._extent
- if field._insertRight and i2 == edit_end:
+ if field._insertRight and (field._allowInsert or i2 == edit_end):
sel_start = i2
sel_to = i2
else:
diff_found = True
break
elif op == 'replace':
-## dbg('replace found: selection =>', (j1, j2))
- sel_start = j1
- sel_to = j2
+ if not prev[i1:i2].strip() and field._insertRight:
+ sel_start = sel_to = j2
+ else:
+ sel_start = j1
+ sel_to = j2
+## dbg('replace found: selection =>', (sel_start, sel_to))
diff_found = True
break
if diff_found:
# now go forwards, looking for earlier changes:
+## dbg('searching forward...')
for next_op in range(len(code_5tuples)):
op, i1, i2, j1, j2 = code_5tuples[next_op]
field = self._FindField(i1)
if op == 'equal':
continue
elif op == 'replace':
-## dbg('setting sel_start to', i1)
- sel_start = i1
+ if field._insertRight:
+ # if replace with spaces in an insert-right control, ignore "forward" replace
+ if not prev[i1:i2].strip():
+ continue
+ elif j1 < i1:
+## dbg('setting sel_start to', j1)
+ sel_start = j1
+ else:
+## dbg('setting sel_start to', i1)
+ sel_start = i1
+ else:
+## dbg('setting sel_start to', i1)
+ sel_start = i1
+## dbg('saw replace; breaking')
break
elif op == 'insert' and not value[i1:i2]:
## dbg('forward %s found' % op)
## dbg('setting sel_start to inserted space:', j1)
sel_start = j1
break
- elif op == 'delete' and field._insertRight and not value[i1:i2].lstrip():
- continue
+ elif op == 'delete':
+## dbg('delete; field._insertRight?', field._insertRight, 'value[%d:%d].lstrip: "%s"' % (i1,i2,value[i1:i2].lstrip()))
+ if field._insertRight:
+ if value[i1:i2].lstrip():
+## dbg('setting sel_start to ', j1)
+ sel_start = j1
+## dbg('breaking loop')
+ break
+ else:
+ continue
+ else:
+## dbg('saw delete; breaking')
+ break
else:
+## dbg('unknown code!')
# we've got what we need
break
prev_sel_start, prev_sel_to = self._prevSelection
field = self._FindField(sel_start)
-
- if self._signOk and (self._prevValue[sel_start] in ('-', '(', ')')
- or self._curValue[sel_start] in ('-', '(', ')')):
+ if( self._signOk
+ and sel_start < self._masklength
+ and (prev[sel_start] in ('-', '(', ')')
+ or value[sel_start] in ('-', '(', ')')) ):
# change of sign; leave cursor alone...
+## dbg("prev[sel_start] in ('-', '(', ')')?", prev[sel_start] in ('-', '(', ')'))
+## dbg("value[sel_start] in ('-', '(', ')')?", value[sel_start] in ('-', '(', ')'))
+## dbg('setting selection to previous one')
sel_start, sel_to = self._prevSelection
- elif field._groupdigits and (self._curValue[sel_start:sel_to] == field._groupChar
- or self._prevValue[sel_start:sel_to] == field._groupChar):
+ elif field._groupdigits and (value[sel_start:sel_to] == field._groupChar
+ or prev[sel_start:sel_to] == field._groupChar):
# do not highlight grouping changes
+## dbg('value[sel_start:sel_to] == field._groupChar?', value[sel_start:sel_to] == field._groupChar)
+## dbg('prev[sel_start:sel_to] == field._groupChar?', prev[sel_start:sel_to] == field._groupChar)
+## dbg('setting selection to previous one')
sel_start, sel_to = self._prevSelection
else:
if prev_select_len >= calc_select_len:
# old selection was bigger; trust it:
- sel_start, sel_to = self._prevSelection
+## dbg('prev_select_len >= calc_select_len?', prev_select_len >= calc_select_len)
+ if not field._insertRight:
+## dbg('setting selection to previous one')
+ sel_start, sel_to = self._prevSelection
+ else:
+ sel_to = self._prevSelection[1]
+## dbg('setting selection to', (sel_start, sel_to))
elif( sel_to > prev_sel_to # calculated select past last selection
and prev_sel_to < len(self._template) # and prev_sel_to not at end of control
test_sel_start, test_sel_to = prev_sel_start, prev_sel_to
## dbg('test selection:', (test_sel_start, test_sel_to))
-## dbg('calc change: "%s"' % self._prevValue[sel_start:sel_to])
-## dbg('test change: "%s"' % self._prevValue[test_sel_start:test_sel_to])
+## dbg('calc change: "%s"' % prev[sel_start:sel_to])
+## dbg('test change: "%s"' % prev[test_sel_start:test_sel_to])
# if calculated selection spans characters, and same characters
# "before" the previous insertion point are present there as well,
# select the ones related to the last known selection instead.
if( sel_start != sel_to
and test_sel_to < len(self._template)
- and self._prevValue[test_sel_start:test_sel_to] == self._prevValue[sel_start:sel_to] ):
+ and prev[test_sel_start:test_sel_to] == prev[sel_start:sel_to] ):
sel_start, sel_to = test_sel_start, test_sel_to
+ # finally, make sure that the old and new values are
+ # different where we say they're different:
+ while( sel_to - 1 > 0
+ and sel_to > sel_start
+ and value[sel_to-1:] == prev[sel_to-1:]):
+ sel_to -= 1
+ while( sel_start + 1 < self._masklength
+ and sel_start < sel_to
+ and value[:sel_start+1] == prev[:sel_start+1]):
+ sel_start += 1
+
## dbg('sel_start, sel_to:', sel_start, sel_to)
-## dbg('previous value: "%s"' % self._prevValue)
- self._SetValue(self._prevValue)
+## dbg('previous value: "%s"' % prev)
+## dbg(indent=0)
+ if just_return_results:
+ return prev, (sel_start, sel_to)
+ # else...
+ self._SetValue(prev)
self._SetInsertionPoint(sel_start)
self._SetSelection(sel_start, sel_to)
+
else:
## dbg('no difference between previous value')
- pass
-## dbg(indent=0)
+## dbg(indent=0)
+ if just_return_results:
+ return prev, self._GetSelection()
def _OnClear(self, event):
def _OnContextMenu(self, event):
## dbg('MaskedEditMixin::OnContextMenu()', indent=1)
- menu = wxMenu()
- menu.Append(wxID_UNDO, "Undo", "")
+ menu = wx.Menu()
+ menu.Append(wx.ID_UNDO, "Undo", "")
menu.AppendSeparator()
- menu.Append(wxID_CUT, "Cut", "")
- menu.Append(wxID_COPY, "Copy", "")
- menu.Append(wxID_PASTE, "Paste", "")
- menu.Append(wxID_CLEAR, "Delete", "")
+ menu.Append(wx.ID_CUT, "Cut", "")
+ menu.Append(wx.ID_COPY, "Copy", "")
+ menu.Append(wx.ID_PASTE, "Paste", "")
+ menu.Append(wx.ID_CLEAR, "Delete", "")
menu.AppendSeparator()
- menu.Append(wxID_SELECTALL, "Select All", "")
+ menu.Append(wx.ID_SELECTALL, "Select All", "")
- EVT_MENU(menu, wxID_UNDO, self._OnCtrl_Z)
- EVT_MENU(menu, wxID_CUT, self._OnCtrl_X)
- EVT_MENU(menu, wxID_COPY, self._OnCtrl_C)
- EVT_MENU(menu, wxID_PASTE, self._OnCtrl_V)
- EVT_MENU(menu, wxID_CLEAR, self._OnClear)
- EVT_MENU(menu, wxID_SELECTALL, self._OnCtrl_A)
+ wx.EVT_MENU(menu, wx.ID_UNDO, self._OnCtrl_Z)
+ wx.EVT_MENU(menu, wx.ID_CUT, self._OnCtrl_X)
+ wx.EVT_MENU(menu, wx.ID_COPY, self._OnCtrl_C)
+ wx.EVT_MENU(menu, wx.ID_PASTE, self._OnCtrl_V)
+ wx.EVT_MENU(menu, wx.ID_CLEAR, self._OnClear)
+ wx.EVT_MENU(menu, wx.ID_SELECTALL, self._OnCtrl_A)
# ## WSS: The base control apparently handles
- # enable/disable of wID_CUT, wxID_COPY, wxID_PASTE
- # and wxID_CLEAR menu items even if the menu is one
+ # enable/disable of wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE
+ # and wx.ID_CLEAR menu items even if the menu is one
# we created. However, it doesn't do undo properly,
# so we're keeping track of previous values ourselves.
# Therefore, we have to override the default update for
# that item on the menu:
- EVT_UPDATE_UI(self, wxID_UNDO, self._UndoUpdateUI)
+ wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self._UndoUpdateUI)
self._contextMenu = menu
self.PopupMenu(menu, event.GetPosition())
def _UndoUpdateUI(self, event):
if self._prevValue is None or self._prevValue == self._curValue:
- self._contextMenu.Enable(wxID_UNDO, False)
+ self._contextMenu.Enable(wx.ID_UNDO, False)
else:
- self._contextMenu.Enable(wxID_UNDO, True)
+ self._contextMenu.Enable(wx.ID_UNDO, True)
def _OnCtrlParametersChanged(self):
## CHANGELOG:
## ====================
+## Version 1.7
+## 1. Fixed intra-right-insert-field erase, such that it doesn't leave a hole, but instead
+## shifts the text to the left accordingly.
+## 2. Fixed _SetValue() to place cursor after last character inserted, rather than end of
+## mask.
+## 3. Fixed some incorrect undo behavior for right-insert fields, and allowed derived classes
+## (eg. numctrl) to pass modified values for undo processing (to handle/ignore grouping
+## chars properly.)
+## 4. Fixed autoselect behavior to work similarly to (2) above, so that combobox
+## selection will only select the non-empty text, as per request.
+## 5. Fixed tabbing to work with 2.5.2 semantics.
+## 6. Fixed size calculation to handle changing fonts
+##
## Version 1.6
## 1. Reorganized masked controls into separate package, renamed things accordingly
## 2. Split actual controls out of this file into their own files.
## non-british spellings still supported for backward-compatibility.
## 20. Added '&' mask specification character for punctuation only (no letters
## or digits).
-## 21. Added (in a separate file) wxMaskedCtrl() factory function to provide
+## 21. Added (in a separate file) wx.MaskedCtrl() factory function to provide
## unified interface to the masked edit subclasses.
##
##
## 2. Fixed EUDATE* autoformats, fixed IsDateType mask list, and added ability to
## use 3-char months for dates, and EUDATETIME, and EUDATEMILTIME autoformats.
## 3. Made all date autoformats automatically pick implied "datestyle".
-## 4. Added IsModified override, since base wxTextCtrl never reports modified if
+## 4. Added IsModified override, since base wx.TextCtrl never reports modified if
## .SetValue used to change the value, which is what the masked edit controls
## use internally.
## 5. Fixed bug in date position adjustment on 2 to 4 digit date conversion when