only for RuBoard - do not distribute or recompile |
We need to add a new class to the RadEx project that will implement IPersistFile and IDropTarget . Call the file clsDropHandler.cls . Its declarations section begins as follows :
'clsDropHandler.cls Implements IDropTarget Implements IPersistFile
The only method that we need to implement on this interface is Load . The code is the same as the code we used to create the icon handler back in Chapter 5. Example 7.1 contains the implementation.
'clsDropHandler.cls Private m_sTargetFile As String Private Sub IPersistFile_Load( ByVal pszFileName As VBShellLib.LPCOLESTR, _ ByVal dwMode As VBShellLib.DWORD) m_sTargetFile = Space(255) CopyMemory ByVal StrPtr(m_sTargetFile), _ ByVal pszFileName, _ Len(m_sTargetFile) End Sub
The most interesting aspect of implementing the IDropTarget interface (and, in particular, its DragEnter method) concerns the POINTL parameter to the DragEnter method. Notice from our earlier presentation of the method's syntax that it is an [in] parameter; therefore, it is not a pointer. We have a slight problem here, because Visual Basic does not allow UDTs to be passed ByVal , which is what is going on here. POINTL is a structure that contains the location of the mouse in the drop area. It is defined like this:
typedef struct _POINTL { LONG x; LONG y; } POINTL;
Fortunately, we do not need this point information, so there is a simple workaround. Instead of passing a point, we can use two Longs, which will occupy the same space on the stack as a POINTL structure. The final definition for DragEnter looks like this:
HRESULT DragEnter ( [in] IDataObject *pDataObj, [in] KEYSTATES grfKeyState, [in] long x, [in] long y, [in, out] DROPEFFECT *pdwEffect );
|
With that said, let's get on to the actual implementation of IDropTarget . We only need to implement two methods ( DragEnter and Drop ) to satisfy our needs. Let's look at the DragEnter implementation, which is shown in Example 7.2, and then we'll discuss how it works.
Private m_dwDropKey As KEYSTATES Private m_dwMouseKey As KEYSTATES Private Sub IDropTarget_DragEnter( ByVal pDataObj As VBShellLib.IDataObject, _ ByVal grfKeyState As VBShellLib.KEYSTATES, _ ByVal x As Long, _ ByVal y As Long, _ pdwEffect As VBShellLib.DROPEFFECT) pdwEffect = DROPEFFECT_COPY 'Get keyboard state 'Does NOT take into account multiple keys being held down If grfKeyState And MK_CONTROL Then m_dwDropKey = MK_CONTROL ElseIf grfKeyState And MK_SHIFT Then m_dwDropKey = MK_SHIFT ElseIf grfKeyState And MK_ALT Then m_dwDropKey = MK_ALT Else m_dwDropKey = 0 End If 'Get mouse state If grfKeyState And MK_LBUTTON Then m_dwMouseKey = MK_LBUTTON ElseIf grfKeyState And MK_RBUTTON Then m_dwMouseKey = MK_RBUTTON End If End Sub
The first parameter given to us by the shell is an IDataObject reference. Remember clsDropFiles? This is the class we used in Chapter 6, to implement IShellExtInit::Initialize . We could pass this IDataObject reference to an instance of clsDropFiles to get a list of all the source filenames for our Drop implementation. But we won't, because in this instance it would be a little slow. It would be called every time we enter the drop target, which is way too often. Instead, we'll wait until Drop is actually called to get a list of the source filenames. So, for now, we can ignore the pDataObj parameter. The parameters we are really interested in are pdwEffect and grfKeyState .
The pdwEffect parameter is set to one of the DROPEFFECT enumeration values and is used to visually indicate what the result of a drop operation would be. The shell conveys this by changing the cursor to one of the shapes shown in Figure 7.1. As the cursor moves over a drop target, the shell changes the cursor to visually show what type of drop operation is occurring. To cancel a drop operation, pdwEffect is set to DROPEFFECT_NONE .
The grfKeyState parameter contains keyboard- and mouse-state information.
Our implementation sets pdwEffect to DROPEFFECT_COPY regardless of the keys pressed. This will cause the cursor to change (as shown in Figure 7.1), giving us a visual cue that the files can be dropped. Keyboard and mouse states are stored in two separate private data members . These values will be used when we implement Drop .
Drop is where all the action takes place. This is the method that is called when a file is dropped on a drop target. Our implementation, which is shown in Example 7.3, is fairly straightforward. Let's take a look.
Private Sub IDropTarget_Drop( ByVal pDataObj As VBShellLib.IDataObject, _ ByVal grfKeyState As VBShellLib.KEYSTATES, _ ByVal x As Long, _ ByVal y As Long, _ pdwEffect As VBShellLib.DROPEFFECT) Dim i As Integer Dim sMsg As String Set m_clsDropFiles = New clsDropFiles m_clsDropFiles.GetDropFiles pDataObj, ".rad" If (m_clsDropFiles.Count = 0) Then MsgBox "Only RAD files can be dropped here!", _ vbOKOnly, _ "RAD Drop Handler" Exit Sub End If If m_dwMouseKey = MK_LBUTTON Then sMsg = "MK_LBUTTON" & vbCrLf End If If m_dwMouseKey = MK_RBUTTON Then sMsg = "MK_RBUTTON" & vbCrLf End If Select Case m_dwDropKey Case MK_CONTROL sMsg = sMsg & "Drop + CTRL" Case MK_SHIFT sMsg = sMsg & "Drop + SHIFT" Case MK_ALT sMsg = sMsg & "Drop + ALT" Case Else sMsg = sMsg & "Normal Drop" End Select sMsg = sMsg & vbCrLf & vbCrLf sMsg = sMsg & "Drop File(s):" & vbCrLf For i = 0 To m_clsDropFiles.Count - 1 sMsg = sMsg & m_clsDropFiles.Files(i) & vbCrLf Next i sMsg = sMsg & vbCrLf sMsg = sMsg & "Target File: " & m_sTargetFile MsgBox sMsg, vbOKOnly, "RAD Drop Handler" End Sub
The shell passes in a reference to IDataObject , which we in turn pass on to an instance of clsDropFiles (see Chapter 5). If our file count is at this point, we know that the files dropped were not .rad files, and we can display an error message. Otherwise, a list of the target files, the name of the drop file, and the keyboard and mouse states are displayed in a message box like the one shown in Figure 7.2.
only for RuBoard - do not distribute or recompile |