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.