From: Kevin A. <ka...@us...> - 2004-05-01 06:13:42
|
Update of /cvsroot/pythoncard/PythonCard/components In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29518/components Modified Files: textfield.py Log Message: transition code to new style event bindings and dispatch this is essentially the same code as used by button.py _bindEvents and _dispatch Index: textfield.py =================================================================== RCS file: /cvsroot/pythoncard/PythonCard/components/textfield.py,v retrieving revision 1.23 retrieving revision 1.24 diff -C2 -d -r1.23 -r1.24 *** textfield.py 21 Apr 2004 03:45:00 -0000 1.23 --- textfield.py 1 May 2004 06:13:34 -0000 1.24 *************** *** 8,11 **** --- 8,35 ---- from PythonCard import event, widget + # KEA 2002-04-04 + # custom event posted after loseFocus when a field + # has been changed + wxEVT_CLOSE_FIELD = wx.NewEventType() + + ##def EVT_CLOSE_FIELD(win, id, func): + ## win.Connect(id, -1, wxEVT_CLOSE_FIELD, func) + # use the new PyEventBinder in 2.5.x + EVT_CLOSE_FIELD = wx.PyEventBinder(wxEVT_CLOSE_FIELD) + + class CloseFieldEvent(event.Event): + name = 'closeField' + binding = EVT_CLOSE_FIELD + id = wxEVT_CLOSE_FIELD + + + TextFieldEvents = ( + event.KeyPressEvent, + event.KeyDownEvent, + event.KeyUpEvent, + event.TextUpdateEvent, + CloseFieldEvent + ) + class TextFieldSpec(widget.WidgetSpec): def __init__( self ) : *************** *** 16,20 **** #event.TextEnterEvent, event.TextUpdateEvent, ! event.CloseFieldEvent ] attributes = { --- 40,44 ---- #event.TextEnterEvent, event.TextUpdateEvent, ! CloseFieldEvent ] attributes = { *************** *** 77,82 **** self.SetBackgroundColour(self.GetParent().GetBackgroundColour()) ! adapter = TextFieldEventBinding(self) ! adapter.bindEvents() def _getAlignment(self): --- 101,107 ---- self.SetBackgroundColour(self.GetParent().GetBackgroundColour()) ! #adapter = TextFieldEventBinding(self) ! #adapter.bindEvents() ! self._bindEvents() def _getAlignment(self): *************** *** 103,127 **** # capabilities or more - ## def _setText( self, aString ) : - ## """Sets the text value and marks the widget as not-modified. - ## aString may contain newline characters if the text widget is multi-line.""" - ## self.SetValue( aString ) - ## - ## def _getText( self ) : - ## """Gets the contents of the widget. Notice that for a multiline text - ## widget, the lines will be separated by (Unix-style) \\n characters, - ## even under Windows where they are separated by a \\r\\n sequence in - ## the native control.""" - ## return self.GetValue() - ## - ## def _getEditable( self ) : - ## """Returns whether the text widget is editable or read-only.""" - ## # KEA the test below doesn't seem to be working, so... - ## #return not self._delegate.GetWindowStyleFlag() & wxTE_READONLY - ## return self.IsEditable() - ## - ## def _setEditable( self, aBoolean ) : - ## """Makes the text widget editable or read-only.""" - ## self.SetEditable( aBoolean ) # KEA new methods to mirror wxPython wxTextCtrl capabilities --- 128,131 ---- *************** *** 224,239 **** def replaceSelection(self, aString, select=0): - """ - if wxPlatform == "__WXMSW__": - if select: - sel = self._delegate.GetSelection() - numNewlines = aString.count('\n') - self._delegate.WriteText(aString) - self._delegate.SetSelection( sel[0], sel[0] + len(aString) + numNewlines) - else: - self._delegate.WriteText(aString) - else: - # Linux - """ sel = self.GetSelection() self.Remove(sel[0], sel[1]) --- 228,231 ---- *************** *** 241,253 **** if select: self.SetSelection(sel[0], sel[0] + len(aString)) - """ - ins = self._delegate.GetInsertionPoint() - sel = self._delegate.GetSelection() - self._delegate.Remove( sel[0], sel[1] ) - #self._delegate.SetInsertionPoint( aFrom ) - self._delegate.WriteText( aString ) - self._delegate.SetSelection( ins, ins + len(aString)) - #self._delegate.SetInsertionPoint( i ) - """ # KEA support SaveFile? --- 233,236 ---- *************** *** 280,325 **** raise AttributeError, "border attribute is read-only" ! # KEA added 2001-11-13 ! # this will be replaced once 2.3.2 has GetStringSelection ! def getStringSelection(self): ! sel = self.getSelection() ! txt = self._getText() ! """ ! if wxPlatform == '__WXMSW__': ! numNewlines = txt.count('\n', 0, sel[0]) ! start = sel[0] - numNewlines ! # have to special-case a selection to the end of the line ! # this is in a try block to avoid an exception when we index past the end ! # of the string try: ! if txt[start + 1] == "\n": ! start = start + 1 except: pass ! #print numNewlines, start ! numNewlines = txt.count('\n', 0, sel[1]) ! end = sel[1] - numNewlines ! # have to special-case a selection to the end of the line ! # this is in a try block to avoid an exception when we index past the end ! # of the string try: ! if txt[end + 1] == "\n": ! end = end + 1 except: pass ! #print numNewlines, end ! selectedText = txt[start:end] ! #print selectedText else: ! """ ! selectedText = txt[sel[0]:sel[1]] ! return selectedText ! def getString(self, aFrom, aTo): ! return self.GetValue()[aFrom:aTo] - # mimic wxSTC method - ClearAll = wx.TextCtrl.Clear - alignment = property(_getAlignment, _setAlignment) border = property(_getBorder, _setBorder) --- 263,511 ---- raise AttributeError, "border attribute is read-only" ! getStringSelection = wx.TextCtrl.GetStringSelection ! ! def getString(self, aFrom, aTo): ! return self.GetValue()[aFrom:aTo] ! ! # mimic wxSTC method ! ClearAll = wx.TextCtrl.Clear ! ! def _bindEvents(self): ! # shouldn't components be subclasses of Scriptable? ! # components would have their own handlers but when ! # looking for a handler match it will search the parents ! # for now just grab handlers from the background ! background = self.GetParent().GetParent() ! ! # where should this check go? ! # should we just set a "global" in the app instance such as ! # self.stack.app.bindUnusedEvents ! # this kind of thing isn't going to work for Rowland's compound ! # components ! if background.stack.app._showDebugMenu: ! bindUnusedEvents = True ! else: ! bindUnusedEvents = False ! ! # helper variable to simplify test for whether to bind InsteadOfTypeEvents ! boundEvents = {} ! ! self.eventIdToHandler = {} ! self.wxEventIdMap = {} ! ! if 0: ! print "\nBINDING...", self.name ! ! #for eventClass in event.WIDGET_EVENTS + ButtonEvents: ! #for eventClass in ButtonEvents: ! for eventClass in event.WIDGET_EVENTS + TextFieldEvents: ! # need to figure out a way to avoid the need ! # for this id to class mapping which is used in _dispatch below ! self.wxEventIdMap[eventClass.id] = eventClass ! # command handler overrides normal mouseClick or select handler ! # so dispatch will automatically dispatch to the command handler ! # by looking up the handler this way ! # it also means that if there is a command association with this component ! # then the regular mouseClick or select handler will never be bound, just ignored ! if issubclass(eventClass, event.CommandTypeEvent) and self.command: ! handler = background.findHandler('on_' + self.command + '_command') ! if not handler: ! handler = background.findHandler('on_' + self.name + '_' + eventClass.name) ! else: ! handler = background.findHandler('on_' + self.name + '_' + eventClass.name) ! if not handler: ! handler = background.findHandler('on_' + eventClass.name) ! if handler or bindUnusedEvents: ! # only bind events that have an event handler ! # in this scenario unused events are never bound ! # which is more efficient, but the Message Watcher needs ! # to be changed ! # alternatively we can bind everything and then in _dispatch ! # if there isn't a match in eventIdToHandler then we know ! # the event isn't used and we can set used to False ! # the complication would be that we probably have to have to ! # always call Skip() which may or may not be a hassle with components ! ! # this doesn't bind command events ! # they would be of the form on_somename_command ! # or perhaps on_command but I don't think we would want ! # to support that ! # the event binding would be specific to a component ! # since on dispatch command overrides something like mouseClickEvent ! # but the name of the command is not related to the component ! # need to look at whether self.command has a value and then bind ! # with ButtonMouseClickEvent.binding if that isn't already bound ! # then in dispatch have to check again I think ! ! # need to avoid double binding ! # also binding shouldn't be order-specific ! # so how to avoid binding mouseDrag to _dispatch ! # if mouseMove is already bound or if binding mouseMove ! # not rebinding if mouseDrag is already bound ! # perhaps MouseDragEvent keeps a reference to MouseMoveEvent ! # and that is inserted into boundEvents, then we check boundEvents ! # prior to rebinding? ! if not boundEvents.get(eventClass.binding, None): ! self.Bind(eventClass.binding, self._dispatch) ! boundEvents[eventClass.binding] = eventClass.name ! if handler: ! if 0: ! print " binding", self.name, eventClass.name, handler._name, eventClass.id ! self.eventIdToHandler[eventClass.id] = handler ! ! # in order for closeField to work properly ! # both gainFocus and loseFocus have to be bound to _dispatch ! # regardless of whether they have handlers or not ! # everything else above is generic except the for statement ! # but that can be generalized once all components are bound ! # the same way ! for eventClass in [event.GainFocusEvent, event.LoseFocusEvent]: ! if not boundEvents.get(eventClass.binding, None): ! self.Bind(eventClass.binding, self._dispatch) ! boundEvents[eventClass.binding] = eventClass.name ! ! ! if 0: ! print "\n boundEvents:" ! for name in boundEvents.values(): ! print " ", name ! print "\n\n" ! print "\n self.eventIdToHandler:" ! for id in self.eventIdToHandler: ! print " ", id, self.eventIdToHandler[id]._function ! print "\n\n" ! boundEvents = None ! ! # this is pretty generic, but Button doesn't have any specific ! # handling that is required so I need to do the same kind of code ! # for TextField and see what the special handling for gainFocus, ! # loseFocus, closeField will look like ! # it probably won't need special handling for the key events ! def _dispatch(self, aWxEvent): ! eventType = aWxEvent.GetEventType() ! ! eventName = None ! ! if eventType == wx.wxEVT_TIMER: ! aWxEvent.interval = aWxEvent.GetInterval() ! # wxPython 2.5.1.5 workaround ! # for some reason wx.TimerEvent does not contain the event target ! # so we have to set it ourselves ! aWxEvent.target = aWxEvent.eventObject = self ! else: try: ! # all events should have GetEventObject() ! # except of course for wx.TimerEvent above ! # KEA 2004-04-25 ! # should we remove this redundant identifier? ! aWxEvent.target = aWxEvent.eventObject = self except: pass ! # Each of these could check the event class like ! # wxListEvent and wxTreeEvent above. try: ! # mouse and key events ! aWxEvent.position = tuple(aWxEvent.GetPosition()) ! aWxEvent.x = aWxEvent.GetX() ! aWxEvent.y = aWxEvent.GetY() ! aWxEvent.altDown = aWxEvent.AltDown() ! aWxEvent.controlDown = aWxEvent.ControlDown() ! aWxEvent.shiftDown = aWxEvent.ShiftDown() except: pass ! try: ! # key events ! aWxEvent.keyCode = aWxEvent.GetKeyCode() ! except: ! pass ! if issubclass(self.wxEventIdMap[eventType], event.CommandTypeEvent): ! # could be command, so need to report the name ! # for the handler if it exists ! if self.command: ! eventName = 'command' ! elif eventType == event.MouseMoveEvent.id: ! # check to see if this is a mouseDrag ! if aWxEvent.Dragging(): ! eventType = event.MouseDragEvent.id ! # don't need this if all event types have unique ids ! #eventName = event.MouseDragEvent.name ! ! # TextField specific stuff ! # the question is how we either call the generic stuff above ! # due to the try/except blocks this code would probably ! # work in the generic event handling but that would be unclean <wink> ! if eventType == wx.wxEVT_SET_FOCUS: ! try: ! aWxEvent.target.DiscardEdits() ! except: ! pass ! elif eventType == wx.wxEVT_KILL_FOCUS: ! try: ! # only wxTextCtrl and wxRightTextCtrl should have IsModified ! # so an exception will be thrown and the event won't be posted ! # for other components, but they shouldn't be binding to these ! # handlers anyway, so I'm just be overly defensive ! # same with DiscardEdits() above ! #modified = obj.IsModified() ! if aWxEvent.target.IsModified(): ! #closeFieldEvent = aWxEvent.Clone() ! #closeFieldEvent.SetEventType(event.wxEVT_CLOSE_FIELD) ! # should I be using wx.PyEvent() instead? ! closeFieldEvent = wx.WindowCreateEvent() ! closeFieldEvent.SetEventType(wxEVT_CLOSE_FIELD) ! closeFieldEvent.SetEventObject(aWxEvent.target) ! closeFieldEvent.SetId(aWxEvent.GetId()) ! closeFieldEvent.SetTimestamp(aWxEvent.GetTimestamp()) ! # this is what Robin suggested instead, see: ! # http://aspn.activestate.com/ASPN/Mail/Message/wxPython-users/1103427 ! #obj.GetParent().GetEventHandler().ProcessEvent(closeFieldEvent) ! # KEA 2004-04-30 ! # ProcessEvent will cause closeField to occur before loseFocus and ! # gainFocus messages, so should we do a wxCallAfter instead? ! # in the case of fields should closeField be an InsteadOfTypeEvent ! # and replace the loseFocus event? probably not since they mean ! # different things ! aWxEvent.target.GetEventHandler().ProcessEvent(closeFieldEvent) ! #wx.PostEvent(obj.GetParent(), evt) ! #print 'posted closeField' ! except: ! pass ! ! ! ! if not eventName: ! eventName = self.wxEventIdMap[eventType].name ! ! # it shouldn't be possible to be in _dispatch for an event ! # that wasn't bound above, but just in case... ! handler = self.eventIdToHandler.get(eventType, None) ! if handler: ! event.EventQueue()._impl.eventOccurred2(eventName, self.name, True) ! if 0: ! print "dispatching", handler._name ! # make a lowercase alias ! aWxEvent.skip = aWxEvent.Skip ! ! # the event handlers are part of the Background so ! # we have to have a reference to call the handler below ! background = self.GetParent().GetParent() ! ! # this is what is in event.py ! # aHandler.getFunction()( aOwner, self.getSource(), self ) ! handler.getFunction()(background, aWxEvent) ! ! # do we have to clean up this alias? ! aWxEvent.skip = None ! # how about this local reference to handler? ! handler = None ! background = None else: ! event.EventQueue()._impl.eventOccurred2(eventName, self.name, False) ! # hopefully this is all we need to do for "unused events" ! aWxEvent.Skip() ! # cleanup ! aWxEvent.target = aWxEvent.eventObject = None alignment = property(_getAlignment, _setAlignment) border = property(_getBorder, _setBorder) |