Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gdkdrop-win32.c

  Sprache: C
 

/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
 * Copyright (C) 2001 Archaeopteryx Software Inc.
 * Copyright (C) 1998-2002 Tor Lillqvist
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */


/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
 */


#include "config.h"
#include <string.h>

#include <io.h>
#include <fcntl.h>

/* The mingw.org compiler does not export GUIDS in it's import library. To work
 * around that, define INITGUID to have the GUIDS declared. */

#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
#define INITGUID
#endif

#include "gdkdropprivate.h"

#include "gdkdrag.h"
#include "gdkprivate-win32.h"
#include "gdkwin32.h"
#include "gdkwin32dnd.h"
#include "gdkdisplayprivate.h"
#include "gdkwin32dnd-private.h"
#include "gdkdisplay-win32.h"
#include "gdkdeviceprivate.h"
#include "gdkhdataoutputstream-win32.h"

#include <ole2.h>

#include <shlobj.h>
#include <shlguid.h>
#include <objidl.h>
#include <glib/gi18n-lib.h>

#include <gdk/gdk.h>
#include <glib/gstdio.h>

#define GDK_TYPE_WIN32_DROP              (gdk_win32_drop_get_type ())
#define GDK_WIN32_DROP(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_DROP, GdkWin32Drop))
#define GDK_WIN32_DROP_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WIN32_DROP, GdkWin32DropClass))
#define GDK_IS_WIN32_DROP(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_DROP))
#define GDK_IS_WIN32_DROP_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WIN32_DROP))
#define GDK_WIN32_DROP_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WIN32_DROP, GdkWin32DropClass))

typedef struct _GdkWin32Drop GdkWin32Drop;
typedef struct _GdkWin32DropClass GdkWin32DropClass;

struct _GdkWin32Drop
{
  GdkDrop          drop;

  /* The drag protocol being in use */
  GdkDragProtocol  protocol;

  /* The actions supported at GTK level. Set in gdk_win32_drop_status(). */
  GdkDragAction    actions;

  guint            scale;          /* Temporarily caches the HiDPI scale */
  int              last_x;         /* Coordinates from last event, in GDK space */
  int              last_y;
  DWORD            last_key_state; /* Key state from last event */

  /* Just like GdkDrop->formats, but an array, and with format IDs
   * stored inside.
   */

  GArray          *droptarget_w32format_contentformat_map;

  /* The list from WM_DROPFILES is store here temporarily,
   * until the next gdk_win32_drop_read_async ()
   */

  char            *dropfiles_list;

  guint drop_finished : 1/* FALSE until gdk_drop_finish() is called */
  guint drop_failed : 1;   /* Whether the drop was unsuccessful */
};

struct _GdkWin32DropClass
{
  GdkDropClass parent_class;
};

GType    gdk_win32_drop_get_type (void);

G_DEFINE_TYPE (GdkWin32Drop, gdk_win32_drop, GDK_TYPE_DROP)

/* This structure is presented to COM as an object that
 * implements IDropTarget interface. Every surface that
 * can be a droptarget has one of these.
 */

struct _drop_target_context
{
  IDropTarget                     idt;
  int                             ref_count;

  /* The drop object we create when a drag enters our surface.
   * The drop object is destroyed when the drag leaves.
   */

  GdkDrop                        *drop;
  /* We get this at the object at creation time and keep
   * it indefinitely. Drops (see above) come and go, but
   * this surface remains the same.
   * This is not a reference, as drop_target_context must not
   * outlive the surface it's attached to.
   * drop_target_context is not folded into GdkWin32Surface
   * only because it's easier to present it to COM as a separate
   * object when it's allocated separately.
   */

  GdkSurface                     *surface;
  /* This is given to us by the OS, we store it here
   * until the drag leaves our surface HWND. It is referenced
   * (using COM reference counting).
   */

  IDataObject                    *data_object;
};

static void
gdk_win32_drop_init (GdkWin32Drop *drop)
{
  GDK_NOTE (DND, g_print ("gdk_win32_drop_init %p\n", drop));
}

static void
gdk_win32_drop_finalize (GObject *object)
{
  GdkDrop *drop;
  GdkWin32Drop *drop_win32;

  GDK_NOTE (DND, g_print ("gdk_win32_drop_finalize %p\n", object));

  drop = GDK_DROP (object);

  drop_win32 = GDK_WIN32_DROP (drop);

  g_array_unref (drop_win32->droptarget_w32format_contentformat_map);

  G_OBJECT_CLASS (gdk_win32_drop_parent_class)->finalize (object);
}

static GdkDrop *
gdk_drop_new (GdkDisplay        *display,
              GdkDevice         *device,
              GdkDrag           *drag,
              GdkContentFormats *formats,
              GdkSurface        *surface,
              GdkDragProtocol    protocol)
{
  GdkWin32Drop *drop_win32;
  GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);

  drop_win32 = g_object_new (GDK_TYPE_WIN32_DROP,
                             "device", device,
                             "drag", drag,
                             "formats", formats,
                             "surface", surface,
                             NULL);

  drop_win32->scale = gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL);
  drop_win32->protocol = protocol;

  return GDK_DROP (drop_win32);
}


#define PRINT_GUID(guid) \
  g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
           ((gulong *)  guid)[0], \
           ((gushort *) guid)[2], \
           ((gushort *) guid)[3], \
           ((guchar *)  guid)[8], \
           ((guchar *)  guid)[9], \
           ((guchar *)  guid)[10], \
           ((guchar *)  guid)[11], \
           ((guchar *)  guid)[12], \
           ((guchar *)  guid)[13], \
           ((guchar *)  guid)[14], \
           ((guchar *)  guid)[15]);

static ULONG STDMETHODCALLTYPE
idroptarget_addref (LPDROPTARGET This)
{
  drop_target_context *ctx = (drop_target_context *) This;

  int ref_count = ++ctx->ref_count;

  GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n"This, ref_count));

  return ref_count;
}

static HRESULT STDMETHODCALLTYPE
idroptarget_queryinterface (LPDROPTARGET This,
                            REFIID       riid,
                            LPVOID      *ppvObject)
{
  GDK_NOTE (DND, {
      g_print ("idroptarget_queryinterface %p "This);
      PRINT_GUID (riid);
    });

  *ppvObject = NULL;

  if (IsEqualGUID (riid, &IID_IUnknown))
    {
      GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
      idroptarget_addref (This);
      *ppvObject = This;
      return S_OK;
    }
  else if (IsEqualGUID (riid, &IID_IDropTarget))
    {
      GDK_NOTE (DND, g_print ("...IDropTarget S_OK\n"));
      idroptarget_addref (This);
      *ppvObject = This;
      return S_OK;
    }
  else
    {
      GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
      return E_NOINTERFACE;
    }
}

static ULONG STDMETHODCALLTYPE
idroptarget_release (LPDROPTARGET This)
{
  drop_target_context *ctx = (drop_target_context *) This;

  int ref_count = --ctx->ref_count;

  GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n"This, ref_count));

  if (ref_count == 0)
    {
      g_clear_object (&ctx->drop);
      g_free (This);
    }

  return ref_count;
}

static GdkContentFormats *
query_object_formats (GdkDisplay   *display,
                      LPDATAOBJECT  pDataObj,
                      GArray       *w32format_contentformat_map)
{
  IEnumFORMATETC *pfmt = NULL;
  FORMATETC fmt;
  HRESULT hr;
  GdkContentFormatsBuilder *builder;
  GdkContentFormats *result_formats;

  hr = IDataObject_EnumFormatEtc (pDataObj, DATADIR_GET, &pfmt);
  if (FAILED (hr))
    return gdk_content_formats_new (NULL, 0);

  builder = gdk_content_formats_builder_new ();
  hr = IEnumFORMATETC_Next (pfmt, 1, &fmt, NULL);

  while (SUCCEEDED (hr) && hr != S_FALSE)
    {
      gboolean is_predef;
      char *registered_name = _gdk_win32_get_clipboard_format_name (fmt.cfFormat, &is_predef);

      if (registered_name && is_predef)
        GDK_NOTE (DND, g_print ("supported built-in source format 0x%x %s\n", fmt.cfFormat, registered_name));
      else if (registered_name)
        GDK_NOTE (DND, g_print ("supported source format 0x%x %s\n", fmt.cfFormat, registered_name));
      else
        GDK_NOTE (DND, g_print ("supported unnamed? source format 0x%x\n", fmt.cfFormat));

      g_free (registered_name);
      gdk_win32_clipdrop_add_win32_format_to_pairs (gdk_win32_display_get_clipdrop (display), fmt.cfFormat, w32format_contentformat_map, builder);
      hr = IEnumFORMATETC_Next (pfmt, 1, &fmt, NULL);
    }

  gdk_win32_com_clear (&pfmt);

  result_formats = gdk_content_formats_builder_free_to_formats (builder);

  return result_formats;
}

static void
set_data_object (LPDATAOBJECT *location, LPDATAOBJECT data_object)
{
  gdk_win32_com_clear (location);

  *location = data_object;

  if (*location != NULL)
    IDataObject_AddRef (*location);
}

/* Figures out an action that the user forces onto us by
 * pressing some modifier keys.
 */

static GdkDragAction
get_user_action (DWORD grfKeyState)
{
  /* Windows explorer does this:
   * 'C'ontrol for 'C'opy
   * a'L't (or Contro'L' + Shift) for 'L'ink
   * Shift for Move
   * Control + Alt or Shift + Alt or Control + Alt + Shift for Default action (see below).
   *
   * Default action is 'Copy' when dragging between drives, 'Move' otherwise.
   * For GTK 'between drives' turns into 'between applications'.
   */

  if (((grfKeyState & (MK_CONTROL | MK_ALT)) == (MK_CONTROL | MK_ALT)) ||
      ((grfKeyState & (MK_ALT | MK_SHIFT)) == (MK_ALT | MK_SHIFT)) ||
      ((grfKeyState & (MK_CONTROL | MK_ALT | MK_SHIFT)) == (MK_CONTROL | MK_ALT | MK_SHIFT)))
    {
      return 0;
    }
  else if ((grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT))
    return GDK_ACTION_LINK;
  else if (grfKeyState & MK_CONTROL)
    return GDK_ACTION_COPY;
  else if (grfKeyState & MK_ALT)
    return GDK_ACTION_MOVE;

  return 0;
}

static DWORD
drop_effect_for_actions (GdkDragAction actions)
{
  DWORD effects = 0;
  int effect_count = 0;

  if (actions & GDK_ACTION_MOVE)
    {
      effects |= DROPEFFECT_MOVE;
      effect_count++;
    }
  if (actions & GDK_ACTION_LINK)
    {
      effects |= DROPEFFECT_LINK;
      effect_count++;
    }
  if (actions & GDK_ACTION_COPY)
    {
      effects |= DROPEFFECT_COPY;
      effect_count++;
    }

  if (effect_count == 0)
    effects = DROPEFFECT_NONE;
  else if (effect_count > 1)
    /* Actually it should be DROPEFECT_ASK, but Windows doesn't support that */
    effects = DROPEFFECT_COPY;

  return effects;
}

static GdkDragAction
actions_for_drop_effects (DWORD effects)
{
  GdkDragAction actions = 0;

  if (effects & DROPEFFECT_MOVE)
    actions |= GDK_ACTION_MOVE;
  if (effects & DROPEFFECT_LINK)
    actions |= GDK_ACTION_LINK;
  if (effects & DROPEFFECT_COPY)
    actions |= GDK_ACTION_COPY;

  return actions;
}

static GdkDragAction
filter_actions (GdkDragAction actions,
                GdkDragAction filter)
{
  return actions & filter;
}

static GdkDragAction
set_source_actions_helper (GdkDrop       *drop,
                           GdkDragAction  actions,
                           DWORD          grfKeyState)
{
  GdkDragAction user_action;
  GdkWin32Drop *drop_win32;

  user_action = get_user_action (grfKeyState);

  drop_win32 = GDK_WIN32_DROP (drop);
  drop_win32->actions = actions;

  if (user_action != 0)
    gdk_drop_set_actions (drop, user_action);
  else
    gdk_drop_set_actions (drop, actions);

  return actions;
}

/* Utility function to translate screen coordinates to surface-relative
 * coordinates. This routine only works with pixel values that aren't
 * scaled by any GDK DPI scale factor.
 */

static void
unscaled_screen_to_client (GdkSurface* surface,
                           double screen_x,
                           double screen_y,
                           double *client_x,
                           double *client_y)
{
  POINT client_origin;

  client_origin.x = 0;
  client_origin.y = 0;
  ClientToScreen (GDK_SURFACE_HWND (surface), &client_origin);
  *client_x = screen_x - client_origin.x;
  *client_y = screen_y - client_origin.y;
}

/* The pdwEffect here initially points
 * to a DWORD that contains the value of dwOKEffects argument in DoDragDrop,
 * i.e. the drag action that the drag source deems acceptable.
 * On return it should point to the effect value that denotes the
 * action that is going to happen on drop, and that is what DoDragDrop will
 * put into the DWORD that pdwEffect was pointing to.
 */

static HRESULT STDMETHODCALLTYPE
idroptarget_dragenter (LPDROPTARGET This,
                       LPDATAOBJECT pDataObj,
                       DWORD        grfKeyState,
                       POINTL       pt,
                       LPDWORD      pdwEffect_and_dwOKEffects)
{
  drop_target_context *ctx = (drop_target_context *) This;
  GdkDrop *drop;
  GdkWin32Drop *drop_win32;
  GdkDisplay *display;
  int pt_x;
  int pt_y;
  double x = 0.0;
  double y = 0.0;
  GdkDrag *drag;
  GdkDragAction source_actions;
  GdkDragAction dest_actions;
  GdkContentFormats *formats;
  GArray *droptarget_w32format_contentformat_map;

  GDK_NOTE (DND, g_print ("idroptarget_dragenter %p @ %ld : %ld"
                          " for dest surface 0x%p"
                          ". dwOKEffects = %lu\n",
                          This, pt.x, pt.y,
                          ctx->surface,
                          *pdwEffect_and_dwOKEffects));

  g_clear_object (&ctx->drop);

  /* Try to find the GdkDrag object for this DnD operation,
   * if it originated in our own application.
   */

  drag = NULL;

  if (ctx->surface)
    drag = gdk_win32_find_drag_for_dest_surface (ctx->surface);

  display = gdk_surface_get_display (ctx->surface);

  droptarget_w32format_contentformat_map = g_array_new (FALSEFALSEsizeof (GdkWin32ContentFormatPair));
  formats = query_object_formats (display, pDataObj, droptarget_w32format_contentformat_map);
  drop = gdk_drop_new (display,
                       gdk_seat_get_pointer (gdk_display_get_default_seat (display)),
                       drag,
                       formats,
                       ctx->surface,
                       GDK_DRAG_PROTO_OLE2);
  drop_win32 = GDK_WIN32_DROP (drop);
  drop_win32->droptarget_w32format_contentformat_map = droptarget_w32format_contentformat_map;
  gdk_content_formats_unref (formats);

  ctx->drop = drop;

  source_actions = set_source_actions_helper (drop,
                                              actions_for_drop_effects (*pdwEffect_and_dwOKEffects),
                                              grfKeyState);

  set_data_object (&ctx->data_object, pDataObj);
  pt_x = pt.x / drop_win32->scale;
  pt_y = pt.y / drop_win32->scale;

  unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
  x /= drop_win32->scale;
  y /= drop_win32->scale;

  gdk_drop_emit_enter_event (drop, TRUE, x, y, GDK_CURRENT_TIME);
  drop_win32->last_key_state = grfKeyState;
  drop_win32->last_x = pt_x;
  drop_win32->last_y = pt_y;
  dest_actions = filter_actions (gdk_drop_get_actions (drop), source_actions);
  *pdwEffect_and_dwOKEffects = drop_effect_for_actions (dest_actions);

  GDK_NOTE (DND, g_print ("idroptarget_dragenter returns S_OK with actions %s"
                          " and drop effect %lu\n",
                          _gdk_win32_drag_action_to_string (dest_actions),
                          *pdwEffect_and_dwOKEffects));

  return S_OK;
}

/* NOTE: This method is called continuously, even if nothing is
 * happening, as long as the drag operation is in progress and
 * the cursor is above our surface.
 * It is OK to return a "safe" dropeffect value (DROPEFFECT_NONE,
 * to indicate that the drop is not possible here), when we
 * do not yet have any real information about acceptability of
 * the drag, because we will have another opportunity to return
 * the "right" value (once we know what it is, after GTK processes
 * the events we emit) very soon.
 */

static HRESULT STDMETHODCALLTYPE
idroptarget_dragover (LPDROPTARGET This,
                      DWORD        grfKeyState,
                      POINTL       pt,
                      LPDWORD      pdwEffect_and_dwOKEffects)
{
  drop_target_context *ctx = (drop_target_context *) This;
  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (ctx->drop);
  int pt_x = pt.x / drop_win32->scale;
  int pt_y = pt.y / drop_win32->scale;
  GdkDragAction source_actions;
  GdkDragAction dest_actions;

  source_actions = actions_for_drop_effects (*pdwEffect_and_dwOKEffects);

  GDK_NOTE (DND, g_print ("idroptarget_dragover %p @ %d : %d"
                          " (raw %ld : %ld)"
                          ", dwOKEffects = %lu"
                          ", suggests %d action\n",
                          This, pt_x, pt_y,
                          pt.x, pt.y,
                          *pdwEffect_and_dwOKEffects,
                          source_actions));

  if (pt_x != drop_win32->last_x ||
      pt_y != drop_win32->last_y ||
      grfKeyState != drop_win32->last_key_state ||
      source_actions != drop_win32->actions)
    {
      double x = 0.0;
      double y = 0.0;

      unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
      x /= drop_win32->scale;
      y /= drop_win32->scale;

      set_source_actions_helper (ctx->drop, source_actions, grfKeyState);

      gdk_drop_emit_motion_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);
      drop_win32->last_key_state = grfKeyState;
      drop_win32->last_x = pt_x;
      drop_win32->last_y = pt_y;
    }

  dest_actions = filter_actions (gdk_drop_get_actions (ctx->drop), source_actions);
  *pdwEffect_and_dwOKEffects = drop_effect_for_actions (dest_actions);

  GDK_NOTE (DND, g_print ("idroptarget_dragover returns S_OK with actions %s"
                          " and effect %lu\n",
                          _gdk_win32_drag_action_to_string (dest_actions),
                          *pdwEffect_and_dwOKEffects));

  return S_OK;
}

static HRESULT STDMETHODCALLTYPE
idroptarget_dragleave (LPDROPTARGET This)
{
  drop_target_context *ctx = (drop_target_context *) This;

  GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n"This));

  gdk_drop_emit_leave_event (GDK_DROP (ctx->drop), TRUE, GDK_CURRENT_TIME);

  g_clear_object (&ctx->drop);
  set_data_object (&ctx->data_object, NULL);

  return S_OK;
}

static HRESULT STDMETHODCALLTYPE
idroptarget_drop (LPDROPTARGET This,
                  LPDATAOBJECT pDataObj,
                  DWORD        grfKeyState,
                  POINTL       pt,
                  LPDWORD      pdwEffect_and_dwOKEffects)
{
  drop_target_context *ctx = (drop_target_context *) This;
  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (ctx->drop);
  double x = 0.0;
  double y = 0.0;
  GdkDragAction dest_action;

  GDK_NOTE (DND, g_print ("idroptarget_drop %p "This));

  if (pDataObj == NULL)
    {
      GDK_NOTE (DND, g_print ("E_POINTER\n"));
      gdk_drop_emit_leave_event (ctx->drop, TRUE, GDK_CURRENT_TIME);
      g_clear_object (&ctx->drop);
      set_data_object (&ctx->data_object, NULL);

      return E_POINTER;
    }

  set_source_actions_helper (ctx->drop,
                             actions_for_drop_effects (*pdwEffect_and_dwOKEffects),
                             grfKeyState);

  drop_win32->drop_finished = FALSE;

  unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
  x /= drop_win32->scale;
  y /= drop_win32->scale;

  gdk_drop_emit_motion_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);

  gdk_drop_emit_drop_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);

  gdk_drop_emit_leave_event (ctx->drop, TRUE, GDK_CURRENT_TIME);

  if (drop_win32->actions == GDK_ACTION_NONE)
    {
      g_clear_object (&ctx->drop);
      set_data_object (&ctx->data_object, NULL);
      return E_UNEXPECTED;
    }

  while (!drop_win32->drop_finished)
    g_main_context_iteration (NULL, FALSE);

  /* Notify OLE of the DnD result
   * Special case:
   * drop_win32->actions is guaranteed to contain 1 action after gdk_drop_finish ()
   */

  dest_action = drop_win32->actions;
  *pdwEffect_and_dwOKEffects = drop_effect_for_actions (dest_action);

  g_clear_object (&ctx->drop);
  set_data_object (&ctx->data_object, NULL);

  GDK_NOTE (DND, g_print ("drop S_OK with effect %lx\n", *pdwEffect_and_dwOKEffects));

  return S_OK;
}

static IDropTargetVtbl idt_vtbl = {
  idroptarget_queryinterface,
  idroptarget_addref,
  idroptarget_release,
  idroptarget_dragenter,
  idroptarget_dragover,
  idroptarget_dragleave,
  idroptarget_drop
};

static drop_target_context *
target_context_new (GdkSurface *surface)
{
  drop_target_context *result;

  result = g_new0 (drop_target_context, 1);
  result->idt.lpVtbl = &idt_vtbl;
  result->ref_count = 0;

  result->surface = surface;

  idroptarget_addref (&result->idt);

  GDK_NOTE (DND, g_print ("target_context_new: %p (surface %p)\n", result, result->surface));

  return result;
}

#if 0

/* From MS Knowledge Base article Q130698 */

static gboolean
resolve_link (HWND     hWnd,
              wchar_t *link,
              char   **lpszPath)
{
  WIN32_FILE_ATTRIBUTE_DATA wfad;
  HRESULT hr;
  IShellLinkW *pslW = NULL;
  IPersistFile *ppf = NULL;

  /* Check if the file is empty first because IShellLink::Resolve for
   * some reason succeeds with an empty file and returns an empty
   * "link target". (#524151)
   */

    if (!GetFileAttributesExW (link, GetFileExInfoStandard, &wfad) ||
        (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
      return FALSE;

  /* Assume failure to start with: */
  *lpszPath = 0;

  /* Call CoCreateInstance to obtain the IShellLink interface
   * pointer. This call fails if CoInitialize is not called, so it is
   * assumed that CoInitialize has been called.
   */


  hr = CoCreateInstance (&CLSID_ShellLink,
                         NULL,
                         CLSCTX_INPROC_SERVER,
                         &IID_IShellLinkW,
                         (LPVOID *)&pslW);

  if (SUCCEEDED (hr))
   {
     /* The IShellLink interface supports the IPersistFile
      * interface. Get an interface pointer to it.
      */

     hr = pslW->lpVtbl->QueryInterface (pslW,
                                        &IID_IPersistFile,
                                        (LPVOID *) &ppf);
   }

  if (SUCCEEDED (hr))
    {
      /* Load the file. */
      hr = ppf->lpVtbl->Load (ppf, link, STGM_READ);
    }

  if (SUCCEEDED (hr))
    {
      /* Resolve the link by calling the Resolve()
       * interface function.
       */

      hr = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
    }

  if (SUCCEEDED (hr))
    {
      wchar_t wtarget[MAX_PATH];

      hr = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
      if (SUCCEEDED (hr))
        *lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
    }

  if (ppf)
    ppf->lpVtbl->Release (ppf);

  if (pslW)
    pslW->lpVtbl->Release (pslW);

  return SUCCEEDED (hr);
}

/* Check for filenames like C:\Users\tml\AppData\Local\Temp\d5qtkvvs.bmp */
static gboolean
filename_looks_tempish (const char *filename)
{
  char *dirname;
  char *p;
  const char *q;
  gboolean retval = FALSE;

  dirname = g_path_get_dirname (filename);

  p = dirname;
  q = g_get_tmp_dir ();

  while (*p && *q &&
         ((G_IS_DIR_SEPARATOR (*p) && G_IS_DIR_SEPARATOR (*q)) ||
          g_ascii_tolower (*p) == g_ascii_tolower (*q)))
    p++, q++;

  if (!*p && !*q)
    retval = TRUE;

  g_free (dirname);

  return retval;
}

static gboolean
close_it (gpointer data)
{
  close (GPOINTER_TO_INT (data));

  return FALSE;
}

#endif

static void
gdk_win32_drop_status (GdkDrop       *drop,
                       GdkDragAction  actions,
                       GdkDragAction  preferred)
{
  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);

  g_return_if_fail (drop != NULL);

  GDK_NOTE (DND, g_print ("gdk_win32_drop_status: %s\n"
                          " context=%p:{source_actions=%s, preferred=%s}\n",
                          _gdk_win32_drag_action_to_string (actions),
                          drop,
                          _gdk_win32_drag_action_to_string (gdk_drop_get_actions (drop)),
                          _gdk_win32_drag_action_to_string (preferred)));

  if (preferred != 0)
    actions = preferred;

  gdk_drop_set_actions (drop, drop_win32->actions & actions);
}

static void
gdk_win32_drop_finish (GdkDrop       *drop,
                       GdkDragAction  action)
{
  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);

  g_return_if_fail (drop != NULL);

  GDK_NOTE (DND, g_print ("gdk_win32_drop_finish with action %s\n",
                          _gdk_win32_drag_action_to_string (action)));

  drop_win32->actions = action;
  drop_win32->drop_finished = TRUE;
}

#if 0

static GdkFilterReturn
gdk_destroy_filter (GdkXEvent *xev,
                    GdkEvent  *event,
                    gpointer   data)
{
  MSG *msg = (MSG *) xev;

  if (msg->message == WM_DESTROY)
    {
      IDropTarget *idtp = (IDropTarget *) data;

      GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
#if 0
      idtp->lpVtbl->Release (idtp);
#endif
      RevokeDragDrop (msg->hwnd);
      CoLockObjectExternal ((IUnknown*) idtp, FALSETRUE);
    }
  return GDK_FILTER_CONTINUE;
}

#endif

void
_gdk_win32_surface_register_dnd (GdkSurface *surface)
{
  drop_target_context *ctx;
  HRESULT hr;
  GdkWin32Surface *impl;

  g_return_if_fail (surface != NULL);

  if (g_object_get_data (G_OBJECT (surface), "gdk-dnd-registered") != NULL)
    return;
  else
    g_object_set_data (G_OBJECT (surface), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));

  GDK_NOTE (DND, g_print ("gdk_win32_surface_register_dnd: %p\n", GDK_SURFACE_HWND (surface)));

  impl = GDK_WIN32_SURFACE (surface);

  /* Return if surface is already setup for DND. */
  if (impl->drop_target != NULL)
    return;

  ctx = target_context_new (surface);

  hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUEFALSE);
  if (!SUCCEEDED (hr))
    OTHER_API_FAILED ("CoLockObjectExternal");
  else
    {
      hr = RegisterDragDrop (GDK_SURFACE_HWND (surface), &ctx->idt);
      if (hr == DRAGDROP_E_ALREADYREGISTERED)
        {
          g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
          CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSEFALSE);
        }
      else if (!SUCCEEDED (hr))
        OTHER_API_FAILED ("RegisterDragDrop");
      else
        {
          impl->drop_target = ctx;
        }
    }
}

void
_gdk_win32_surface_unregister_dnd (GdkSurface *surface)
{
  GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);

  if (impl->drop_target)
    idroptarget_release (&impl->drop_target->idt);
}

static gpointer
grab_data_from_hdata (GTask  *task,
                      HANDLE  hdata,
                      gsize  *data_len)
{
  gpointer ptr;
  SIZE_T length;
  guchar *data;

  ptr = GlobalLock (hdata);
  if (ptr == NULL)
    {
      DWORD error_code = GetLastError ();
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Cannot get DnD data. GlobalLock(0x%p) failed: 0x%lx."), hdata, error_code);
      return NULL;
    }

  length = GlobalSize (hdata);
  if (length == 0 && GetLastError () != NO_ERROR)
    {
      DWORD error_code = GetLastError ();
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Cannot get DnD data. GlobalSize(0x%p) failed: 0x%lx."), hdata, error_code);
      GlobalUnlock (hdata);
      return NULL;
    }

  data = g_try_malloc (length);

  if (data == NULL)
    {
      char *length_str = g_strdup_printf ("%" G_GSIZE_FORMAT, length);
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Cannot get DnD data. Failed to allocate %s bytes to store the data."), length_str);
      g_free (length_str);
      GlobalUnlock (hdata);
      return NULL;
    }

  memcpy (data, ptr, length);
  *data_len = length;

  GlobalUnlock (hdata);

  return data;
}

static void
gdk_win32_drop_read_async (GdkDrop             *drop,
                           GdkContentFormats   *formats,
                           int                  io_priority,
                           GCancellable        *cancellable,
                           GAsyncReadyCallback  callback,
                           gpointer             user_data)
{
  GdkWin32Drop              *drop_win32 = GDK_WIN32_DROP (drop);
  GTask                     *task;
  drop_target_context       *tctx;
  const char * const        *mime_types;
  gsize                      i, j, n_mime_types;
  GdkWin32ContentFormatPair *pair;
  FORMATETC                  fmt;
  HRESULT                    hr;
  STGMEDIUM                  storage;
  guchar                    *data;
  gsize                      data_len;
  GInputStream              *stream;

  task = g_task_new (drop, cancellable, callback, user_data);
  g_task_set_priority (task, io_priority);
  g_task_set_source_tag (task, gdk_win32_drop_read_async);

  mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types);

  if (drop_win32->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
    {
      for (i = 0; i < n_mime_types; i++)
        if (g_strcmp0 (mime_types[i], "text/uri-list") == 0)
          break;
      if (i >= n_mime_types)
        {
          g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                                   _("No compatible transfer format found"));
          g_object_unref (task);
          g_clear_pointer (&drop_win32->dropfiles_list, g_free);

          return;
        }

      stream = g_memory_input_stream_new_from_data (drop_win32->dropfiles_list, strlen (drop_win32->dropfiles_list), g_free);
      drop_win32->dropfiles_list = NULL;
      g_object_set_data (G_OBJECT (stream), "gdk-dnd-stream-contenttype", (gpointer) "text/uri-list");
      g_task_return_pointer (task, stream, g_object_unref);
      g_object_unref (task);

      return;
    }

  tctx = GDK_WIN32_SURFACE (gdk_drop_get_surface (drop))->drop_target;

  if (tctx == NULL)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("GDK surface 0x%p is not registered as a drop target"), gdk_drop_get_surface (drop));
      g_object_unref (task);
      return;
    }

  if (tctx->data_object == NULL)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Target context record 0x%p has no data object"), tctx);
      g_object_unref (task);
      return;
    }

  for (pair = NULL, i = 0; i < n_mime_types; i++)
    {
      for (j = 0; j < drop_win32->droptarget_w32format_contentformat_map->len; j++)
        {
          pair = &g_array_index (drop_win32->droptarget_w32format_contentformat_map, GdkWin32ContentFormatPair, j);
          if (pair->contentformat == mime_types[i])
            break;

          pair = NULL;
        }
    }

  if (pair == NULL)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                               _("No compatible transfer format found"));
      g_object_unref (task);
      return;
    }

  fmt.cfFormat = pair->w32format;
  if (_gdk_win32_format_uses_hdata (pair->w32format))
    fmt.tymed = TYMED_HGLOBAL;
  else
    g_assert_not_reached ();

  fmt.ptd = NULL;
  fmt.dwAspect = DVASPECT_CONTENT;
  fmt.lindex = -1;

  hr = IDataObject_GetData (tctx->data_object, &fmt, &storage);

  if (!SUCCEEDED (hr) || hr != S_OK)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("IDataObject_GetData (0x%x) failed, returning 0x%lx"), fmt.cfFormat, hr);
      g_object_unref (task);
      return;
    }

  if (!pair->transmute)
    {
      if (_gdk_win32_format_uses_hdata (pair->w32format))
        {
          data = grab_data_from_hdata (task, storage.hGlobal, &data_len);

          if (data == NULL)
            {
              ReleaseStgMedium (&storage);
              g_object_unref (task);

              return;
            }
        }
      else
        {
          g_assert_not_reached ();
        }
    }
  else
    {
      GdkDisplay *display = gdk_drop_get_display (drop);
      gdk_win32_clipdrop_transmute_windows_data (gdk_win32_display_get_clipdrop (display),
                                                 pair->w32format, pair->contentformat,
                                                 storage.hGlobal,
                                                 &data,
                                                 &data_len);
    }

  ReleaseStgMedium (&storage);

  if (data == NULL)
    {
      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
                               _("Failed to transmute DnD data W32 format 0x%x to %p (%s)"), pair->w32format, pair->contentformat, pair->contentformat);
      g_object_unref (task);
      return;
    }

  stream = g_memory_input_stream_new_from_data (data, data_len, g_free);
  g_object_set_data (G_OBJECT (stream), "gdk-dnd-stream-contenttype", (gpointer) pair->contentformat);
  g_task_return_pointer (task, stream, g_object_unref);
  g_object_unref (task);
}

static GInputStream *
gdk_win32_drop_read_finish (GdkDrop       *drop,
                            GAsyncResult  *result,
                            const char   **out_mime_type,
                            GError       **error)
{
  GTask *task;
  GInputStream *stream;

  g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL);
  task = G_TASK (result);
  g_return_val_if_fail (g_task_get_source_tag (task) == gdk_win32_drop_read_async, NULL);

  stream = g_task_propagate_pointer (task, error);

  if (stream && out_mime_type)
    *out_mime_type = g_object_get_data (G_OBJECT (stream), "gdk-dnd-stream-contenttype");

  return stream;
}

static void
gdk_win32_drop_class_init (GdkWin32DropClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GdkDropClass *drop_class = GDK_DROP_CLASS (klass);

  object_class->finalize = gdk_win32_drop_finalize;

  drop_class->status = gdk_win32_drop_status;
  drop_class->finish = gdk_win32_drop_finish;
  drop_class->read_async = gdk_win32_drop_read_async;
  drop_class->read_finish = gdk_win32_drop_read_finish;
}

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.46 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik