We've studied drag-and-drop enabled applications from two points of view, that of a drag source and that of a drop site. But for most applications, you'll want a window to act as both a drag source and a drop site. As it turns out, this is just a simple combination of the techniques we've already seen in the previous sections.
If we use our completed drag source example program from the previous section as a starting point, and then compare it to the drop site example from the first section, it is apparent that the only "pieces" missing are:
A call to dropEnable, to make
DragSource's canvas drop-enabled;
A handler for the SEL_DND_MOTION message;
and,
A handler for the SEL_DND_DROP
message.
If you merge those three short bits of code from
dropsite.rb into dragsource.rb,
you should end up with a program that can act as both a drag source and a
drop site. To test this program, you should be able to start up two
separate copies side-by-side and then drag from one to the other. Since,
as written, both copies will start up with a red background, you might
want to modify one of them to have a different initial backgroud color.
Another interesting possibility is to start up a third program (such as an
FXRuby example that has displays a color dialog) and drag colors back and
forth between all three programs. You could spend days doing this and
never leave the house.
The complete program is listed below, and is included in the
examples directory under the file
name dragdrop.rb.
require 'fox16'
include Fox
class DragDropWindow < FXMainWindow
def initialize(anApp)
# Initialize base class
super(anApp, "Drag and Drop", :opts => DECOR_ALL, :width => 400, :height => 300)
# Fill main window with canvas
@canvas = FXCanvas.new(self, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y)
@canvas.backColor = "red"
# Enable canvas for drag-and-drop messages
@canvas.dropEnable
# Handle expose events on the canvas
@canvas.connect(SEL_PAINT) do |sender, sel, event|
FXDCWindow.new(@canvas, event) do |dc|
dc.foreground = @canvas.backColor
dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h)
end
end
# Handle left button press
@canvas.connect(SEL_LEFTBUTTONPRESS) do
#
# Capture (grab) the mouse when the button goes down, so that all future
# mouse events will be reported to this widget, even if those events occur
# outside of this widget.
#
@canvas.grab
# Advertise which drag types we can offer
dragTypes = [FXWindow.colorType]
@canvas.beginDrag(dragTypes)
end
# Handle mouse motion events
@canvas.connect(SEL_MOTION) do |sender, sel, event|
if @canvas.dragging?
@canvas.handleDrag(event.root_x, event.root_y)
unless @canvas.didAccept == DRAG_REJECT
@canvas.dragCursor = getApp().getDefaultCursor(DEF_SWATCH_CURSOR)
else
@canvas.dragCursor = getApp().getDefaultCursor(DEF_DNDSTOP_CURSOR)
end
end
end
# Handle SEL_DND_MOTION messages from the canvas
@canvas.connect(SEL_DND_MOTION) do
if @canvas.offeredDNDType?(FROM_DRAGNDROP, FXWindow.colorType)
@canvas.acceptDrop
end
end
# Handle left button release
@canvas.connect(SEL_LEFTBUTTONRELEASE) do
@canvas.ungrab
@canvas.endDrag
end
# Handle SEL_DND_DROP message from the canvas
@canvas.connect(SEL_DND_DROP) do
# Try to obtain the data as color values first
data = @canvas.getDNDData(FROM_DRAGNDROP, FXWindow.colorType)
unless data.nil?
# Update canvas background color
@canvas.backColor = Fox.fxdecodeColorData(data)
end
end
# Handle request for DND data
@canvas.connect(SEL_DND_REQUEST) do |sender, sel, event|
if event.target == FXWindow.colorType
@canvas.setDNDData(FROM_DRAGNDROP, FXWindow.colorType, Fox.fxencodeColorData(@canvas.backColor))
end
end
end
def create
# Create the main window and canvas
super
# Register the drag type for colors
FXWindow.colorType = getApp().registerDragType(FXWindow.colorTypeName)
# Show the main window
show(PLACEMENT_SCREEN)
end
end
if __FILE__ == $0
FXApp.new("DragDrop", "FXRuby") do |theApp|
DragDropWindow.new(theApp)
theApp.create
theApp.run
end
end