[fxruby-users] Help with ruby segfault, initiated from FX::FXWindow::onUpdate()

Joey Kinsella jkinsella at ancillaryservices.com
Mon Jan 11 15:21:25 EST 2010


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.



More information about the fxruby-users mailing list