~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/widget/src/windows/nsDragService.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Netscape Public License
 
6
 * Version 1.1 (the "License"); you may not use this file except in
 
7
 * compliance with the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/NPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is mozilla.org code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is 
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 1998
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *   Mike Pinkerton (pinkerton@netscape.com)
 
24
 *   Mark Hammond (MarkH@ActiveState.com)
 
25
 *
 
26
 * Alternatively, the contents of this file may be used under the terms of
 
27
 * either the GNU General Public License Version 2 or later (the "GPL"), or 
 
28
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
29
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
30
 * of those above. If you wish to allow use of your version of this file only
 
31
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
32
 * use your version of this file under the terms of the NPL, indicate your
 
33
 * decision by deleting the provisions above and replace them with the notice
 
34
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
35
 * the provisions above, a recipient may use your version of this file under
 
36
 * the terms of any one of the NPL, the GPL or the LGPL.
 
37
 *
 
38
 * ***** END LICENSE BLOCK ***** */
 
39
 
 
40
#include "nsDragService.h"
 
41
#include "nsITransferable.h"
 
42
#include "nsDataObj.h"
 
43
 
 
44
#include "nsWidgetsCID.h"
 
45
#include "nsNativeDragTarget.h"
 
46
#include "nsNativeDragSource.h"
 
47
#include "nsClipboard.h"
 
48
#include "nsISupportsArray.h"
 
49
#include "nsDataObjCollection.h"
 
50
 
 
51
#include "nsAutoPtr.h"
 
52
 
 
53
#include <ole2.h>
 
54
#include <oleidl.h>
 
55
#include <shlobj.h>
 
56
 
 
57
// shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
 
58
#include <shellapi.h>
 
59
 
 
60
 
 
61
//-------------------------------------------------------------------------
 
62
//
 
63
// DragService constructor
 
64
//
 
65
//-------------------------------------------------------------------------
 
66
nsDragService::nsDragService()
 
67
{
 
68
  mNativeDragTarget = nsnull;
 
69
  mNativeDragSrc    = nsnull;
 
70
  mDataObject       = nsnull;
 
71
}
 
72
 
 
73
//-------------------------------------------------------------------------
 
74
//
 
75
// DragService destructor
 
76
//
 
77
//-------------------------------------------------------------------------
 
78
nsDragService::~nsDragService()
 
79
{
 
80
  NS_IF_RELEASE(mNativeDragSrc);
 
81
  NS_IF_RELEASE(mNativeDragTarget);
 
82
  NS_IF_RELEASE(mDataObject);
 
83
}
 
84
 
 
85
 
 
86
//-------------------------------------------------------------------------
 
87
NS_IMETHODIMP nsDragService::InvokeDragSession (nsIDOMNode *aDOMNode, nsISupportsArray * anArrayTransferables, nsIScriptableRegion * aRegion, PRUint32 aActionType)
 
88
{
 
89
  nsBaseDragService::InvokeDragSession ( aDOMNode, anArrayTransferables, aRegion, aActionType );
 
90
  
 
91
  nsresult rv;
 
92
  PRUint32 numItemsToDrag = 0;
 
93
  rv = anArrayTransferables->Count(&numItemsToDrag);
 
94
  if ( !numItemsToDrag )
 
95
    return NS_ERROR_FAILURE;
 
96
 
 
97
  // The clipboard class contains some static utility methods
 
98
  // that we can use to create an IDataObject from the transferable
 
99
 
 
100
  // if we're dragging more than one item, we need to create a "collection" object to fake out
 
101
  // the OS. This collection contains one |IDataObject| for each transerable. If there is just
 
102
  // the one (most cases), only pass around the native |IDataObject|.
 
103
  nsRefPtr<IDataObject> itemToDrag;
 
104
  if ( numItemsToDrag > 1 ) {
 
105
    nsDataObjCollection * dataObjCollection = new nsDataObjCollection();
 
106
    if (!dataObjCollection)
 
107
      return NS_ERROR_OUT_OF_MEMORY;
 
108
    itemToDrag = dataObjCollection;
 
109
    for ( PRUint32 i=0; i<numItemsToDrag; ++i ) {
 
110
      nsCOMPtr<nsISupports> supports;
 
111
      anArrayTransferables->GetElementAt(i, getter_AddRefs(supports));
 
112
      nsCOMPtr<nsITransferable> trans(do_QueryInterface(supports));
 
113
      if ( trans ) {
 
114
        nsRefPtr<IDataObject> dataObj;
 
115
        if ( NS_SUCCEEDED(nsClipboard::CreateNativeDataObject(trans, getter_AddRefs(dataObj))) ) {
 
116
          dataObjCollection->AddDataObject(dataObj);
 
117
        }
 
118
        else
 
119
          return NS_ERROR_FAILURE;
 
120
      }
 
121
    }
 
122
  } // if dragging multiple items
 
123
  else {
 
124
    nsCOMPtr<nsISupports> supports;
 
125
    anArrayTransferables->GetElementAt(0, getter_AddRefs(supports));
 
126
    nsCOMPtr<nsITransferable> trans(do_QueryInterface(supports));
 
127
    if ( trans ) {
 
128
      if ( NS_FAILED(nsClipboard::CreateNativeDataObject(trans, getter_AddRefs(itemToDrag))) )
 
129
        return NS_ERROR_FAILURE;
 
130
    }
 
131
  } // else dragging a single object
 
132
  
 
133
  return StartInvokingDragSession ( itemToDrag, aActionType );
 
134
}
 
135
 
 
136
//-------------------------------------------------------------------------
 
137
NS_IMETHODIMP nsDragService::StartInvokingDragSession(IDataObject * aDataObj, PRUint32 aActionType)
 
138
{
 
139
  // To do the drag we need to create an object that 
 
140
  // implements the IDataObject interface (for OLE)
 
141
  NS_IF_RELEASE(mNativeDragSrc);
 
142
  mNativeDragSrc = (IDropSource *)new nsNativeDragSource();
 
143
  if ( !mNativeDragSrc )
 
144
    return NS_ERROR_OUT_OF_MEMORY;
 
145
    
 
146
  mNativeDragSrc->AddRef();
 
147
 
 
148
  // Now figure out what the native drag effect should be
 
149
  DWORD dropRes;
 
150
  DWORD effects = DROPEFFECT_SCROLL;
 
151
  if (aActionType & DRAGDROP_ACTION_COPY) {
 
152
    effects |= DROPEFFECT_COPY;
 
153
  }
 
154
  if (aActionType & DRAGDROP_ACTION_MOVE) {
 
155
    effects |= DROPEFFECT_MOVE;
 
156
  }
 
157
  if (aActionType & DRAGDROP_ACTION_LINK) {
 
158
    effects |= DROPEFFECT_LINK;
 
159
  }
 
160
 
 
161
  mDragAction = aActionType;      //XXX not sure why we bother to cache this, it can change during the drag
 
162
  mDoingDrag  = PR_TRUE;
 
163
 
 
164
  // Call the native D&D method
 
165
  HRESULT res = ::DoDragDrop(aDataObj, mNativeDragSrc, effects, &dropRes);
 
166
 
 
167
  // For some drag/drop interactions, IDataObject::SetData doesn't get called with
 
168
  // a CFSTR_PERFORMEDDROPEFFECT format and the intermediate file (if it was created)
 
169
  // isn't deleted.  See http://bugzilla.mozilla.org/show_bug.cgi?id=203847#c4 for a
 
170
  // detailed description of the different cases.  Now that we know that the drag/drop 
 
171
  // operation has ended, call SetData() so that the intermediate file is deleted.
 
172
  static CLIPFORMAT PerformedDropEffect = ::RegisterClipboardFormat( CFSTR_PERFORMEDDROPEFFECT );
 
173
  FORMATETC fmte = {(CLIPFORMAT) PerformedDropEffect, NULL, DVASPECT_CONTENT, -1, TYMED_NULL};
 
174
  STGMEDIUM medium;
 
175
  medium.tymed = TYMED_NULL;
 
176
  medium.pUnkForRelease = NULL;
 
177
  aDataObj->SetData(&fmte, &medium, FALSE);
 
178
 
 
179
  mDoingDrag  = PR_FALSE;
 
180
 
 
181
  return (DRAGDROP_S_DROP == res?NS_OK:NS_ERROR_FAILURE);
 
182
}
 
183
 
 
184
//-------------------------------------------------------------------------
 
185
// Make Sure we have the right kind of object
 
186
nsDataObjCollection* nsDragService::GetDataObjCollection(IDataObject* aDataObj)
 
187
{
 
188
  nsDataObjCollection * dataObjCol = nsnull;
 
189
  if (aDataObj) {
 
190
    nsIDataObjCollection* dataObj;
 
191
    if (aDataObj->QueryInterface(IID_IDataObjCollection, (void**)&dataObj) == S_OK) {
 
192
      dataObjCol = NS_STATIC_CAST(nsDataObjCollection*, aDataObj);
 
193
      dataObj->Release();
 
194
    }
 
195
  }
 
196
 
 
197
  return dataObjCol;
 
198
}
 
199
 
 
200
//-------------------------------------------------------------------------
 
201
NS_IMETHODIMP nsDragService::GetNumDropItems (PRUint32 * aNumItems)
 
202
{
 
203
  if ( !mDataObject ) {
 
204
    *aNumItems = 0;
 
205
    return NS_OK;
 
206
  }
 
207
 
 
208
  if ( IsCollectionObject(mDataObject) ) {
 
209
    
 
210
    nsDataObjCollection * dataObjCol = GetDataObjCollection(mDataObject);
 
211
    if ( dataObjCol )
 
212
      *aNumItems = dataObjCol->GetNumDataObjects();
 
213
  }
 
214
  else {
 
215
    // Next check if we have a file drop. Return the number of files in
 
216
    // the file drop as the number of items we have, pretending like we
 
217
    // actually have > 1 drag item.
 
218
    FORMATETC fe2;
 
219
    SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
 
220
    if ( mDataObject->QueryGetData(&fe2) == S_OK ) {
 
221
      STGMEDIUM stm;
 
222
      if ( mDataObject->GetData(&fe2, &stm) == S_OK ) {      
 
223
        HDROP hdrop = (HDROP) GlobalLock(stm.hGlobal);
 
224
        *aNumItems = ::DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
 
225
        ::GlobalUnlock(stm.hGlobal);
 
226
        ::ReleaseStgMedium(&stm);
 
227
      }
 
228
    }
 
229
    else
 
230
      *aNumItems = 1;
 
231
  }
 
232
 
 
233
  return NS_OK;
 
234
}
 
235
 
 
236
//-------------------------------------------------------------------------
 
237
NS_IMETHODIMP nsDragService::GetData (nsITransferable * aTransferable, PRUint32 anItem)
 
238
{
 
239
  // This typcially happens on a drop, the target would be asking
 
240
  // for it's transferable to be filled in
 
241
  // Use a static clipboard utility method for this
 
242
  if ( !mDataObject )
 
243
    return NS_ERROR_FAILURE;
 
244
 
 
245
  nsresult dataFound = NS_ERROR_FAILURE;
 
246
 
 
247
  if ( IsCollectionObject(mDataObject) ) {
 
248
    // multiple items, use |anItem| as an index into our collection
 
249
    nsDataObjCollection * dataObjCol = GetDataObjCollection(mDataObject);
 
250
    PRUint32 cnt = dataObjCol->GetNumDataObjects();   
 
251
    if (anItem >= 0 && anItem < cnt) {
 
252
      IDataObject * dataObj = dataObjCol->GetDataObjectAt(anItem);
 
253
      dataFound = nsClipboard::GetDataFromDataObject(dataObj, 0, nsnull, aTransferable);
 
254
    }
 
255
    else
 
256
      NS_WARNING ( "Index out of range!" );
 
257
  }
 
258
  else {
 
259
    // If they are asking for item "0", we can just get it...
 
260
    if (anItem == 0) 
 
261
       dataFound = nsClipboard::GetDataFromDataObject(mDataObject, anItem, nsnull, aTransferable);
 
262
    else {
 
263
      // It better be a file drop, or else non-zero indexes are invalid!
 
264
      FORMATETC fe2;
 
265
      SET_FORMATETC(fe2, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
 
266
      if ( mDataObject->QueryGetData(&fe2) == S_OK )
 
267
        dataFound = nsClipboard::GetDataFromDataObject(mDataObject, anItem, nsnull, aTransferable);
 
268
      else
 
269
        NS_WARNING ( "Reqesting non-zero index, but clipboard data is not a collection!" );
 
270
    }
 
271
  }
 
272
  return dataFound;
 
273
}
 
274
 
 
275
//---------------------------------------------------------
 
276
NS_IMETHODIMP nsDragService::SetIDataObject (IDataObject * aDataObj)
 
277
{
 
278
  // When the native drag starts the DragService gets 
 
279
  // the IDataObject that is being dragged
 
280
  NS_IF_RELEASE(mDataObject);
 
281
  mDataObject = aDataObj;
 
282
  NS_IF_ADDREF(mDataObject);
 
283
 
 
284
  return NS_OK;
 
285
}
 
286
 
 
287
//-------------------------------------------------------------------------
 
288
NS_IMETHODIMP nsDragService::IsDataFlavorSupported(const char *aDataFlavor, PRBool *_retval)
 
289
{
 
290
  if ( !aDataFlavor || !mDataObject || !_retval )
 
291
    return NS_ERROR_FAILURE;
 
292
 
 
293
#ifdef NS_DEBUG
 
294
  if ( strcmp(aDataFlavor, kTextMime) == 0 )
 
295
    NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
 
296
#endif
 
297
 
 
298
  *_retval = PR_FALSE;
 
299
 
 
300
  FORMATETC fe;
 
301
  UINT format = 0;
 
302
  
 
303
  if ( IsCollectionObject(mDataObject) ) {
 
304
    // We know we have one of our special collection objects.
 
305
    format = nsClipboard::GetFormat(aDataFlavor);
 
306
    SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
 
307
 
 
308
    // See if any one of the IDataObjects in the collection supports this data type
 
309
    nsDataObjCollection* dataObjCol = GetDataObjCollection(mDataObject);
 
310
    if ( dataObjCol ) {
 
311
      PRUint32 cnt = dataObjCol->GetNumDataObjects();
 
312
      for (PRUint32 i=0;i<cnt;++i) {
 
313
        IDataObject * dataObj = dataObjCol->GetDataObjectAt(i);
 
314
        if (S_OK == dataObj->QueryGetData(&fe))
 
315
          *_retval = PR_TRUE;             // found it!
 
316
      }
 
317
    }
 
318
  } // if special collection object
 
319
  else {
 
320
    // Ok, so we have a single object. Check to see if has the correct data type. Since
 
321
    // this can come from an outside app, we also need to see if we need to perform
 
322
    // text->unicode conversion if the client asked for unicode and it wasn't available.
 
323
    format = nsClipboard::GetFormat(aDataFlavor);
 
324
    SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
 
325
    if ( mDataObject->QueryGetData(&fe) == S_OK )
 
326
      *_retval = PR_TRUE;                 // found it!
 
327
    else {
 
328
      // We haven't found the exact flavor the client asked for, but maybe we can
 
329
      // still find it from something else that's on the clipboard
 
330
      if ( strcmp(aDataFlavor, kUnicodeMime) == 0 ) {
 
331
        // client asked for unicode and it wasn't present, check if we have CF_TEXT.
 
332
        // We'll handle the actual data substitution in the data object.
 
333
        format = nsClipboard::GetFormat(kTextMime);
 
334
        SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
 
335
        if ( mDataObject->QueryGetData(&fe) == S_OK )
 
336
          *_retval = PR_TRUE;                 // found it!
 
337
      }
 
338
      else if ( strcmp(aDataFlavor, kURLMime) == 0 ) {
 
339
        // client asked for a url and it wasn't present, but if we have a file, then
 
340
        // we have a URL to give them (the path, or the internal URL if an InternetShortcut).
 
341
        format = nsClipboard::GetFormat(kFileMime);
 
342
        SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_FILE | TYMED_GDI);
 
343
        if ( mDataObject->QueryGetData(&fe) == S_OK )
 
344
          *_retval = PR_TRUE;                 // found it!
 
345
      }
 
346
    } // else try again
 
347
  }
 
348
 
 
349
  return NS_OK;
 
350
}
 
351
 
 
352
 
 
353
//
 
354
// IsCollectionObject
 
355
//
 
356
// Determine if this is a single |IDataObject| or one of our private collection
 
357
// objects. We know the difference because our collection object will respond to supporting
 
358
// the private |MULTI_MIME| format.
 
359
//
 
360
PRBool
 
361
nsDragService :: IsCollectionObject ( IDataObject* inDataObj )
 
362
{
 
363
  PRBool isCollection = PR_FALSE;
 
364
  
 
365
  // setup the format object to ask for the MULTI_MIME format. We only need to do this once
 
366
  static UINT sFormat = 0;
 
367
  static FORMATETC sFE;
 
368
  if ( !sFormat ) {
 
369
    sFormat = nsClipboard::GetFormat(MULTI_MIME);
 
370
    SET_FORMATETC(sFE, sFormat, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
 
371
  }
 
372
  
 
373
  // ask the object if it supports it. If yes, we have a collection object
 
374
  if ( inDataObj->QueryGetData(&sFE) == S_OK )
 
375
    isCollection = PR_TRUE; 
 
376
 
 
377
  return isCollection;
 
378
 
 
379
} // IsCollectionObject
 
380
 
 
381
 
 
382
//
 
383
// EndDragSession
 
384
//
 
385
// Override the default to make sure that we release the data object when the drag ends. It
 
386
// seems that OLE doesn't like to let apps quit w/out crashing when we're still holding onto 
 
387
// their data
 
388
//
 
389
NS_IMETHODIMP
 
390
nsDragService::EndDragSession ()
 
391
{
 
392
  nsBaseDragService::EndDragSession();
 
393
  NS_IF_RELEASE(mDataObject);
 
394
 
 
395
  return NS_OK;
 
396
}