Reini Urban wrote:
>Robert May wrote:
Here's what I am proposing:

(1) All windows/controls will gain the -acceptfiles option that will be
used to set/unset the WS_EX_ACCEPTFILES extended style on the
window/control (For those of you familiar with it, this is all that the
Win32 DrapAcceptFiles() API does). This is a minor change to
GUI_Options.cpp, and improves on the original implementation that only
supported dropping for Window/DialogBox.

Great!

OK.

(2) All Windows/Controls will gain the AcceptFiles() method to get/set
the WS_EX_ACCEPTFILES extended style.  Additionally, as this information
is available from the extended style, no additional storage is needed to
track a window's dragdrop state.

The only minor inconvenience is with the naming.
  DragDrop object
  -acceptfiles option
  DropFiles ( => WM_DROPFILES ) NEM or OEM eventname
  GetDroppedFiles() vs. DragQueryFile() methods

I'd rather prefer -dropfiles over -acceptfiles, so that the supported
event has the same name as the option which declares it.
On the other side, WS_EX_ACCEPTFILES and DrapAcceptFiles() is also
established on the API side.

I'm not wedded to the API call names, particularly when we have methods that don't directly map to the API. As I think about it I propose the following public API:

- flag to turn on/off WS_EX_ACCEPTFILES will be -acceptfiles - we have precedent for naming style options like this. - the callback will be -onDropFiles and <window_name)_DropFiles, as that's what it is called today, and matches precedent for naming events (cf. WM_DROPFILES) - For consistency the module will be named Win32::GUI::DropFiles. This better reflects the actual functionality, and avoids a namespace clash with the existing Loft Win32::GUI::DragDop - There will an AccepFiles() method on the window objects for getting/setting the WS_EX_ACCEPTFILES style. This is consistent with current naming conventions (e.g. -dialogui/DialogUI()) Note that we can also use $win->Change(-acceptfiles => 0|1) for setting.

(2) The DropFiles callback which is already supported for all
windows/controls (handling WM_DROPFILES) will change it's callback
parameter from the current HDROP integer to a Win32::GUI::DropFiles
object.  This is a minor change to GUI_MessageLoops.cpp

This change will only be made if our new Win32::GUI::DropFiles module is loaded. If it is not, then the existing callback with a drophandle will be used - this gives us the backwards compatibility we are looking for.

(3) The Win32::GUI::DropFiles object will expose the following methods:

- GetDroppedFiles, which will return:
   in scalar context the number of files dropped
   in list context a list of dropped file names
- GetDroppedFile, which will take a zero-based index for
   the dropped file, and will return the file name

hmm.

Why hmm? We need to provide some sort of iterator access so that the user can efficiently deal with large numbers of dropped files, rather than only being able to get a list of all the files. Perhaps a proper interator api would be better with a Reset() and GetNextFile[Name]() interface?

- GetDropPos, which will return:
   in scalar context whether the drop was in the client or
     non-client area of the window

Is this possible at all?

Yes, it's the return value of DragQueryPos

   in list context the position of the mouse when the drop
     occurred in client co-ordinates

It should be possible to write GetDroppedFiles() so that code written to
use the GUI Loft's GetDroppedFiles() method will continue to work,
without modification.
> That was my idea also.
> The only problem is with the naming, DragQueryFile() vs
> GetDroppedFile()

This is no longer necessary, and deals with the naming conflict for DragQueryFile

It's only the Loft which introduced the name GetDroppedFiles().
WINAPI declares DragQueryFile and DragQueryPoint. I would like to stay
with the WINAPI names.

I'm not wedded to the API calls, but the module will also provide the following (non-?)public APIs:

Win32::GUI::DropFiles::DrapAcceptFiles(HWND,FLAG)
- direct mapping of the win32 api.  Included only for completeness.

Win32::GUI::DropFiles::DragQueryFile(HDROP, [ITEM])
- mapping of the win32 api DragQueryFile api: if called with only the HDROP parameter, returns a count of the dropped files; if called with the HDROP and ITEMS parameter returns the filename for ITEM

Win32::GUI::DropFiles::DragQueryPoint(HDROP)
- returns a list of 3 scalars: X-pos, y-pos and (non-)client flag

Win32::GUI::DropFiles::DragFinish(HDROP)
- direct mapping of the win32 api

GetDropPos: Doesn't DragQueryPoint report the position
while being dragged also? So it's not only after being dropped.

No. We only get the HDROP handle after the drop occurs (in the WM_DROPFILES message) You need to implement a full COM IDropTarget interface and use RegisterDragDrop() api to get this sort of functionality. You'd then get DragEnter, DragOver, DragLeave and Drop callbacks, and more control over what the cursor looks like etc. A future enhancement to this module? :-)

The idea behind the high level GetDroppedFiles() is to automatically
cleanup the object.

I don't agree with this - that might have been the intention of the original DragDrop module, but is not mine.

I'm not sure if this is a good idea, since perl
automatically will DESTROY the HDROP, and a DragQueryPoint afterward
will fail. So please back this idea out.

Right - calling DragFinish makes the HDOP handle invalid, and so it can't be re-used later. My implementation of GetDroppedFiles() does not call DragFinish(), and I have DrapFinish called in the object's DESTORY method, so that it gets called when the object goes out of scope (typically at the end of the DropFiles callback).

This approach is problematic, as if the DropFiles module is not loaded, then there is no DESTROY method, and DragFinish never gets called, resulting in a memory leak. Note that this is no worse than today's situation, where if you set the WS_EX_ACCEPTFILES style and don't explicitly call DragFinish() somewhere in the callback we have a leak.

Really the WM_DROPFILES handling code should call DragFinish() and return 0. This is easy enough to implement, but does mean that object passed to the callback is only valid during the callback. As I will be checking to see if our module is loaded before deciding whether to pass a HDROP handle, or Win32::GUI::DropFiles object to the callback I propose that if the module is not loaded, we'll explicitly call DragFinish when the callback returns. From what I can see there's no harm in calling it if it's already been called during the callback, and doing this will solve the memory leak problem.

(4) I intend to write all this as a separable module, with its own DLL
for the new Win32 api calls required.  Whilst there is some overhead of
this approach I want to use it as a proof of concept, as it gives the
following advantages:
- the code is separable, and hence more maintainable
- there will be no overhead (in fact a saving) for applications that
don't need the functionality (GUI.dll and GUI.pm are already way too big)
- I believe that writing tests for smaller bits of functionality will
prove significantly easier. (although I'm not sure this is a good example)
- It will be possible to 'dual-life' modules like this, allowing for
fixes/upgrades outside the normal Win32::GUI release cycle

Ok. I like that idea and I did this with CustomDraw.pm
The basic event and the object property getters do work without
use Win32::GUI::CustomDraw;
You only need to use it - and import a lot of autoloaded methods and
helper constants - if you want to call a method, primarly for setting a
value, and need some constants.

Right.

Maybe we should export the CustomDraw XS calls to a seperate DLL which
should be imported by the user or AUTOLOAD'ed.
But it's by far not that big as the convenience methods in the perl part.

Probably - but I haven't had a chance to look at your CustomDraw code yet.

For DragDrop:
The WM_DROPFILES event should work without use Win32::GUI::DragDrop;
> But calling the methods might require it.

Without Win32::GUI::DropFiles it'll work as today, passing a HDROP handle. If you use Win32::GUI::DropFiles; then you'll get an object in the callback and acceess to the methods.

Or AUTOLOAD it based on the object name.
Same with the Brush and DC maybe.


If it proves to be the wrong approach, then it will be easy to fold the
changes back into the main GUI.xs and GUI.pm files.  If it proves good,
the over time I hope to move other less used features out of GUI.pm and
GUI.xs.

Additionally I want to look at putting more of the code than we might
usually in perl rather than XS so that we can keep the DLL sizes small
and (in the future) take better advantages of AUTOLOADing little used
methods.

OwnerDraw, NotifyIcon and such.
Yes.

NotifyIcon I certainly have on my list of candidates. I haven't thought much beyond that, but I am hopeful that a restructuring like this might reduce GUI.xs and GUI.pm to more manageable sizes. I also want to experiment with moving a lot of the per-control methods from XS back into perl. There are many methods implemented in XS that do a single call to SendMessage. By moving them back into perl I don't think we'll see much of a performance hit (there's still one 'thunk' between perl and XS, which I suspect to be the largest time overhead), and will be able to move all such methods to be AUTOLOADed, so we only take compile time and memory hits if we use them in our apps.

PS:
Note that I did some wrong guards with the new NotifyIcon members.
It should be compiler dependent and not IE dependent, when the shell
version call will fail.

I haven't looked yet. It's not really compiler dependent, but SDK version dependent. I'm using VC6++ compiler on Win98, but with the latest SDK headers, and I don't get hit by any problems. I may make it a requirement to have the lastest headers, so that I can remove much of the work-arounds from GUI.h (or perhaps just move it all to separate header files) ... something to think about in the future.

Regards,
Rob.

Reply via email to