Opened 9 years ago

Closed 9 years ago

#32 closed task (fixed)

Port DND classes

Reported by: dmik Owned by: dmik
Priority: blocker Milestone: Qt GA
Component: QtGui Version: 4.5.1 Beta 1
Severity: high Keywords:
Cc:

Description

Provide the OS/2 version of the Drag And Drop supporting classes. This basically includes the internal QDragManager class and its various helpers.

Note that this enhancement depends on #31.

Change History (20)

comment:1 Changed 9 years ago by diver

  • Milestone changed from Qt Enhanced to Qt GA

comment:2 Changed 9 years ago by diver

  • Priority changed from major to blocker

comment:3 Changed 9 years ago by diver

  • Severity set to high

comment:4 Changed 9 years ago by dmik

  • Owner set to dmik
  • Status changed from new to accepted

comment:5 Changed 9 years ago by dmik

I recalled that in order to paint in windows during DnD, we need to use a special presentation space, obtained with DrgGetPS. It wasn't implemented in Qt3 but Qt4 seems to frequently use it so it's a must now (which means some more work). For example, examples\draganddrop\dropsite uses this feature.

comment:6 Changed 9 years ago by dmik

There is one difficulty with painting while dragging. It seems that once DrgGetPS is called for the given HWND, PM assumes that the whole window rectangle will be repainted by the application and doesn't attempt to restore the bits under the previous position of the dragging mouse pointer. However, in Qt4 child widgets normally share a single top-level HWND and may want to repaint themselves separately from each other while dragging over them. As a result, child widgets that don't repaint after a given mouse move, will contain the previous mouse pointer image if it happens moves over them.

What could help is that if PM provided a way to specify the clip rectangle for DrgGetPS() didn't assume we repaint areas outside it but currently I don't know how to achieve it.

comment:7 Changed 9 years ago by dmik

Overriding the repaint rectangle in QRasterWindowSurface::flush() so that it always covers the entire window while in drag significantly improves the situation, but there are still a few mouse pointers that are left unpainted. I assume that it happens due to the message flow and our DrgGetPS() call going out of sync.

comment:8 Changed 9 years ago by dmik

DrgGetPS() looks completely incorrect to me: it doesn't even clip out the other upper top-level windows from the rectangle of the window it is called for and therefore if the mouse pointer is over this overlapping area of such an upper window it won't be erased there.

comment:9 Changed 9 years ago by dmik

The previous comment is incorrect itself: DrgGetPS() seems to only work correctly when called from within the DM_DRAGENTER/DM_DRAGLEAVE/DM_DROP message handler (which is kind of mentioned in the documentation).

So I think I found a working solution: we will make sure that all update requests issued during DM_DRAGENTER/DM_DRAGLEAVE/DM_DROP processing will be executed *before* we return from these messages (normally they would have been handled upon the next event loop iteration where neither of DrgGetPS()/WinGetPS()/WinBeginPaint?() could correctly deal with the dragging mouse pointer).

I will clean up the code and commit tomorrow.

comment:10 Changed 9 years ago by dmik

Painting during DnD is finished in r447. The dropsite example works quite smooth now: no screen corruption and almost no flicker (the one that is still seen is due to a way PM handles drawing of the mouse pointer during DnD and looks like unavoidable).

comment:11 Changed 9 years ago by dmik

Unfortunately, I cannot boot ECS with Samba any longer. Every boot gives me

LIBC Error: Couldn't register process in shared memory!

Have been trying for an hour. Have no idea how to solve this. W/o Samba, it's hardly usable here.

comment:12 Changed 9 years ago by dmik

Got some progress in implementing the drag part of the deal. (The Samba problem was solved after another half an hour of retries. I will probably have to migrate back to LANMAN).

comment:13 Changed 9 years ago by dmik

I enabled DnD in r456 (note that you need to run configure.cmd which will cause almost a full rebuild, sorry). Plain text drag and drop should now work between Qt4 and PM applications. DnD workers for other MIME formats (namely, bitmaps and the generic worker for all other MIME types not recognized by PM) will be added next, as well as the support for setting custom mouse pointers for the DnD operations (QDrag::setPixmap()).

The following problems remain:

  1. All standard Qt widgets use the left mouse button (hard coded) to start the drag operation which looks a bit weird on OS/2 where the right mouse button is normally used to start dragging. The only thing we can do here is to patch every single widget that supports DnD so that it will use the value from SV_BEGINDRAG to determine the mouse button that should initiate DnD. I don't like this solution a lot because adding platform-dependent code to many sources intended to be cross-platform is generally not a good idea.
  2. EPM shows weird behavior when dragging from it to Qt4 applications: when drop occurs, they send DM_RENDER to EPM and wait for DM_RENDERCOMPLETE, but the latter never appears so both the Qt4 application and EPM freeze (killing EPM will unfreeze both).
  3. The Qt4 API allows to access the dragged data (e.g. the dragged text string) before the drag operation is finished and it is used in many Qt4 applications (including the dropsite example where it inspects the dragged data and puts the results to a table, and the textedit demo program where the dragged text is checked for null and the drop will simply insert nothing after a drop if it gets a null string during the drag, even if there is actually some data). However, PMREF explicitly says that
    The data transfers must not be done before responding to the DM_DROP message.
    

and therefore accessing dragged data is disabled until the object is dropped. In case of the dropsite example, it means that the table is left
empty until the drop; in case of the textedit demo program, it means that we cannot paste text into the text area at all. I suppose that other Qt applications may show similar weird behavior. Interesting that if we violate PMREF and enable accessing dragged data before DM_DROP, things start work well (tested with Firefox) but EPM becomes again a problem: an attempt to initiate the data transfer hangs the whole system until reboot...

I will first do more DnD workers and custom mouse pointers and then decide if we should resolve any of the above problems for the GA.

comment:14 Changed 9 years ago by dmik

I must admit that DnD in OS/2 is pure hell. The protocol is weakly documented and every application decides on its own. For example, Mozilla apps acting as drag sources are unable to handle the data render requests after they return from DrgDrag? (which happens when the target returns from DM_DROP handling), although PMREF explicitly states that data rendering requests must not be sent by the target from DM_DROP but instead from a private message posted from DM_DROP to itself.

On the other hand, there are applications that don't complete render requests (don't reply with DM_RENDERCOMPLETE) while the target is in DM_DROP (like EPM that I already mentioned).

I still cannot find a common denominator to make both cases work.

comment:15 Changed 9 years ago by dmik

If we send the DM_RENDER request to Mozilla after the DnD session is over (i.e. we return from DM_DROP and it returns from DrgDrag?), Mozilla simply crashes. Nice.

I would not mind waiting for DM_RENDERCOMPLETE asynchronously (which would be a more or less acceptable solution for the above problems) but the problem is that rendering is synchronous in Qt by design: QMimeData::data() and its derivatives must instantly return the dragged data and there is simply no API for later data retrieval.

comment:16 Changed 9 years ago by dmik

In r458, I solved the above sync/async rendering issue (covers problems 2 and 3 from comment 13 above). In order to do so, I had to hard code the applications that don't support synchronous rendering using the EXE name and window class name and switch to asynchronous rendering when such an application is detected as a drag source.

At present, only EPM is known to not support synchronous rendering. If we detect more applications, we should add them to qdnd_pm.cpp (QPMDragData::initialize()).

Hard coding is extremely inflexible but it's best we can do ATM: there is no other way to detect if the particular application supports synchronous rendering or not. Later we may make it more flexible by introducing a system-wide Qt settings key for that purpose.

Note that for applications that don't support synchronous rendering, dragged data is only available from within the QDropEvent handler. For the ones, supporting such rendering, it is available from all DnD events, similarly to other platforms.

comment:17 Changed 9 years ago by dmik

In r465, I added the support for all mime types not directly supported by dedicated converters. So, now any kind of data may be dragged and dropped between two Qt4 applications.

Note that I'm not going to add dedicated support for native bitmap DnD since I can't find a single application around that would drag bitmaps. Once there is a real need, such a support may be easily added.

Currently, I'm facing two new problems:

  1. It seems that if the child widget doesn't accept the DnD event, it is sent to the parent widget which may then accept it -- at least, both Windows and X11 show such a behavior. On OS/2, if the child refuses the drop, it remains refused which demonstrates different functionality comparing to these platforms (check draggabletext and draggable icons examples).
  2. It seems that newly created child widgets inherit the parent widget palette but it's not the case on OS/2 for some reason. (Not a DnD issue, but should be easier to fix now than to remember for later).

comment:18 Changed 9 years ago by dmik

Problem 1 is fixed in r466. Problem 2 is fixed in r467 (quite a tricky vendor bug).

comment:19 Changed 9 years ago by dmik

Implemented support for setting the drag pixmap in r469. It has the following limitations:

  1. Pixmap masks and alpha channels are ignored when setting a pixmap for the drag cursor with QDrag::setPixmap(). We have to use HBITMAP as a drag image (which doesn't supports masks) because HBITMAP can be of any size while HPOINTER (which may also be used as a drag image) has a fixed system size which causes the pixmap to be scaled down if it's bigger. Such scaling gives bad visual appearance (which is worse than neutral gray pixels instead of transparent ones as in case of full-sized HBITMAP) and even makes DnD less useless in some cases like the examples/draganddrop/puzzle one.
  2. QDrag::setDragCursor() is not supported (pixmaps set using this method are simply ignored and the system DnD cursor shapes are always used). PM doesn't allow to change the drag image during drag so these custom drag cursors are useless in our case.

As of now, I'm quite satisfied with the current DnD implementation -- it is better than in Qt3 (at least because of the support painting during drag, pixmaps and better support for some strange applications such as EPM). All test cases from examples/draganddrop seem to work very well.

There are just a few cosmetic things to be done which I leave for tomorrow.

comment:20 Changed 9 years ago by dmik

  • Resolution set to fixed
  • Status changed from accepted to closed

I fixed all the cosmetics and some more small bugs. This task is now done.

Note: See TracTickets for help on using tickets.