r=mkaply, sr=blizzard (platform specific), a=mkaply
OS/2 only - new drag drop code from Rich Walsh. Good stuff.
This commit is contained in:
mkaply%us.ibm.com 2003-08-25 22:54:06 +00:00
Родитель 1855e5ef0f
Коммит b41bd3ece7
4 изменённых файлов: 599 добавлений и 262 удалений

Просмотреть файл

@ -19,6 +19,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Rich Walsh <dragtext@e-vertise.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -51,7 +52,6 @@
// helper functions
void WriteTypeEA(const char *filename, const char *type);
nsresult GetURLObjectContents(const char *pszPath, char **ppszURL);
int UnicodeToCodepage( const nsAString& inString, char **outText);
// --------------------------------------------------------------------------
@ -93,79 +93,91 @@ MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
switch (msg) {
// if the user requests the contents of a URL be rendered (vs the URL
// itself), change the suggested target name from the URL's title to
// the name of the file that will be retrieved
case DM_RENDERPREPARE: {
pdragtransfer = (PDRAGTRANSFER)mp1;
dragservice = (nsDragService*)pdragtransfer->pditem->ulItemID;
if (pdragtransfer->usOperation == DO_COPY &&
!strcmp(dragservice->mMimeType, kURLMime)) {
nsCOMPtr<nsIURL> linkURL(do_QueryInterface(dragservice->mSourceData));
nsCAutoString filename;
linkURL->GetFileName(filename);
if (filename.IsEmpty()) {
linkURL->GetHost(filename);
filename.Append("/file");
}
DrgDeleteStrHandle(pdragtransfer->pditem->hstrTargetName);
pdragtransfer->pditem->hstrTargetName = DrgAddStrHandle(ToNewCString(filename));
}
return (MRESULT)TRUE;
}
case DM_RENDER: {
pdragtransfer = (PDRAGTRANSFER)mp1;
dragservice = (nsDragService*)pdragtransfer->pditem->ulItemID;
DrgQueryStrName(pdragtransfer->hstrRenderToName, CCHMAXPATH, chPath);
BOOL ok = FALSE;
// if we're dragging a URL, render either the URL itself or the file
// it references if the user Ctrl-dropped
if (dragservice->mMimeType) {
if (!strcmp(dragservice->mMimeType, kURLMime)) {
// if the user requests the contents of a URL be rendered (vs the URL
// itself), change the suggested target name from the URL's title to
// the name of the file that will be retrieved
case DM_RENDERPREPARE: {
pdragtransfer = (PDRAGTRANSFER)mp1;
dragservice = (nsDragService*)pdragtransfer->pditem->ulItemID;
if (pdragtransfer->usOperation == DO_COPY &&
!strcmp(dragservice->mMimeType, kURLMime)) {
nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
if (urlObject) {
nsCAutoString strUrl;
urlObject->GetSpec(strUrl);
if (pdragtransfer->usOperation == DO_COPY)
dragservice->WriteData(chPath, strUrl.get());
else {
fp = fopen(chPath, "wb+");
fwrite(strUrl.get(), strUrl.Length(), 1, fp);
fclose(fp);
WriteTypeEA(chPath, "UniformResourceLocator");
nsCAutoString filename;
urlObject->GetFileName(filename);
if (filename.IsEmpty()) {
urlObject->GetHost(filename);
filename.Append("/file");
}
ok = TRUE;
DrgDeleteStrHandle(pdragtransfer->pditem->hstrTargetName);
pdragtransfer->pditem->hstrTargetName = DrgAddStrHandle(ToNewCString(filename));
}
}
else
// if we're dragging text, do NLS conversion then write it to file
if (!strcmp(dragservice->mMimeType, kUnicodeMime)) {
nsCOMPtr<nsISupportsString> strObject(do_QueryInterface(dragservice->mSourceData));
if (strObject) {
nsAutoString strData;
strObject->GetData(strData);
char * pText;
int cnt = UnicodeToCodepage( strData, &pText);
if (cnt) {
return (MRESULT)TRUE;
}
case DM_RENDER: {
pdragtransfer = (PDRAGTRANSFER)mp1;
dragservice = (nsDragService*)pdragtransfer->pditem->ulItemID;
DrgQueryStrName(pdragtransfer->hstrRenderToName, CCHMAXPATH, chPath);
BOOL ok = FALSE;
// if we're dragging a URL, render either the URL itself or the file
// it references if the user Ctrl-dropped; use the nsIURL interface
// to determine if the latter is even possible - if it fails (as it
// will for mailto:), drop into the code that uses nsIURI to render
// a URL object
if (!strcmp(dragservice->mMimeType, kURLMime)) {
if (pdragtransfer->usOperation == DO_COPY) {
nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
if (urlObject) {
nsCAutoString strUrl;
urlObject->GetSpec(strUrl);
dragservice->WriteData(chPath, strUrl.get());
ok = TRUE;
}
}
if (!ok) {
nsCOMPtr<nsIURI> uriObject(do_QueryInterface(dragservice->mSourceData));
if (uriObject) {
nsCAutoString strUri;
uriObject->GetSpec(strUri);
fp = fopen(chPath, "wb+");
fwrite(pText, cnt, 1, fp);
fwrite(strUri.get(), strUri.Length(), 1, fp);
fclose(fp);
WriteTypeEA(chPath, "UniformResourceLocator");
ok = TRUE;
}
}
}
else
// if we're dragging text, do NLS conversion then write it to file
if (!strcmp(dragservice->mMimeType, kUnicodeMime)) {
nsCOMPtr<nsISupportsString> strObject(do_QueryInterface(dragservice->mSourceData));
if (strObject) {
nsAutoString strData;
strObject->GetData(strData);
char * pText;
int cnt = UnicodeToCodepage( strData, &pText);
if (cnt) {
fp = fopen(chPath, "wb+");
fwrite(pText, cnt, 1, fp);
fclose(fp);
ok = TRUE;
}
}
}
DrgPostTransferMsg(pdragtransfer->hwndClient, DM_RENDERCOMPLETE, pdragtransfer,
(ok ? DMFL_RENDEROK : DMFL_RENDERFAIL), 0, TRUE);
DrgFreeDragtransfer(pdragtransfer);
return (MRESULT)TRUE;
}
DrgPostTransferMsg(pdragtransfer->hwndClient, DM_RENDERCOMPLETE, pdragtransfer,
(ok ? DMFL_RENDEROK : DMFL_RENDERFAIL), 0, TRUE);
DrgFreeDragtransfer(pdragtransfer);
return (MRESULT)TRUE;
}
default:
break;
default:
break;
}
return ::WinDefWindowProc(hWnd, msg, mp1, mp2);
@ -183,10 +195,11 @@ nsDragService::~nsDragService()
NS_IMETHODIMP nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, nsISupportsArray *aTransferables, nsIScriptableRegion *aRegion, PRUint32 aActionType)
{
// Utter hack for drag drop - provide a way for nsWindow
// to set mSourceDataItems
// Utter hack for drag drop - provide a way for nsWindow to set
// mSourceDataItems & to clear mSourceNode so we can identify native drags
if (!aDOMNode && !aRegion && !aActionType) {
mSourceDataItems = aTransferables;
mSourceNode = 0;
return NS_OK;
}
nsBaseDragService::InvokeDragSession ( aDOMNode, aTransferables, aRegion, aActionType );
@ -286,6 +299,7 @@ NS_IMETHODIMP nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, nsISupports
DrgDeleteDraginfoStrHandles(pDragInfo);
DrgFreeDraginfo(pDragInfo);
mSourceNode = 0;
mSourceDataItems = 0;
mSourceData = 0;
mMimeType = 0;
@ -643,44 +657,6 @@ void WriteTypeEA(const char *filename, const char *type)
// --------------------------------------------------------------------------
/* GetURLObjectContents -- This function returns the contents of a WPSH URL
* object by reading the file name specified in the dragitem. This is
* necessary so that dropping URL objects onto the browser displays the
* page at the URL, rather than displaying the URL itself.
*/
nsresult GetURLObjectContents(const char *pszPath, char **ppszURL)
{
nsresult rv = NS_ERROR_FAILURE;
size_t readsize;
ULONG filesize;
FILE *fp = 0;
fp = fopen(pszPath, "r");
if (fp) {
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (filesize > 0) {
readsize = (size_t)filesize;
*ppszURL = (char*)nsMemory::Alloc(readsize+1);
if (*ppszURL) {
readsize = fread((void *)*ppszURL, 1, readsize, fp);
if (readsize) {
(*ppszURL)[readsize] = '\0';
rv = NS_OK;
}
else
nsMemory::Free(*ppszURL);
}
}
fclose(fp);
}
return (rv);
}
// --------------------------------------------------------------------------
// to do: this needs to take into account the current page's encoding
// if it is different than the PM codepage

Просмотреть файл

@ -19,6 +19,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Rich Walsh <dragtext@e-vertise.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or

Просмотреть файл

@ -20,6 +20,7 @@
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* IBM Corp.
* Rich Walsh <dragtext@e-vertise.com>
*
*/
@ -149,6 +150,30 @@ static PRUint32 gLastInputEventTime = 0;
static int currentWindowIdentifier = 0;
#endif
//-------------------------------------------------------------------------
// Drag and Drop flags and helper functions
// see the D&D section toward the end of this file for additional info
#define DRAG_NONE 0 // no drag in progress
#define DRAG_FROMSYS 1 // native drag that Moz isn't involved in
#define DRAG_OVEROTHER 2 // native drag over another Moz window
#define DRAG_OVERTHIS 4 // native drag over this nsWindow object
#define DRAG_FROMMOZ 8 // Moz is the source, pointer could be anywhere
nsresult SetNativeDragData( PDRAGINFO pdraginfo, nsIDragService* dragService);
nsresult GetNativeDropData( PDRAGINFO pdraginfo, nsITransferable* trans);
char * GetAtom( ATOM aAtom);
char * GetFilePath( PDRAGITEM pdragitem);
char * GetFileContents( PDRAGITEM pdragitem);
// gInDrop counteracts a "fix one thing, break something else" problem:
// it is set & cleared by OnDrop() to enable the display of new content
// areas (i.e. new tabs) which would otherwise be suppressed by Show()
// during a native drag; it must be global because the new window
// belongs to a different nsWindow object than the one dropped on
PRBool gInDrop = PR_FALSE;
//-------------------------------------------------------------------------
//
// nsWindow constructor
@ -181,6 +206,7 @@ nsWindow::nsWindow() : nsBaseWidget()
mIsScrollBar = FALSE;
mInSetFocus = FALSE;
mChromeHidden = FALSE;
mDragHps = 0;
mIsTopWidgetWindow = PR_FALSE;
@ -382,13 +408,14 @@ void nsWindow::InitEvent(nsGUIEvent& event, PRUint32 aEventType, nsPoint* aPoint
event.nativeMsg = 0;
if (nsnull == aPoint) { // use the point from the event
// get the message position in client coordinates and in twips
// for most events, get the message position; for native drag events,
// msg position is incorrect, so get the current position instead
POINTL ptl;
WinQueryMsgPos( 0/*hab*/, &ptl);
if (mNativeDrag)
WinQueryPointerPos( HWND_DESKTOP, &ptl);
else
WinQueryMsgPos( 0/*hab*/, &ptl);
WinMapWindowPoints( HWND_DESKTOP, mWnd, &ptl, 1);
if ((aEventType == NS_DRAGDROP_DROP) && (mNativeDrag)) {
ptl.x = ptl.y = 1;
}
#if 0
printf("++++++++++nsWindow::InitEvent (!pt) mapped point = %ld, %ld\n", ptl.x, ptl.y);
@ -1156,12 +1183,18 @@ NS_METHOD nsWindow::Show(PRBool bState)
HWND hwnd = GetMainWindow();
if( bState == PR_TRUE)
{
WinShowWindow( hwnd, TRUE);
// if we're in a drag & Moz didn't originate it, suppress popups
// like the Bookmark menu (mainly because we can't hide them again
// during the drag); however, if a drop just occurred, display
// the window anyway (otherwise, the contents of new tabs will
// never be displayed
PRUint32 dragStatus = GetDragStatus(0, 0);
if (gInDrop || !(dragStatus & (DRAG_OVEROTHER | DRAG_OVERTHIS)))
WinShowWindow( hwnd, TRUE);
}
else
{
WinShowWindow( hwnd, FALSE);
}
}
return NS_OK;
@ -1912,15 +1945,18 @@ void* nsWindow::GetNativeData(PRUint32 aDataType)
case NS_NATIVE_WINDOW:
case NS_NATIVE_PLUGIN_PORT:
return (void*)mWnd;
case NS_NATIVE_GRAPHIC:
HPS hps;
if (mNativeDrag) {
hps = DrgGetPS(mWnd);
} else {
case NS_NATIVE_GRAPHIC: {
// during a native drag over the current window or any drag
// originating in Moz, return a drag HPS to avoid screen corruption;
HPS hps = 0;
GetDragStatus(DRAG_OVERTHIS | DRAG_FROMMOZ, &hps);
if (!hps)
hps = WinGetPS(mWnd);
}
nsPaletteOS2::SelectGlobalPalette(hps, mWnd);
return (void*)hps;
}
case NS_NATIVE_COLORMAP:
default:
break;
@ -1935,10 +1971,9 @@ void nsWindow::FreeNativeData(void * data, PRUint32 aDataType)
switch(aDataType)
{
case NS_NATIVE_GRAPHIC:
if (mNativeDrag) {
DrgReleasePS((HPS)data);
} else {
WinReleasePS((HPS)data);
if (data) {
if (ReleaseDragHPS((HPS)data))
WinReleasePS((HPS)data);
}
break;
case NS_NATIVE_WIDGET:
@ -1985,18 +2020,38 @@ NS_METHOD nsWindow::Scroll(PRInt32 aDx, PRInt32 aDy, nsRect *aClipRect)
// this rect is inex
}
// this prevents screen corruption while scrolling during a
// Moz-originated drag - during a native drag, the screen
// isn't updated until the drag ends. so there's no corruption
HPS hps = 0;
GetDragStatus(DRAG_FROMMOZ, &hps);
WinScrollWindow( mWnd, aDx, -aDy, aClipRect ? &rcl : 0, 0, 0,
0, SW_SCROLLCHILDREN | SW_INVALIDATERGN);
Update();
if (hps)
ReleaseDragHPS(hps);
return NS_OK;
}
NS_IMETHODIMP nsWindow::ScrollWidgets(PRInt32 aDx, PRInt32 aDy)
{
// this prevents screen corruption while scrolling during a
// Moz-originated drag - during a native drag, the screen
// isn't updated until the drag ends. so there's no corruption
HPS hps = 0;
GetDragStatus(DRAG_FROMMOZ, &hps);
// Scroll the entire contents of the window + change the offset of any child windows
WinScrollWindow( mWnd, aDx, -aDy, NULL, NULL, NULL,
NULL, SW_INVALIDATERGN | SW_SCROLLCHILDREN);
WinScrollWindow( mWnd, aDx, -aDy, 0, 0, 0, 0,
SW_INVALIDATERGN | SW_SCROLLCHILDREN);
Update(); // Force synchronous generation of NS_PAINT
if (hps)
ReleaseDragHPS(hps);
return NS_OK;
}
@ -2010,11 +2065,20 @@ NS_IMETHODIMP nsWindow::ScrollRect(nsRect &aRect, PRInt32 aDx, PRInt32 aDy)
rcl.yTop = rcl.yBottom + aRect.height;
NS2PM( rcl);
// this prevents screen corruption while scrolling during a
// Moz-originated drag - during a native drag, the screen
// isn't updated until the drag ends. so there's no corruption
HPS hps = 0;
GetDragStatus(DRAG_FROMMOZ, &hps);
// Scroll the bits in the window defined by trect.
// Child windows are not scrolled.
WinScrollWindow(mWnd, aDx, -aDy, &rcl, NULL, NULL,
NULL, SW_INVALIDATERGN);
WinScrollWindow(mWnd, aDx, -aDy, &rcl, 0, 0, 0, SW_INVALIDATERGN);
Update(); // Force synchronous generation of NS_PAINT
if (hps)
ReleaseDragHPS(hps);
return NS_OK;
}
@ -2783,19 +2847,21 @@ PRBool nsWindow::OnPaint()
// Get rect to redraw and validate window
RECTL rcl = { 0 };
HPS hPS = WinBeginPaint(mWnd, NULLHANDLE, &rcl);
// get the current drag status; if we're currently in a Moz-originated
// drag, get the special drag HPS then pass it to WinBeginPaint();
// if there is no hpsDrag, WinBeginPaint() will return a normal HPS
HPS hpsDrag = 0;
GetDragStatus(DRAG_FROMMOZ, &hpsDrag);
HPS hPS = WinBeginPaint(mWnd, hpsDrag, &rcl);
nsPaletteOS2::SelectGlobalPalette(hPS, mWnd);
// XXX What is this check doing? If it's trying to check for an empty
// paint rect then use the IsRectEmpty() function...
if (rcl.xLeft || rcl.xRight || rcl.yTop || rcl.yBottom)
// if the update rect is empty, suppress the paint event
if (!WinIsRectEmpty(0, &rcl))
{
// call the event callback
if (mEventCallback)
{
nsPaintEvent event;
InitEvent(event, NS_PAINT);
// build XP rect from in-ex window rect
@ -2816,7 +2882,7 @@ PRBool nsWindow::OnPaint()
(PRInt32) mWnd);
#endif // NS_DEBUG
nsresult res;
//nsresult res;
static NS_DEFINE_CID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
@ -2848,7 +2914,9 @@ PRBool nsWindow::OnPaint()
}
}
WinEndPaint( hPS );
WinEndPaint(hPS);
if (hpsDrag)
ReleaseDragHPS(hpsDrag);
}
return rc;
@ -3444,8 +3512,37 @@ void nsWindow::RemoveFromStyle( ULONG style)
// --------------------------------------------------------------------------
// Drag'n'drop --------------------------------------------------------------
//
// General considerations: PM normally locks the screen during a drag to
// prevent video corruption. Since this conflicts with Mozilla's penchant
// for popping up menus, scrolling windows, etc. during a drag, Moz leaves
// the screen unlocked. Thus, a *lot* of screwing-around is required to
// avoid said corruption when it originates a drag. Even more is required
// when the drag originates elsewhere & the screen is locked.
//
// GetDragStatus() is at the heart of this corruption-avoidance scheme, and
// as far as its author (R.Walsh) can tell, every invocation is *required*.
// The end result: for Moz drags, all while-you-drag features should be
// fully enabled & corruption free; for native drags, popups & scrolling
// are suppressed but some niceties, such as moving the cursor in text
// fields, are enabled.
//
// For native drags, Mozilla will permit you to drop these items: WPS Url
// objects, files, the contents of files (Alt-drop), and text from DragText.
//
// A note on DragText support: DT offers several text rendering mechanisms.
// However, some employ deferred rendering (which nsWindow doesn't support
// currently), and some are only available to registered users. To provide
// at least some d&d text support for all users, two rendering mechanisms
// are implemented. DRM_ATOM (a freeware feature) uses a Drg API atom for
// drags under 256 characters. DRM_OS2FILE (a shareware feature) uses a
// temporary file that DT creates for each drag it originates. The code
// below uses the presence of another of DT's rendering mechanisms,
// DRM_DTSHARE, to distinguish these text-bearing files from other files.
// --------------------------------------------------------------------------
#define DispatchDragDropEvent(msg) DispatchStandardEvent(msg,NS_DRAGDROP_EVENT)
// --------------------------------------------------------------------------
PRBool nsWindow::OnDragOver(MPARAM mp1, MPARAM mp2, MRESULT &mr)
{
@ -3453,78 +3550,88 @@ PRBool nsWindow::OnDragOver(MPARAM mp1, MPARAM mp2, MRESULT &mr)
USHORT usDrop = DOR_DROP;
USHORT usDefaultOp = DO_MOVE;
PDRAGINFO pdraginfo = (PDRAGINFO)mp1;
DRAGITEM dragitem;
nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv);
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
DrgAccessDraginfo(pdraginfo);
// no drag session means this is a native drag's first dragover;
// if the dragged item is acceptable, a transferable will be created,
// the current drag will be identified as native, and a drag-enter
// event will be dispatched
if (!dragSession) {
/* Check to see if we can handle it. If we can, create a drag session */
DrgQueryDragitem(pdraginfo, sizeof(DRAGITEM), &dragitem, 0);
if (DrgVerifyRMF(&dragitem, "DRM_OS2FILE","DRF_TEXT") ||
DrgVerifyRMF(&dragitem, "DRM_OS2FILE","DRF_UNKNOWN")) {
dragService->StartDragSession();
if (NS_SUCCEEDED(SetNativeDragData( pdraginfo, dragService))) {
dragService->GetCurrentSession(getter_AddRefs(dragSession));
mNativeDrag = TRUE;
} else {
usDrop = DOR_NEVERDROP;
if (dragSession) {
mNativeDrag = TRUE;
DispatchDragDropEvent(NS_DRAGDROP_ENTER);
}
}
}
if ((dragSession) && (!mNativeDrag)) {
// no dragsession at this point means the drag will never be acceptable
// or there was an error, so tell PM not to bother us again; otherwise,
// determine the type of operation
if (!dragSession) {
usDrop = DOR_NEVERDROP;
}
else {
switch (pdraginfo->usOperation) {
case DO_COPY:
usDefaultOp = DO_COPY;
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY);
break;
case DO_LINK:
usDefaultOp = DO_LINK;
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK);
break;
default:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
break;
}
// some widgets fail to set canDrop appropriately, so preset it,
// then dispatch the event
dragSession->SetCanDrop(PR_TRUE);
DispatchDragDropEvent(NS_DRAGDROP_OVER);
// if the target won't accept the drop, let PM know
PRBool canDrop;
dragSession->GetCanDrop(&canDrop);
if (!canDrop)
usDrop = DOR_NODROP;
switch (pdraginfo->usOperation) {
case DO_COPY:
usDefaultOp = DO_COPY;
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY);
break;
case DO_LINK:
usDefaultOp = DO_LINK;
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK);
break;
default:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
break;
}
} else if (mNativeDrag && (mContentType == eContentTypeContent)) {
usDefaultOp = DO_MOVE;
} else {
usDrop = DOR_NEVERDROP;
}
DrgFreeDraginfo(pdraginfo);
mr = MRFROM2SHORT(usDrop, usDefaultOp);
DispatchDragDropEvent(NS_DRAGDROP_OVER);
return PR_TRUE;
}
PRBool nsWindow::OnDragLeave( MPARAM mp1, MPARAM mp2)
{
if (mNativeDrag) {
nsresult rv;
nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv);
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (dragSession) {
dragService->EndDragSession();
mNativeDrag = FALSE;
}
}
DispatchDragDropEvent(NS_DRAGDROP_EXIT);
DrgFreeDraginfo(pdraginfo);
return PR_TRUE;
}
// --------------------------------------------------------------------------
extern nsresult GetURLObjectContents(const char *pszPath, char **ppszURL);
PRBool nsWindow::OnDragLeave( MPARAM mp1, MPARAM mp2)
{
DispatchDragDropEvent(NS_DRAGDROP_EXIT);
// if this is a native drag, we have to do cleanup - in particular,
// freeing the transferable created on the first dragover
if (mNativeDrag) {
nsresult rv;
nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv);
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (dragSession) {
dragService->InvokeDragSession(0, 0, 0, 0);
dragService->EndDragSession();
mNativeDrag = FALSE;
}
}
return PR_TRUE;
}
// --------------------------------------------------------------------------
@ -3532,108 +3639,357 @@ PRBool nsWindow::OnDrop(MPARAM mp1, MPARAM mp2)
{
nsresult rv;
PDRAGINFO pdraginfo = (PDRAGINFO)mp1;
DRAGITEM dragitem;
nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv);
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
DrgAccessDraginfo(pdraginfo);
switch (pdraginfo->usOperation) {
case DO_COPY:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY);
break;
case DO_LINK:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK);
break;
default:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
break;
}
if (!dragSession || !DrgAccessDraginfo(pdraginfo))
return PR_FALSE;
// for native drags, retrieve the dragged item's data, put it in a
// transferable, then pass it to nsDragService for use by the target
if (mNativeDrag) {
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
// now create the transferable and stuff data into it.
nsCOMPtr<nsITransferable> trans(do_CreateInstance("@mozilla.org/widget/transferable;1"));
DrgQueryDragitem(pdraginfo, sizeof(DRAGITEM), &dragitem, 0);
// get the dropped file's path
ULONG ulCnrLength = DrgQueryStrNameLen(dragitem.hstrContainerName);
ULONG ulSrcLength = DrgQueryStrNameLen(dragitem.hstrSourceName);
char* pszPath = (char*)nsMemory::Alloc(ulCnrLength+ulSrcLength+1);
DrgQueryStrName(dragitem.hstrContainerName, ulCnrLength+1, pszPath);
DrgQueryStrName(dragitem.hstrSourceName, ulSrcLength+1, &pszPath[ulCnrLength]);
// add a special flavor if we're an anchor to indicate that we have a URL
// in the drag data
nsCAutoString dragData;
// if this is a URL object, get the file's content;
// the function allocs a buffer we have to free
if (DrgVerifyType(&dragitem, "UniformResourceLocator"))
{
char* pszURL;
if (NS_FAILED(GetURLObjectContents(pszPath, &pszURL)))
return NS_ERROR_FAILURE;
dragData += pszURL;
nsMemory::Free(pszURL);
}
// otherwise, turn the path into a URL
else
{
nsCOMPtr<nsILocalFile> file;
if ( NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(pszPath), PR_TRUE, getter_AddRefs(file))) )
{
nsCAutoString urlSpec;
NS_GetURLSpecFromFile(file, urlSpec);
dragData += urlSpec;
nsCOMPtr<nsITransferable> trans(do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
rv = GetNativeDropData(pdraginfo, trans);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsISupportsArray> transArray(do_CreateInstance("@mozilla.org/supports-array;1", &rv));
if (NS_SUCCEEDED(rv)) {
transArray->InsertElementAt(trans, 0);
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
dragService->InvokeDragSession(0, transArray, 0, 0);
}
}
// get the suggested target name & use it as a title
ULONG ulLength = DrgQueryStrNameLen(dragitem.hstrTargetName);
PSZ pszTitle = (PSZ)nsMemory::Alloc(ulLength+1);
DrgQueryStrName(dragitem.hstrTargetName, ulLength+1, pszTitle);
dragData += NS_LITERAL_CSTRING("\n");
dragData += pszTitle;
nsMemory::Free(pszPath);
nsMemory::Free(pszTitle);
nsCOMPtr<nsISupportsString> urlPrimitive(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if ( !urlPrimitive )
return NS_ERROR_FAILURE;
urlPrimitive->SetData(NS_ConvertASCIItoUCS2(dragData));
trans->SetTransferData(kURLMime, urlPrimitive, dragData.Length() * 2);
nsCOMPtr<nsISupportsArray> transArray(do_CreateInstance("@mozilla.org/supports-array;1"));
if ( !transArray )
return NS_ERROR_FAILURE;
transArray->InsertElementAt(trans, 0);
dragService->InvokeDragSession(0, transArray, 0, 0);
}
else
{
switch (pdraginfo->usOperation) {
case DO_COPY:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY);
break;
case DO_LINK:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK);
break;
default:
dragSession->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
break;
}
rv = NS_OK;
}
DispatchDragDropEvent(NS_DRAGDROP_DROP);
// if all of the above went well, dispatch the event; setting gInDrop
// enables the display of new tabs that would otherwise be suppressed
if (NS_SUCCEEDED(rv)) {
gInDrop = PR_TRUE;
DispatchDragDropEvent(NS_DRAGDROP_DROP);
gInDrop = PR_FALSE;
}
// target deletes the string handles
// target deletes the string handles
DrgDeleteDraginfoStrHandles(pdraginfo);
// if the source was Moz, this will fail (as it should) because
// we're still in DrgDrag; in that case the source will free this
// if the source was Moz, this will fail (as it should) because
// we're still in DrgDrag; in that case the source will free this
DrgFreeDraginfo(pdraginfo);
if (mNativeDrag && dragSession) {
dragService->InvokeDragSession(0, 0, 0, 0);
dragService->EndDragSession();
mNativeDrag = PR_FALSE;
}
return PR_TRUE;
return (NS_SUCCEEDED(rv) ? PR_TRUE : PR_FALSE);
}
// --------------------------------------------------------------------------
// This method is used to determine if it is possible to paint the screen
// during d&d without corruption. If not, the caller will probably suppress
// the paint or other screen activity. If requested, a drag HPS will be
// returned if the current status matches one of the flags specified in aState.
PRUint32 nsWindow::GetDragStatus(PRUint32 aState, HPS * oHps)
{
PRUint32 rtn = DRAG_NONE;
// a drag from another app is over the current object's window
if (mNativeDrag)
rtn = DRAG_OVERTHIS;
else
// if there's a drag anywhere in the system, see if Moz is involved,
// as indicated by the existence of a dragsession
if (DrgQueryDragStatus() == DGS_DRAGINPROGRESS) {
rtn = DRAG_FROMSYS;
nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1");
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (dragSession) {
nsCOMPtr<nsIDOMNode> dragNode;
dragSession->GetSourceNode(getter_AddRefs(dragNode));
// if there's a source node, then Moz originated the drag - otherwise,
// the drag originated elsewhere; since we eliminated native drags
// over the current object's window above, the drag pointer must be
// be over some other object's window (e.g. the pointer is over the
// Bookmark button, causing the bookmark menu to appear & need painting)
if (dragNode)
rtn = DRAG_FROMMOZ;
else
rtn = DRAG_OVEROTHER;
}
}
// if the caller wants an HPS, and the current drag status matches
// the specified drag state, *and* a drag hps hasn't already been
// requested for this window, get the hps; otherwise, return zero;
// (if we provide a 2nd hps for a window, the cursor in text fields
// won't be erased when it's moved to another position)
if (oHps)
if (!mDragHps && (rtn & aState)) {
mDragHps = DrgGetPS(mWnd);
*oHps = mDragHps;
}
else
*oHps = 0;
return rtn;
}
//-------------------------------------------------------------------------
// if there's an outstanding drag hps & it matches the one
// passed in, release it
HPS nsWindow::ReleaseDragHPS(HPS aHps)
{
if (mDragHps && aHps == mDragHps) {
DrgReleasePS(mDragHps);
mDragHps = 0;
aHps = 0;
}
return aHps;
}
// --------------------------------------------------------------------------
// D&D helper functions
// --------------------------------------------------------------------------
// evaluates a native drag, and if acceptable, creates a transferable, inserts
// the available flavors (but not the data), then passes it to nsDragService
// so Mozilla widgets can review the item being dragged over them
nsresult SetNativeDragData( PDRAGINFO pdraginfo, nsIDragService* dragService)
{
nsresult rv = NS_ERROR_FAILURE;
PDRAGITEM pdragitem = DrgQueryDragitemPtr(pdraginfo, 0);
if (!pdragitem)
return rv;
// the only acceptable DRMs are atoms & files that already exist
if (DrgVerifyRMF(pdragitem, "DRM_ATOM", 0) ||
(DrgVerifyRMF(pdragitem, "DRM_OS2FILE", 0) &&
pdragitem->hstrContainerName && pdragitem->hstrSourceName)) {
nsCOMPtr<nsITransferable> trans(do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
if (trans) {
// everything is always "text"
trans->AddDataFlavor(kUnicodeMime);
// if the dragitem explicitly identifies itself as a URL,
// or if this is *not* one of DragText's temporary files,
// identify this as a URL too
if (DrgVerifyType(pdragitem, "UniformResourceLocator") ||
!DrgVerifyRMF(pdragitem, "DRM_DTSHARE", 0))
trans->AddDataFlavor(kURLMime);
nsCOMPtr<nsISupportsArray> transArray(do_CreateInstance("@mozilla.org/supports-array;1", &rv));
if (transArray) {
transArray->InsertElementAt(trans, 0);
dragService->StartDragSession();
dragService->InvokeDragSession(0, transArray, 0, 0);
rv = NS_OK;
}
}
}
return rv;
}
// --------------------------------------------------------------------------
// after a native item has been dropped, this retrieves its data and
// places its in the supplied transferable
nsresult GetNativeDropData( PDRAGINFO pdraginfo, nsITransferable* trans)
{
nsresult rv = NS_ERROR_FAILURE;
char * pszText = 0;
PDRAGITEM pdragitem = DrgQueryDragitemPtr(pdraginfo, 0);
if (!pdragitem)
return rv;
BOOL isUrl = DrgVerifyType(pdragitem, "UniformResourceLocator");
nsCString textStr;
// DRM_ATOM uses a Drg HSTR in ulItemID to pass up to 255 chars
if (DrgVerifyRMF(pdragitem, "DRM_ATOM", 0)) {
pszText = GetAtom(pdragitem->ulItemID);
if (pszText)
textStr.Assign(pszText);
}
else
// for DRM_OS2FILE, use the file's contents if it is a WPS Url object
// or a DragText temp file being used to pass text (identifiable by the
// presence of DRM_DTSHARE), or if the user has pressed the Alt key when
// dropping; otherwise, get the file's path & transform it into a Url
if (DrgVerifyRMF(pdragitem, "DRM_OS2FILE", 0)) {
if (isUrl ||
DrgVerifyRMF(pdragitem, "DRM_DTSHARE", 0) ||
(WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000)) {
pszText = GetFileContents(pdragitem);
if (pszText)
textStr.Assign(pszText);
}
else {
pszText = GetFilePath(pdragitem);
if (pszText) {
isUrl = TRUE;
nsCOMPtr<nsILocalFile> file;
if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(pszText),
PR_TRUE, getter_AddRefs(file))))
NS_GetURLSpecFromFile(file, textStr);
}
}
}
nsMemory::Free(pszText);
if (textStr.IsEmpty())
return rv;
// if this is a URL, append a title to the string
if (isUrl && pdragitem->hstrTargetName) {
pszText = GetAtom(pdragitem->hstrTargetName);
if (pszText) {
textStr += NS_LITERAL_CSTRING("\n");
textStr += pszText;
nsMemory::Free(pszText);
}
}
// convert the string to Unicode
ULONG ulInLength = textStr.Length() + 1;
PRUnichar* pszUCS = (PRUnichar*)nsMemory::Alloc(2*ulInLength);
if (!pszUCS)
return rv;
ULONG ulOutLength = MultiByteToWideChar( 0, PromiseFlatCString(textStr).get(),
ulInLength-1, pszUCS, 2*ulInLength);
// if successful, set the UCS string into the transferable,
if (ulOutLength) {
pszUCS[ulOutLength] = 0;
nsCOMPtr<nsISupportsString> textPrimitive(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (textPrimitive ) {
textPrimitive->SetData(nsDependentString(pszUCS));
// for URLs, we have to set the entire string into the transferable
// and label it accordingly, then get rid of the title we added
if (isUrl) {
trans->SetTransferData(kURLMime, textPrimitive, 2*ulOutLength);
PRUnichar* pBrk = (PRUnichar*)UniStrchr((const UniChar*)pszUCS, L'\n');
if (pBrk) {
*pBrk = 0;
ulOutLength = UniStrlen((const UniChar*)pszUCS);
textPrimitive->SetData(nsDependentString(pszUCS));
}
}
// set the text or URL-minus-title into the transferable
// and label it as plain text
trans->SetTransferData(kUnicodeMime, textPrimitive, 2*ulOutLength);
rv = NS_OK;
}
}
nsMemory::Free(pszUCS);
return rv;
}
// --------------------------------------------------------------------------
// return a ptr to a buffer containing the text associated with a
// drag atom; the caller frees the buffer
char * GetAtom( ATOM aAtom)
{
ULONG ulInLength = DrgQueryStrNameLen(aAtom) + 1;
char* pszText = (char*)nsMemory::Alloc(ulInLength);
if (pszText)
DrgQueryStrName(aAtom, ulInLength, pszText);
return pszText;
}
// --------------------------------------------------------------------------
// return a ptr to a buffer containing the file path specified in the
// dragitem; the caller frees the buffer
char * GetFilePath( PDRAGITEM pdragitem)
{
ULONG ulCnrLength = DrgQueryStrNameLen(pdragitem->hstrContainerName);
ULONG ulSrcLength = DrgQueryStrNameLen(pdragitem->hstrSourceName);
char* pszText = (char*)nsMemory::Alloc(ulCnrLength+ulSrcLength+1);
if (pszText) {
DrgQueryStrName(pdragitem->hstrContainerName, ulCnrLength+1, pszText);
DrgQueryStrName(pdragitem->hstrSourceName, ulSrcLength+1, &pszText[ulCnrLength]);
}
return pszText;
}
// --------------------------------------------------------------------------
// read the file specified in the dragitem; return either a pointer to
// the buffer containing its contents (which the caller must free) or a
// null ptr if there were any failures
char * GetFileContents( PDRAGITEM pdragitem)
{
char* pszText = 0;
char* pszPath = GetFilePath( pdragitem);
if (pszPath) {
FILE *fp = fopen(pszPath, "r");
if (fp) {
fseek(fp, 0, SEEK_END);
ULONG filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (filesize > 0) {
size_t readsize = (size_t)filesize;
pszText = (char*)nsMemory::Alloc(readsize+1);
if (pszText) {
readsize = fread((void *)pszText, 1, readsize, fp);
if (readsize)
pszText[readsize] = '\0';
else {
nsMemory::Free(pszText);
pszText = 0;
}
}
}
fclose(fp);
}
nsMemory::Free(pszPath);
}
return pszText;
}
// --------------------------------------------------------------------------
// Raptor object access
// --------------------------------------------------------------------------
// 'Native data'
// function to translate from a WM_CHAR to an NS VK_ constant ---------------

Просмотреть файл

@ -31,6 +31,7 @@
* 06/15/2000 IBM Corp. Added NS2PM for rectangles
* 06/21/2000 IBM Corp. Added CaptureMouse
*
* Rich Walsh <dragtext@e-vertise.com>
*/
#ifndef _nswindow_h
@ -252,6 +253,7 @@ protected:
BOOL mInSetFocus;
BOOL mChromeHidden;
nsContentType mContentType;
HPS mDragHps; // retrieved by DrgGetPS() during a drag
HWND GetParentHWND() const;
HWND GetHWND() const { return mWnd; }
@ -293,8 +295,10 @@ protected:
virtual PRBool DispatchResizeEvent( PRInt32 aClientX, PRInt32 aClientY);
void GetNonClientBounds(nsRect &aRect);
void DeferPosition( HWND, HWND, long, long, long, long, ULONG);
void ConstrainZLevel(HWND *aAfter);
void ConstrainZLevel(HWND *aAfter);
PRUint32 GetDragStatus(PRUint32 aState, HPS * oHps);
HPS ReleaseDragHPS(HPS hps);
// Enumeration of the methods which are accessable on the PM thread
enum {