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.