Joey Kinsella
2010-Jan-11 20:21 UTC
[fxruby-users] Help with ruby segfault, initiated from FX::FXWindow::onUpdate()
Hello all, I am currently working on a custom widget that extends an FXPacker. This widget is similar to an FXComboBox, so it utilizes the FXPopup widget. Everything works fine as far as my widget itself goes. (FXFlexBox) However if I add the FXFlexBox widget to a window that is not the main window of an application (ie: closing this window, will not terminate the application.) I get a Segmentation Fault from ruby. My theory is that my widget is still attempting to do updates even though the parent object was destroyed. I''ve attempted to link to SEL_DESTROY/overload the destroy() method, to ensure that the FXPopup, FXList, FXTextField, and FXMenuButton that I am utilizing gets destroyed. However I have been unsuccessful. And even more I have found that even if I call theFlexBox.destroy() manually I get the same results... Does anyone know if I am over looking something in the Message system? Is there a way around this? I''ve attached the code below, Please help.. Thanks, Joey ---Begin Code--- ############################################################## ## File: flexbox.rb ## Author: Joey Kinsella ## Date: 2010-01-08 ## ## Description: A Flexbox widget for fxruby. ## TODO: Add kwargs support. (This is easy, I''m just lazy...) ## TODO: Add support for FXFlexBox options... ## ## Options should probably include something like the following: ## FLEXBOX_CASE_SENSITIVE ## -- Case sensitive searching... ## ## FLEXBOX_AUTO_ADD_(BEFORE|AFTER|FIRST|LAST) ## -- Auto add to list when enter is hit. ## ## FLEXBOX_AUTO_DEFAULT ## -- Return the list to it''s original state after a selection. ## -- This is how FXFlexBox currently works. # ## XXX: Should I add icon support? ## XXX: getFont()/setFont() ? ## XXX: other `standard'' list methods? ############################################################## require ''fox16'' module Fox # An FXFlexBox is essentially an FXComboBox, that adds a `popup'' search # feature. class FXFlexBox < FXPacker include Responder FLEXBOX_NORMAL = 0 # :nodoc: # Return an initialized FXFlexBox instance, with room to display +cols+ # columns of text. # # == Parameters: # # +p+:: the parent widget for this flex-box [FXComposite] # +cols+:: number of columns [Integer] # +target+:: message target [FXObject] # +selector+:: message identifier [Integer] # +opts+:: the options [Integer] # +x+:: initial x-position [Integer] # +y+:: initial y-position [Integer] # +width+:: initial width [Integer] # +height+:: initial height [Integer] # +padLeft+:: left-side padding, in pixels [Integer] # +padRight+:: right-side padding, in pixels [Integer] # +padTop+:: top-side padding, in pixels [Integer] # +padBottom+:: bottom-size padding, in pixels [Integer] def initialize(p, cols, target=nil, selector=0, opts=FLEXBOX_NORMAL, x=0, y=0, width=0, height=0, padLeft=DEFAULT_PAD, padRight=DEFAULT_PAD, padTop=DEFAULT_PAD, padBottom=DEFAULT_PAD) # :yields: theFlexBox @parent = p @columns = cols @target = target @selector = selector @internal_list = [] @real_query = "" if(block_given?) flex = FXFlexBox.new(p,cols,target,selector,opts,x,y,width,height, padLeft,padRight,padTop,padBottom) yield(flex) return (flex) end super(p, opts, x, y, width, height, 0, 0, 0, 0, 0, 0) identifier :ID_EDITBOX, :ID_LISTBOX @editbox = FXTextField.new(self, cols, self, ID_EDITBOX) @pane = FXPopup.new(self, FRAME_LINE) @listbox = FXList.new(@pane, self, ID_LISTBOX, :opts => LIST_BROWSESELECT | LIST_AUTOSELECT | LAYOUT_FILL | SCROLLERS_TRACK | HSCROLLING_OFF) @button = FXMenuButton.new(self, nil, nil, @pane, FRAME_RAISED | FRAME_THICK | MENUBUTTON_DOWN | MENUBUTTON_ATTACH_RIGHT, 0, 0, 0, 0, 0, 0, 0, 0) FXMAPFUNC(SEL_COMMAND, ID_LISTBOX, ''onSelectEntry'') FXMAPFUNC(SEL_KEYPRESS, ID_EDITBOX, ''onKeyPress'') FXMAPFUNC(SEL_FOCUSOUT, ID_EDITBOX, ''onLostFocus'') FXMAPFUNC(SEL_DESTROY, 0, ''onDestroy'') FXMAPFUNC(SEL_CLOSE, 0, ''onDestroy'') FXMAPFUNC(SEL_UPDATE, 0, ''onDestroy'') end # initialize # this is for setting the layout properly. def layout() # :nodoc: border = self.borderWidth itemHeight = self.height - (border << 1) buttonWidth = @button.getDefaultWidth() textWidth = self.width - buttonWidth - (border << 1) @editbox.position(border, border, textWidth, itemHeight) @button.position(border + textWidth, border, buttonWidth, itemHeight) @pane.resize(self.width, @pane.getDefaultHeight()) end # layout # Append a new item to the list with the specified text and user data. # # == Paramters: # # +text+:: the text to display on this list # +data+:: the data associated with this list item. def appendItem(text, data=nil) item = { :text => text, :data => data } # This could be a bug, what if we want to add two of the same values? unless(@internal_list.include?(item)) @internal_list << item end @listbox.appendItem(text, nil, data) end # appendItem # Remove all items from the list. def clearItems() @internal_list = [] @listbox.clearItems @editbox.text = "" end # clearItems # returns the number of visible items in the list. def numVisible() return (@listbox.numVisible) end # numVisible # sets the number of visible items in the list to +n+ def numVisible=(n) @listbox.numVisible = n end # numVisible # sets the text in the text field to +text+ def setText(text) @editbox.text = text end # setText # returns the text in the textfield. def getText() return (@editbox.text) end # calls a block once for each item in the list, passing a reference to that # item as a parameter. def each(&blk) # :yields: aListItem @listbox.each(blk) end # each # Return the item at given +index+; returns a reference to an FXListItem # instance. Raises IndexError if index is out of bounds. def getItem(index) return @listbox.getItem(index) end # getItem # Return item user data; this is equivalent to: # # +getItem(index).data+ # # Raises IndexError if index is out of bounds. def getItemData(index) return getItem(index).data end # getItemData # Return item text; this is equivalent to: # # +getItem(index).text+ # # Raises IndexError if index is out of bounds. def getItemText(index) return getItem(index).text end # getItemText # Insert a new (possibly subclassed) item at the given +index+, e.g. # # flex.insertItem(1, FXListItem.new("My Searchable")) # # Raises IndexError if +index+ is out of bounds. Returns either integer # +index+ of the inserted item or -1 if the item already exists. def insertItem(index, item) temp = { :text => item.text, :data => item.data } unless(@internal_list.include?(temp)) @internal_list << temp return @listbox.insertItem(index, item) end return -1 end # insertItem # Remove item at index from list. Raises IndexError if index is out of # bounds. def removeItem(index) temp = getItem(index) item = { :text => temp.text, :data => temp.data } @listbox.removeItem(index) @internal_list.delete(item) end # removeItem # restore the the FXListBox to the internal list state. def restoreList() # really, we just need to call queryList() with no parameters... return queryList() end # destroy the widget. def destroy() # :nodoc: puts "DESTROYED!!!!" @pane.destroy() @editbox.destroy() @button.destroy() @listbox.destroy() super() end private # handler for when an entry gets selected. def onSelectEntry(sender, sel, index) item = nil begin item = @listbox.getItem(index) @editbox.text = item.text rescue IndexError return false end if(@target && @target.respond_to?(:handle)) #@target.handle(self, FXSEL(SEL_COMMAND, @selector), @editbox.text) @target.handle(self, FXSEL(SEL_COMMAND, @selector), item.data) end # make sure the popup disappears.... @button.handle(self, FXSEL(SEL_COMMAND, ID_UNPOST), nil) return false end # handler for when a user is typing in the box. def onKeyPress(sender, sel, event) text = sender.text char = event.text # is the key press something other than ASCII input? if((event.code & 0xf000) != 0) case event.code when KEY_BackSpace return queryList(text.slice(0, text.length - 1)) when KEY_Return @button.handle(self, FXSEL(SEL_COMMAND, ID_UNPOST), nil) when KEY_KP_Enter @button.handle(self, FXSEL(SEL_COMMAND, ID_UNPOST), nil) when KEY_Down return scrollListItem(KEY_Down) when KEY_KP_Down return scrollListItem(KEY_Down) when KEY_Up return scrollListItem(KEY_Up) when KEY_KP_Up return scrollListItem(KEY_Up) else return queryList(text) end @real_query = "#{text}#{char}" return false end unless(@pane.shown?) @button.handle(self, FXSEL(SEL_COMMAND, ID_POST), nil) end @real_query = "#{text}#{char}" return queryList(@real_query) end # onKeyPress # this method gets called when the textfield loses focus. def onLostFocus(sender, sel, event) restoreList() end # onLostFocus # searches through the last, and only shows what matches +search+ # TODO: add hanlding for different options (ie: case sensitive, etc) def queryList(search = nil) @listbox.clearItems # is this wrong? @internal_list.each do |item| text = item[:text].downcase if(search.nil? || search.length == 0 || text =~ /^#{search.downcase}/) @listbox.appendItem(item[:text], nil, item[:data]) end end return false end # queryList # scrolls through the list based on +direction+ # +direction+ should be KEY_Down, or KEY_Up def scrollListItem(direction = KEY_Down) # heh, nothing to scroll through... if(@listbox.numItems == 0) return false end current = @listbox.currentItem max = @listbox.numItems if(max == 1) @listbox.setCurrentItem(0) @editbox.text = @listbox.getItemText(0) return false end case direction when KEY_Down if((current + 1) < max) @listbox.setCurrentItem(current + 1) @editbox.text = @listbox.getItemText(current + 1) end when KEY_Up if((current - 1) >= 0) @listbox.setCurrentItem(current - 1) @editbox.text = @listbox.getItemText(current - 1) end end return true end end # FXFlexBox end # Fox ---End Code--- -- If you are not the intended recipient, you are hereby notified that any dissemination, distribution, copying or other use of this communication is strictly prohibited. If you have received this communication in error, please notify us immediately.