This might seem like a trivial thing to do, but as it turns out it’s not. My use case for this scenario is that somebody last week suggested it would be very nice if Help Builder supported the ability to drag and drop images directly from explorer into the Help Builder edit window. The nice thing about that is that you can leave explorer running and drag and drop multiple images easily.
FoxPro supports drag and drop easily enough via he Ole Drag & Drop features that have been in the product since 7.0 (I think). To manage drag and drop to a control from an external application you basically implement the OleDragOver method to essentially enable the control as a drop target by querying the drag data and then setting the OleDropHasData Property to a non-zero value. Then you can use the OleDragDrop event to actually do what you need to do on the drop action.
With files from Explorer you can check for a format type of 15 – file handles, which allows capturing an array of all the selected files dropped.
* OleDragOver
LPARAMETERS oDataObject, nEffect, nButton, nShift, nXCoord, nYCoord, nState
*** Check for files
IF odataobject.GETFORMAT(15)
THIS.OLEDROPHASDATA = 1
ENDIF
* OleDragDrop
LPARAMETERS oDataObject, nEffect, nButton, nShift, nXCoord, nYCoord
LOCAL lcText, lcOldPath, lcImage
IF oDataObject.GetFormat(15)
DIMENSION laFiles[1]
oDataObject.GetData(15,@laFiles)
IF ALEN(laFiles,1) > 0
lcText = ""
lcImage = laFiles[1]
lcext = LOWER(JUSTEXT(lcImage))
DO CASE
CASE INLIST(lcExt,"png","gif","jpg","bmp","TIF")
*** Drop an image
lcOldPath = SYS(5) + CURDIR()
CD (JUSTPATH(goHelp.oHelp.cFileName))
lcRelImage = SYS(2014,lcImage)
IF lcRelImage # ".."
lcImage = lcRelImage
ENDIF
*** Convert to proper case
lcImage = LongPath(lcImage)
lcText = " " + THIS.cLTag + [img src="]+ lcImage +["]+THIS.cRTag
CD (lcOldPath)
ENDCASE
this.SelLength = 0
this.SelText = lcText
ENDIF
So far so good. But this code actually doesn’t quite do what I want. I want the text to be dropped into the textbox at the drop location. However, dragging and dropping files out of Explorer is not text (the only format that VFP will drop directly) so by default the drop operation doesn’t place the files anywhere. My code actually modifies the file formatting so that it’s turned into Help Builder’s markup code that gets embedded which is stored in lcText. My first thought was to use SelText to force the text into the document as shown above. Unfortunately this doesn’t quite do the trick as the text is pasted at the current cursor position, not the drop cursor position. Drag and Drop doesn’t actually change the cursor position of the text box.
So that’s a problem. Another thought here was to modify the DragAndDrop Data object that gets passed using SetData() and assigning my updated text to it and changing the format to 1. Unfortunately, that doesn’t work either because SetData() cannot be called from the DragOver or DragDrop events.
So, finally the solution turned out to be more of a nuisance and hack. The OleDragDrop event passes in a X and Y coordinate for the form. With that coordinate you can now force the mouse cursor to this position essentially forcing SelText focus to this location. So with this update the working code looks like this:
LPARAMETERS oDataObject, nEffect, nButton, nShift, nXCoord, nYCoord
LOCAL lcText, lcOldPath, lcImage
IF oDataObject.GetFormat(15)
DIMENSION laFiles[1]
oDataObject.GetData(15,@laFiles)
IF ALEN(laFiles,1) > 0
lcText = ""
lcImage = laFiles[1]
lcext = LOWER(JUSTEXT(lcImage))
DO CASE
CASE INLIST(lcExt,"png","gif","jpg","bmp","TIF")
*** Drop an image
lcOldPath = SYS(5) + CURDIR()
CD (JUSTPATH(goHelp.oHelp.cFileName))
lcRelImage = SYS(2014,lcImage)
IF lcRelImage # ".."
lcImage = lcRelImage
ENDIF
*** Convert to proper case
lcImage = LongPath(lcImage)
lcText = " " + THIS.cLTag + [img src="]+ lcImage +["]+THIS.cRTag
CD (lcOldPath)
ENDCASE
IF !EMPTY(lcText)
*** Pick up the Mouse Position of the drop location
*** and force the mouse pointer to that position
MOUSE CLICK AT nYCoord,nXCoord PIXELS WINDOW (THISFORM.Name)
DOEVENTS
*** Read ahead 1 char to check for spaces
this.SelLength = 1
IF this.SelText != " "
lcText = lcText + " "
ENDIF
*** Now paste at cursor
SelLength = 0
this.SelText = lcText
ENDIF
ENDIF
ENDIF
RETURN
Note the use of the MOUSE command to force the mouse to the location. The DOEVENTS is also significant to allow the form to refresh before changing the SelText location.
One thing that sucks about this is that VFP’s drag and drop behavior looks a little weird in a text box. You see the drag and drop cursor and you also see – a few characters below a caret cursor ‘dragging behind’. It looks a bit confusing. The cursor is where it actually drops which is the correct behavior, but the cursor caret is a visual nuisance.
Anyway there you have it – it works even if it’s not as straight forward as I would have expected.
Other Posts you might also like