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 'fox' include Fox class DragDropWindow < FXMainWindow def initialize(anApp) # Initialize base class super(anApp, "Drag and Drop", nil, nil, DECOR_ALL, 0, 0, 400, 300) # Fill main window with canvas @canvas = FXCanvas.new(self, nil, 0, 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) { |sender, sel, event| FXDCWindow.new(@canvas, event) { |dc| dc.foreground = @canvas.backColor dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h) } } # Handle left button press @canvas.connect(SEL_LEFTBUTTONPRESS) { # # 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) } # Handle mouse motion events @canvas.connect(SEL_MOTION) { |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 } # Handle SEL_DND_MOTION messages from the canvas @canvas.connect(SEL_DND_MOTION) { if @canvas.offeredDNDType?(FROM_DRAGNDROP, FXWindow.colorType) @canvas.acceptDrop end } # Handle left button release @canvas.connect(SEL_LEFTBUTTONRELEASE) { @canvas.ungrab @canvas.endDrag } # Handle SEL_DND_DROP message from the canvas @canvas.connect(SEL_DND_DROP) { # 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 } # Handle request for DND data @canvas.connect(SEL_DND_REQUEST) { |sender, sel, event| if event.target == FXWindow.colorType @canvas.setDNDData(FROM_DRAGNDROP, FXWindow.colorType, Fox.fxencodeColorData(@canvas.backColor)) 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 |