/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * IBM Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Rich Walsh * * 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 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #define INCL_DOSMISC #define INCL_DOSERRORS #include "nsDragService.h" #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" #include "nsString.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsIWebBrowserPersist.h" #include "nsILocalFile.h" #include "nsIURI.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsOS2Uni.h" #include "wdgtos2rc.h" #include "nsILocalFileOS2.h" #include "nsIDocument.h" #include "nsGUIEvent.h" #include "nsISelection.h" // -------------------------------------------------------------------------- // Local defines // undocumented(?) #ifndef DC_PREPAREITEM #define DC_PREPAREITEM 0x0040 #endif // limit URL object titles to a reasonable length #define MAXTITLELTH 31 #define TITLESEPARATOR (L' ') #define DTSHARE_NAME "\\SHAREMEM\\MOZ_DND" #define DTSHARE_RMF "" #define OS2FILE_NAME "MOZ_TGT.TMP" #define OS2FILE_TXTRMF "" #define OS2FILE_UNKRMF "" // not defined in the OS/2 toolkit headers extern "C" { APIRET APIENTRY DosQueryModFromEIP(HMODULE *phMod, ULONG *pObjNum, ULONG BuffLen, PCHAR pBuff, ULONG *pOffset, ULONG Address); } // -------------------------------------------------------------------------- // Helper functions nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd); nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult, bool content, char** outText); nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd); nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult, char** outText); nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName); nsresult GetAtom( ATOM aAtom, char** outText); nsresult GetFileName(PDRAGITEM pditem, char** outText); nsresult GetFileContents(PCSZ pszPath, char** outText); nsresult GetTempFileName(char** outText); void SaveTypeAndSource(nsILocalFile *file, nsIDOMDocument *domDoc, PCSZ pszType); int UnicodeToCodepage( const nsAString& inString, char **outText); int CodepageToUnicode( const nsACString& inString, PRUnichar **outText); void RemoveCarriageReturns(char * pszText); MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2); // -------------------------------------------------------------------------- // Global data static HPOINTER gPtrArray[IDC_DNDCOUNT]; static char * gTempFile = 0; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- nsDragService::nsDragService() { // member initializers and constructor code mDragWnd = WinCreateWindow( HWND_DESKTOP, WC_STATIC, 0, 0, 0, 0, 0, 0, HWND_DESKTOP, HWND_BOTTOM, 0, 0, 0); WinSubclassWindow( mDragWnd, nsDragWindowProc); HMODULE hModResources = NULLHANDLE; DosQueryModFromEIP(&hModResources, NULL, 0, NULL, NULL, (ULONG) &gPtrArray); for (int i = 0; i < IDC_DNDCOUNT; i++) gPtrArray[i] = ::WinLoadPointer(HWND_DESKTOP, hModResources, i+IDC_DNDBASE); } // -------------------------------------------------------------------------- nsDragService::~nsDragService() { // destructor code WinDestroyWindow(mDragWnd); for (int i = 0; i < IDC_DNDCOUNT; i++) { WinDestroyPointer(gPtrArray[i]); gPtrArray[i] = 0; } } NS_IMPL_ISUPPORTS_INHERITED1(nsDragService, nsBaseDragService, nsIDragSessionOS2) // -------------------------------------------------------------------------- NS_IMETHODIMP nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, nsISupportsArray *aTransferables, nsIScriptableRegion *aRegion, PRUint32 aActionType) { if (mDoingDrag) return NS_ERROR_UNEXPECTED; nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, aTransferables, aRegion, aActionType); NS_ENSURE_SUCCESS(rv, rv); mSourceDataItems = aTransferables; WinSetCapture(HWND_DESKTOP, NULLHANDLE); // Assume we are only dragging one thing for now PDRAGINFO pDragInfo = DrgAllocDraginfo(1); if (!pDragInfo) return NS_ERROR_UNEXPECTED; pDragInfo->usOperation = DO_DEFAULT; DRAGITEM dragitem; dragitem.hwndItem = mDragWnd; dragitem.ulItemID = (ULONG)this; dragitem.fsControl = DC_OPEN; dragitem.cxOffset = 2; dragitem.cyOffset = 2; dragitem.fsSupportedOps = DO_COPYABLE|DO_MOVEABLE|DO_LINKABLE; // since there is no source file, leave these "blank" dragitem.hstrContainerName = NULLHANDLE; dragitem.hstrSourceName = NULLHANDLE; rv = NS_ERROR_FAILURE; ULONG idIcon = 0; // bracket this to reduce our footprint before the drag begins { nsCOMPtr genericItem; mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem)); nsCOMPtr transItem (do_QueryInterface(genericItem)); nsCOMPtr genericData; PRUint32 len = 0; // see if we have a URL or text; if so, the title method // will save the data and mimetype for use with a native drop if (NS_SUCCEEDED(transItem->GetTransferData(kURLMime, getter_AddRefs(genericData), &len))) { nsXPIDLCString targetName; rv = GetUrlAndTitle( genericData, getter_Copies(targetName)); if (NS_SUCCEEDED(rv)) { // advise PM that we need a DM_RENDERPREPARE msg // *before* it composes a render-to filename dragitem.fsControl |= DC_PREPAREITEM; dragitem.hstrType = DrgAddStrHandle("UniformResourceLocator"); dragitem.hstrRMF = DrgAddStrHandle(""); dragitem.hstrTargetName = DrgAddStrHandle(targetName.get()); idIcon = IDC_DNDURL; } } else if (NS_SUCCEEDED(transItem->GetTransferData(kUnicodeMime, getter_AddRefs(genericData), &len))) { nsXPIDLCString targetName; rv = GetUniTextTitle( genericData, getter_Copies(targetName)); if (NS_SUCCEEDED(rv)) { dragitem.hstrType = DrgAddStrHandle("Plain Text"); dragitem.hstrRMF = DrgAddStrHandle(""); dragitem.hstrTargetName = DrgAddStrHandle(targetName.get()); idIcon = IDC_DNDTEXT; } } } // if neither URL nor text are available, make this a Moz-only drag // by making it unidentifiable to native apps if (NS_FAILED(rv)) { mMimeType = 0; dragitem.hstrType = DrgAddStrHandle("Unknown"); dragitem.hstrRMF = DrgAddStrHandle(""); dragitem.hstrTargetName = NULLHANDLE; } DrgSetDragitem(pDragInfo, &dragitem, sizeof(DRAGITEM), 0); DRAGIMAGE dragimage; memset(&dragimage, 0, sizeof(DRAGIMAGE)); dragimage.cb = sizeof(DRAGIMAGE); dragimage.fl = DRG_ICON; if (idIcon) dragimage.hImage = gPtrArray[idIcon-IDC_DNDBASE]; if (dragimage.hImage) { dragimage.cyOffset = 8; dragimage.cxOffset = 2; } else dragimage.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE); mDoingDrag = true; LONG escState = WinGetKeyState(HWND_DESKTOP, VK_ESC) & 0x01; HWND hwndDest = DrgDrag(mDragWnd, pDragInfo, &dragimage, 1, VK_BUTTON2, (void*)0x80000000L); // Don't lock the desktop PS // determine whether the drag ended because Escape was pressed if (hwndDest == 0 && (WinGetKeyState(HWND_DESKTOP, VK_ESC) & 0x01) != escState) mUserCancelled = true; FireDragEventAtSource(NS_DRAGDROP_END); mDoingDrag = false; // do clean up; if the drop completed, // the target will delete the string handles if (hwndDest == 0) DrgDeleteDraginfoStrHandles(pDragInfo); DrgFreeDraginfo(pDragInfo); // reset nsDragService's members mSourceDataItems = 0; mSourceData = 0; mMimeType = 0; // reset nsBaseDragService's members mSourceDocument = nsnull; mSourceNode = nsnull; mSelection = nsnull; mDataTransfer = nsnull; mUserCancelled = false; mHasImage = false; mImage = nsnull; mImageX = 0; mImageY = 0; mScreenX = -1; mScreenY = -1; return NS_OK; } // -------------------------------------------------------------------------- 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 pdxfer = (PDRAGTRANSFER)mp1; nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID; if (pdxfer->usOperation == DO_COPY && (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000) && !strcmp(dragservice->mMimeType, kURLMime)) { // QI'ing nsIURL will fail for mailto: and the like nsCOMPtr urlObject(do_QueryInterface(dragservice->mSourceData)); if (urlObject) { nsCAutoString filename; urlObject->GetFileName(filename); if (filename.IsEmpty()) { urlObject->GetHost(filename); filename.Append("/file"); } DrgDeleteStrHandle(pdxfer->pditem->hstrTargetName); pdxfer->pditem->hstrTargetName = DrgAddStrHandle(filename.get()); } } return (MRESULT)TRUE; } case DM_RENDER: { nsresult rv = NS_ERROR_FAILURE; PDRAGTRANSFER pdxfer = (PDRAGTRANSFER)mp1; nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID; char chPath[CCHMAXPATH]; DrgQueryStrName(pdxfer->hstrRenderToName, CCHMAXPATH, chPath); // if the user Ctrl-dropped a URL, use the nsIURL interface // to determine if it points to content (i.e. a file); if so, // fetch its contents; if not (e.g. a 'mailto:' url), drop into // the code that uses nsIURI to render a URL object if (!strcmp(dragservice->mMimeType, kURLMime)) { if (pdxfer->usOperation == DO_COPY && (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)) { nsCOMPtr urlObject(do_QueryInterface(dragservice->mSourceData)); if (urlObject) rv = dragservice->SaveAsContents(chPath, urlObject); } if (!NS_SUCCEEDED(rv)) { nsCOMPtr uriObject(do_QueryInterface(dragservice->mSourceData)); if (uriObject) rv = dragservice->SaveAsURL(chPath, uriObject); } } else // if we're dragging text, do NLS conversion then write it to file if (!strcmp(dragservice->mMimeType, kUnicodeMime)) { nsCOMPtr strObject( do_QueryInterface(dragservice->mSourceData)); if (strObject) rv = dragservice->SaveAsText(chPath, strObject); } DrgPostTransferMsg(pdxfer->hwndClient, DM_RENDERCOMPLETE, pdxfer, (NS_SUCCEEDED(rv) ? DMFL_RENDEROK : DMFL_RENDERFAIL), 0, TRUE); DrgFreeDragtransfer(pdxfer); return (MRESULT)TRUE; } // we don't use these msgs but neither does WinDefWindowProc() case DM_DRAGOVERNOTIFY: case DM_ENDCONVERSATION: return 0; default: break; } return ::WinDefWindowProc(hWnd, msg, mp1, mp2); } //------------------------------------------------------------------------- // if the versions of Start & EndDragSession in nsBaseDragService // were called (and they shouldn't be), they'd break nsIDragSessionOS2; // they're overridden here and turned into no-ops to prevent this NS_IMETHODIMP nsDragService::StartDragSession() { NS_ERROR("OS/2 version of StartDragSession() should never be called!"); return NS_OK; } NS_IMETHODIMP nsDragService::EndDragSession(bool aDragDone) { NS_ERROR("OS/2 version of EndDragSession() should never be called!"); return NS_OK; } // -------------------------------------------------------------------------- NS_IMETHODIMP nsDragService::GetNumDropItems(PRUint32 *aNumDropItems) { if (mSourceDataItems) mSourceDataItems->Count(aNumDropItems); else *aNumDropItems = 0; return NS_OK; } // -------------------------------------------------------------------------- NS_IMETHODIMP nsDragService::GetData(nsITransferable *aTransferable, PRUint32 aItemIndex) { // make sure that we have a transferable if (!aTransferable) return NS_ERROR_INVALID_ARG; // get flavor list that includes all acceptable flavors (including // ones obtained through conversion). Flavors are nsISupportsCStrings // so that they can be seen from JS. nsresult rv = NS_ERROR_FAILURE; nsCOMPtr flavorList; rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); if (NS_FAILED(rv)) return rv; // count the number of flavors PRUint32 cnt; flavorList->Count (&cnt); for (unsigned int i= 0; i < cnt; ++i ) { nsCOMPtr genericWrapper; flavorList->GetElementAt(i, getter_AddRefs(genericWrapper)); nsCOMPtr currentFlavor; currentFlavor = do_QueryInterface(genericWrapper); if (currentFlavor) { nsXPIDLCString flavorStr; currentFlavor->ToString(getter_Copies(flavorStr)); nsCOMPtr genericItem; mSourceDataItems->GetElementAt(aItemIndex, getter_AddRefs(genericItem)); nsCOMPtr item (do_QueryInterface(genericItem)); if (item) { nsCOMPtr data; PRUint32 tmpDataLen = 0; rv = item->GetTransferData(flavorStr, getter_AddRefs(data), &tmpDataLen); if (NS_SUCCEEDED(rv)) { rv = aTransferable->SetTransferData(flavorStr, data, tmpDataLen); break; } } } } return rv; } // -------------------------------------------------------------------------- // This returns true if any of the dragged items support a specified data // flavor. This doesn't make a lot of sense when dragging multiple items: // all of them ought to match. OTOH, Moz doesn't support multiple drag // items so no problems arise. If they do, use the commented-out code to // switch from "any" to "all". NS_IMETHODIMP nsDragService::IsDataFlavorSupported(const char *aDataFlavor, bool *_retval) { if (!_retval) return NS_ERROR_INVALID_ARG; *_retval = false; PRUint32 numDragItems = 0; if (mSourceDataItems) mSourceDataItems->Count(&numDragItems); if (!numDragItems) return NS_OK; // return true if all items support this flavor // for (PRUint32 itemIndex = 0, *_retval = true; // itemIndex < numDragItems && *_retval; ++itemIndex) { // *_retval = false; // return true if any item supports this flavor for (PRUint32 itemIndex = 0; itemIndex < numDragItems && !(*_retval); ++itemIndex) { nsCOMPtr genericItem; mSourceDataItems->GetElementAt(itemIndex, getter_AddRefs(genericItem)); nsCOMPtr currItem (do_QueryInterface(genericItem)); if (currItem) { nsCOMPtr flavorList; currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); if (flavorList) { PRUint32 numFlavors; flavorList->Count( &numFlavors ); for (PRUint32 flavorIndex=0; flavorIndex < numFlavors; ++flavorIndex) { nsCOMPtr genericWrapper; flavorList->GetElementAt(flavorIndex, getter_AddRefs(genericWrapper)); nsCOMPtr currentFlavor; currentFlavor = do_QueryInterface(genericWrapper); if (currentFlavor) { nsXPIDLCString flavorStr; currentFlavor->ToString ( getter_Copies(flavorStr) ); if (strcmp(flavorStr, aDataFlavor) == 0) { *_retval = true; break; } } } // for each flavor } } } return NS_OK; } // -------------------------------------------------------------------------- // use nsIWebBrowserPersist to fetch the contents of a URL nsresult nsDragService::SaveAsContents(PCSZ pszDest, nsIURL* aURL) { nsCOMPtr linkURI(do_QueryInterface(aURL)); if (!linkURI) return NS_ERROR_FAILURE; nsCOMPtr webPersist( do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1")); if (!webPersist) return NS_ERROR_FAILURE; nsCOMPtr file; NS_NewNativeLocalFile(nsDependentCString(pszDest), true, getter_AddRefs(file)); if (!file) return NS_ERROR_FAILURE; FILE* fp; if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp))) return NS_ERROR_FAILURE; fwrite("", 0, 1, fp); fclose(fp); webPersist->SaveURI(linkURI, nsnull, nsnull, nsnull, nsnull, file); return NS_OK; } // -------------------------------------------------------------------------- // save this URL in a file that the WPS will identify as a WPUrl object nsresult nsDragService::SaveAsURL(PCSZ pszDest, nsIURI* aURI) { nsCAutoString strUri; aURI->GetSpec(strUri); if (strUri.IsEmpty()) return NS_ERROR_FAILURE; nsCOMPtr file; NS_NewNativeLocalFile(nsDependentCString(pszDest), true, getter_AddRefs(file)); if (!file) return NS_ERROR_FAILURE; FILE* fp; if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp))) return NS_ERROR_FAILURE; fwrite(strUri.get(), strUri.Length(), 1, fp); fclose(fp); nsCOMPtr domDoc; GetSourceDocument(getter_AddRefs(domDoc)); SaveTypeAndSource(file, domDoc, "UniformResourceLocator"); return NS_OK; } // -------------------------------------------------------------------------- // save this text to file after conversion to the current codepage nsresult nsDragService::SaveAsText(PCSZ pszDest, nsISupportsString* aString) { nsAutoString strData; aString->GetData(strData); if (strData.IsEmpty()) return NS_ERROR_FAILURE; nsCOMPtr file; NS_NewNativeLocalFile(nsDependentCString(pszDest), true, getter_AddRefs(file)); if (!file) return NS_ERROR_FAILURE; nsXPIDLCString textStr; int cnt = UnicodeToCodepage(strData, getter_Copies(textStr)); if (!cnt) return NS_ERROR_FAILURE; FILE* fp; if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp))) return NS_ERROR_FAILURE; fwrite(textStr.get(), cnt, 1, fp); fclose(fp); nsCOMPtr domDoc; GetSourceDocument(getter_AddRefs(domDoc)); SaveTypeAndSource(file, domDoc, "Plain Text"); return NS_OK; } // -------------------------------------------------------------------------- // Split a Moz Url/Title into its components, save the Url for use by // a native drop, then compose a title. nsresult nsDragService::GetUrlAndTitle(nsISupports *aGenericData, char **aTargetName) { // get the URL/title string nsCOMPtr strObject ( do_QueryInterface(aGenericData)); if (!strObject) return NS_ERROR_FAILURE; nsAutoString strData; strObject->GetData(strData); // split string into URL and Title - // if there's a title but no URL, there's no reason to continue PRInt32 lineIndex = strData.FindChar ('\n'); if (lineIndex == 0) return NS_ERROR_FAILURE; // get the URL portion of the text nsAutoString strUrl; if (lineIndex == -1) strUrl = strData; else strData.Left(strUrl, lineIndex); // save the URL for later use nsCOMPtr saveURI; NS_NewURI(getter_AddRefs(saveURI), strUrl); if (!saveURI) return NS_ERROR_FAILURE; // if there's a bona-fide title & it isn't just a copy of the URL, // limit it to a reasonable length, perform NLS conversion, then return if (++lineIndex && lineIndex != (int)strData.Length() && !strUrl.Equals(Substring(strData, lineIndex, strData.Length()))) { PRUint32 strLth = NS_MIN((int)strData.Length()-lineIndex, MAXTITLELTH); nsAutoString strTitle; strData.Mid(strTitle, lineIndex, strLth); if (!UnicodeToCodepage(strTitle, aTargetName)) return NS_ERROR_FAILURE; mSourceData = do_QueryInterface(saveURI); mMimeType = kURLMime; return NS_OK; } // if the URI can be handled as a URL, construct a title from // the hostname & filename; if not, use the first MAXTITLELTH // characters that appear after the scheme name nsCAutoString strTitle; nsCOMPtr urlObj( do_QueryInterface(saveURI)); if (urlObj) { nsCAutoString strFile; urlObj->GetHost(strTitle); urlObj->GetFileName(strFile); if (!strFile.IsEmpty()) { strTitle.AppendLiteral("/"); strTitle.Append(strFile); } else { urlObj->GetDirectory(strFile); if (strFile.Length() > 1) { nsCAutoString::const_iterator start, end, curr; strFile.BeginReading(start); strFile.EndReading(end); strFile.EndReading(curr); for (curr.advance(-2); curr != start; --curr) if (*curr == '/') break; strTitle.Append(Substring(curr, end)); } } } else { saveURI->GetSpec(strTitle); PRInt32 index = strTitle.FindChar (':'); if (index != -1) { if ((strTitle.get())[++index] == '/') if ((strTitle.get())[++index] == '/') ++index; strTitle.Cut(0, index); } if (strTitle.Length() > MAXTITLELTH) strTitle.Truncate(MAXTITLELTH); } *aTargetName = ToNewCString(strTitle); mSourceData = do_QueryInterface(saveURI); mMimeType = kURLMime; return NS_OK; } // -------------------------------------------------------------------------- // Construct a title for text drops from the leading words of the text. // Alphanumeric characters are copied to the title; sequences of // non-alphanums are replaced by a single space nsresult nsDragService::GetUniTextTitle(nsISupports *aGenericData, char **aTargetName) { // get the string nsCOMPtr strObject ( do_QueryInterface(aGenericData)); if (!strObject) return NS_ERROR_FAILURE; // alloc a buffer to hold the unicode title text int bufsize = (MAXTITLELTH+1)*2; PRUnichar * buffer = (PRUnichar*)nsMemory::Alloc(bufsize); if (!buffer) return NS_ERROR_FAILURE; nsAutoString strData; strObject->GetData(strData); nsAutoString::const_iterator start, end; strData.BeginReading(start); strData.EndReading(end); // skip over leading non-alphanumerics for( ; start != end; ++start) if (UniQueryChar( *start, CT_ALNUM)) break; // move alphanumerics into the buffer & replace contiguous // non-alnums with a single separator character int ctr, sep; for (ctr=0, sep=0; start != end && ctr < MAXTITLELTH; ++start) { if (UniQueryChar( *start, CT_ALNUM)) { buffer[ctr] = *start; ctr++; sep = 0; } else if (!sep) { buffer[ctr] = TITLESEPARATOR; ctr++; sep = 1; } } // eliminate trailing separators & lone characters // orphaned when the title is truncated if (sep) ctr--; if (ctr >= MAXTITLELTH - sep && buffer[ctr-2] == TITLESEPARATOR) ctr -= 2; buffer[ctr] = 0; // if we ended up with no alnums, call the result "text"; // otherwise, do NLS conversion if (!ctr) { *aTargetName = ToNewCString(NS_LITERAL_CSTRING("text")); ctr = 1; } else ctr = UnicodeToCodepage( nsDependentString(buffer), aTargetName); // free our buffer, then exit nsMemory::Free(buffer); if (!ctr) return NS_ERROR_FAILURE; mSourceData = aGenericData; mMimeType = kUnicodeMime; return NS_OK; } // -------------------------------------------------------------------------- // nsIDragSessionOS2 // -------------------------------------------------------------------------- // DragOverMsg() provides minimal handling if a drag session is already // in progress. If not, it assumes this is a native drag that has just // entered the window and calls NativeDragEnter() to start a session. NS_IMETHODIMP nsDragService::DragOverMsg(PDRAGINFO pdinfo, MRESULT &mr, PRUint32* dragFlags) { nsresult rv = NS_ERROR_FAILURE; if (!&mr || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo)) return rv; *dragFlags = 0; mr = MRFROM2SHORT(DOR_NEVERDROP, 0); // examine the dragged item & "start" a drag session if OK; // also, signal the need for a dragenter event if (!mDoingDrag) if (NS_SUCCEEDED(NativeDragEnter(pdinfo))) *dragFlags |= DND_DISPATCHENTEREVENT; // if we're in a drag, set it up to be dispatched if (mDoingDrag) { SetCanDrop(false); switch (pdinfo->usOperation) { case DO_COPY: SetDragAction(DRAGDROP_ACTION_COPY); break; case DO_LINK: SetDragAction(DRAGDROP_ACTION_LINK); break; default: SetDragAction(DRAGDROP_ACTION_MOVE); break; } if (mSourceNode) *dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_MOZDRAG; else *dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_NATIVEDRAG; rv = NS_OK; } DrgFreeDraginfo(pdinfo); return rv; } // -------------------------------------------------------------------------- // Evaluates native drag data, and if acceptable, creates & stores // a transferable with the available flavors (but not the data); // if successful, it "starts" the session. NS_IMETHODIMP nsDragService::NativeDragEnter(PDRAGINFO pdinfo) { nsresult rv = NS_ERROR_FAILURE; bool isFQFile = FALSE; bool isAtom = FALSE; PDRAGITEM pditem = 0; if (pdinfo->cditem != 1) return rv; pditem = DrgQueryDragitemPtr(pdinfo, 0); if (pditem) { if (DrgVerifyRMF(pditem, "DRM_ATOM", 0)) { isAtom = TRUE; rv = NS_OK; } else if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0)) rv = NS_OK; else if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) { rv = NS_OK; if (pditem->hstrContainerName && pditem->hstrSourceName) isFQFile = TRUE; } } if (NS_SUCCEEDED(rv)) { rv = NS_ERROR_FAILURE; nsCOMPtr trans( do_CreateInstance("@mozilla.org/widget/transferable;1", &rv)); if (trans) { bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator"); bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000); // if this is a fully-qualified file or the item claims to be // a Url, identify it as a Url & also offer it as html if ((isFQFile && !isAlt) || isUrl) { trans->AddDataFlavor(kURLMime); trans->AddDataFlavor(kHTMLMime); } // everything is always "text" trans->AddDataFlavor(kUnicodeMime); // if we can create the array, initialize the session nsCOMPtr transArray( do_CreateInstance("@mozilla.org/supports-array;1", &rv)); if (transArray) { transArray->InsertElementAt(trans, 0); mSourceDataItems = transArray; // add the dragged data to the transferable if we have easy access // to it (i.e. no need to read a file or request rendering); for // URLs, we'll skip creating a title until the drop occurs nsXPIDLCString someText; if (isAtom) { if (NS_SUCCEEDED(GetAtom(pditem->ulItemID, getter_Copies(someText)))) NativeDataToTransferable( someText.get(), 0, isUrl); } else if (isFQFile && !isAlt && NS_SUCCEEDED(GetFileName(pditem, getter_Copies(someText)))) { nsCOMPtr file; if (NS_SUCCEEDED(NS_NewNativeLocalFile(someText, true, getter_AddRefs(file)))) { nsCAutoString textStr; NS_GetURLSpecFromFile(file, textStr); if (!textStr.IsEmpty()) { someText.Assign(ToNewCString(textStr)); NativeDataToTransferable( someText.get(), 0, TRUE); } } } mSourceNode = 0; mSourceDocument = 0; mDoingDrag = TRUE; rv = NS_OK; } } } return rv; } // -------------------------------------------------------------------------- // Invoked after a dragover event has been dispatched, this constructs // a reply to DM_DRAGOVER based on the canDrop & dragAction attributes. NS_IMETHODIMP nsDragService::GetDragoverResult(MRESULT& mr) { nsresult rv = NS_ERROR_FAILURE; if (!&mr) return rv; if (mDoingDrag) { bool canDrop = false; USHORT usDrop; GetCanDrop(&canDrop); if (canDrop) usDrop = DOR_DROP; else usDrop = DOR_NODROP; PRUint32 action; USHORT usOp; GetDragAction(&action); if (action & DRAGDROP_ACTION_COPY) usOp = DO_COPY; else if (action & DRAGDROP_ACTION_LINK) usOp = DO_LINK; else { if (mSourceNode) usOp = DO_MOVE; else usOp = DO_UNKNOWN+1; if (action == DRAGDROP_ACTION_NONE) usDrop = DOR_NODROP; } mr = MRFROM2SHORT(usDrop, usOp); rv = NS_OK; } else mr = MRFROM2SHORT(DOR_NEVERDROP, 0); return rv; } // -------------------------------------------------------------------------- // have the client dispatch the event, then call ExitSession() NS_IMETHODIMP nsDragService::DragLeaveMsg(PDRAGINFO pdinfo, PRUint32* dragFlags) { if (!mDoingDrag || !dragFlags) return NS_ERROR_FAILURE; if (mSourceNode) *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG; else *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG; return NS_OK; } // -------------------------------------------------------------------------- // DropHelp occurs when you press F1 during a drag; apparently, // it's like a regular drop in that the target has to do clean up NS_IMETHODIMP nsDragService::DropHelpMsg(PDRAGINFO pdinfo, PRUint32* dragFlags) { if (!mDoingDrag) return NS_ERROR_FAILURE; if (pdinfo && DrgAccessDraginfo(pdinfo)) { DrgDeleteDraginfoStrHandles(pdinfo); DrgFreeDraginfo(pdinfo); } if (!dragFlags) return NS_ERROR_FAILURE; if (mSourceNode) *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG; else *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG; return NS_OK; } // -------------------------------------------------------------------------- // for native drags, clean up; // for all drags, signal that Moz is no longer in d&d mode NS_IMETHODIMP nsDragService::ExitSession(PRUint32* dragFlags) { if (!mDoingDrag) return NS_ERROR_FAILURE; if (!mSourceNode) { mSourceDataItems = 0; mDataTransfer = 0; mDoingDrag = FALSE; // if we created a temp file, delete it if (gTempFile) { DosDelete(gTempFile); nsMemory::Free(gTempFile); gTempFile = 0; } } if (!dragFlags) return NS_ERROR_FAILURE; *dragFlags = 0; return NS_OK; } // -------------------------------------------------------------------------- // If DropMsg() is presented with native data that has to be rendered, // the drop event & cleanup will be defered until the client's window // has received a render-complete msg. NS_IMETHODIMP nsDragService::DropMsg(PDRAGINFO pdinfo, HWND hwnd, PRUint32* dragFlags) { if (!mDoingDrag || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo)) return NS_ERROR_FAILURE; switch (pdinfo->usOperation) { case DO_MOVE: SetDragAction(DRAGDROP_ACTION_MOVE); break; case DO_COPY: SetDragAction(DRAGDROP_ACTION_COPY); break; case DO_LINK: SetDragAction(DRAGDROP_ACTION_LINK); break; default: // avoid "moving" (i.e. deleting) native text/objects if (mSourceNode) SetDragAction(DRAGDROP_ACTION_MOVE); else SetDragAction(DRAGDROP_ACTION_COPY); break; } // if this is a native drag, move the source data to a transferable; // request rendering if needed nsresult rv = NS_OK; bool rendering = false; if (!mSourceNode) rv = NativeDrop( pdinfo, hwnd, &rendering); // note: NativeDrop() sends an end-conversation msg to native // sources but nothing sends them to Mozilla - however, Mozilla // (i.e. nsDragService) doesn't need them, so... // if rendering, the action flags are off because we don't want // the client to do anything yet; the status flags are off because // we'll be exiting d&d mode before the next screen update occurs if (rendering) *dragFlags = 0; else { // otherwise, set the flags & free the native drag structures *dragFlags = DND_EXITSESSION; if (NS_SUCCEEDED(rv)) { if (mSourceNode) *dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_MOZDRAG; else *dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_NATIVEDRAG; } DrgDeleteDraginfoStrHandles(pdinfo); DrgFreeDraginfo(pdinfo); rv = NS_OK; } return rv; } // -------------------------------------------------------------------------- // Invoked by DropMsg to fill a transferable with native data; // if necessary, requests the source to render it. NS_IMETHODIMP nsDragService::NativeDrop(PDRAGINFO pdinfo, HWND hwnd, bool* rendering) { *rendering = false; nsresult rv = NS_ERROR_FAILURE; PDRAGITEM pditem = DrgQueryDragitemPtr(pdinfo, 0); if (!pditem) return rv; nsXPIDLCString dropText; bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator"); // identify format; the order of evaluation here is important // DRM_ATOM - DragText stores up to 255 chars in a Drg API atom // DRM_DTSHARE - DragText renders up to 1mb to named shared mem if (DrgVerifyRMF(pditem, "DRM_ATOM", 0)) rv = GetAtom(pditem->ulItemID, getter_Copies(dropText)); else if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0)) { rv = RenderToDTShare( pditem, hwnd); if (NS_SUCCEEDED(rv)) *rendering = true; } // DRM_OS2FILE - get the file's path or contents if it exists; // request rendering if it doesn't else if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) { bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000); // the file has to be rendered - currently, we only present // its content, not its name, to Moz to avoid conflicts if (!pditem->hstrContainerName || !pditem->hstrSourceName) { rv = RenderToOS2File( pditem, hwnd); if (NS_SUCCEEDED(rv)) *rendering = true; } // for Url objects and 'Alt+Drop', get the file's contents; // otherwise, convert it's path to a Url else { nsXPIDLCString fileName; if (NS_SUCCEEDED(GetFileName(pditem, getter_Copies(fileName)))) { if (isUrl || isAlt) rv = GetFileContents(fileName.get(), getter_Copies(dropText)); else { isUrl = true; nsCOMPtr file; if (NS_SUCCEEDED(NS_NewNativeLocalFile(fileName, true, getter_AddRefs(file)))) { nsCAutoString textStr; NS_GetURLSpecFromFile(file, textStr); if (!textStr.IsEmpty()) { dropText.Assign(ToNewCString(textStr)); rv = NS_OK; } } } // filename as Url } // got filename } // existing files } // DRM_OS2FILE // if OK, put what data there is in the transferable; this could be // everything needed or just the title of a Url that needs rendering if (NS_SUCCEEDED(rv)) { // for Urls, get the title & remove any linefeeds nsXPIDLCString titleText; if (isUrl && pditem->hstrTargetName && NS_SUCCEEDED(GetAtom(pditem->hstrTargetName, getter_Copies(titleText)))) for (char* ptr=strchr(titleText.BeginWriting(),'\n'); ptr; ptr=strchr(ptr, '\n')) *ptr = ' '; rv = NativeDataToTransferable( dropText.get(), titleText.get(), isUrl); } // except for renderings, tell the source we're done with the data if (!*rendering) DrgSendTransferMsg( pditem->hwndItem, DM_ENDCONVERSATION, (MPARAM)pditem->ulItemID, (MPARAM)DMFL_TARGETSUCCESSFUL); return (rv); } // -------------------------------------------------------------------------- // Because RenderCompleteMsg() is called after the native (PM) drag // session has ended, all of the drag status flags should be off. // // FYI... PM's asynchronous rendering mechanism is not compatible with // nsIDataFlavorProvider which expects data to be rendered synchronously NS_IMETHODIMP nsDragService::RenderCompleteMsg(PDRAGTRANSFER pdxfer, USHORT usResult, PRUint32* dragFlags) { nsresult rv = NS_ERROR_FAILURE; if (!mDoingDrag || !pdxfer) return rv; // this msg should never come from Moz - if it does, fail if (!mSourceNode) rv = NativeRenderComplete(pdxfer, usResult); // DrgQueryDraginfoPtrFromDragitem() doesn't work - this does PDRAGINFO pdinfo = (PDRAGINFO)MAKEULONG(0x2c, HIUSHORT(pdxfer->pditem)); DrgDeleteStrHandle(pdxfer->hstrSelectedRMF); DrgDeleteStrHandle(pdxfer->hstrRenderToName); DrgFreeDragtransfer(pdxfer); // if the source is Moz, don't touch pdinfo - it's been freed already if (pdinfo && !mSourceNode) { DrgDeleteDraginfoStrHandles(pdinfo); DrgFreeDraginfo(pdinfo); } // this shouldn't happen if (!dragFlags) return (ExitSession(dragFlags)); // d&d is over, so the DND_DragStatus flags should all be off *dragFlags = DND_EXITSESSION; if (NS_SUCCEEDED(rv)) *dragFlags |= DND_DISPATCHEVENT; // lie so the client will honor the exit-session flag return NS_OK; } // -------------------------------------------------------------------------- // this is here to provide a false sense of symmetry with the other // method-pairs - rendered data always comes from a native source NS_IMETHODIMP nsDragService::NativeRenderComplete(PDRAGTRANSFER pdxfer, USHORT usResult) { nsresult rv = NS_ERROR_FAILURE; nsXPIDLCString rmf; // identify the rendering mechanism, then get the data if (NS_SUCCEEDED(GetAtom(pdxfer->hstrSelectedRMF, getter_Copies(rmf)))) { nsXPIDLCString dropText; if (!strcmp(rmf.get(), DTSHARE_RMF)) rv = RenderToDTShareComplete(pdxfer, usResult, getter_Copies(dropText)); else if (!strcmp(rmf.get(), OS2FILE_TXTRMF) || !strcmp(rmf.get(), OS2FILE_UNKRMF)) rv = RenderToOS2FileComplete(pdxfer, usResult, true, getter_Copies(dropText)); if (NS_SUCCEEDED(rv)) { bool isUrl = false; IsDataFlavorSupported(kURLMime, &isUrl); rv = NativeDataToTransferable( dropText.get(), 0, isUrl); } } DrgSendTransferMsg(pdxfer->hwndClient, DM_ENDCONVERSATION, (MPARAM)pdxfer->ulTargetInfo, (MPARAM)DMFL_TARGETSUCCESSFUL); return rv; } // -------------------------------------------------------------------------- // fills the transferable created by NativeDragEnter with // the set of flavors and data the target will see onDrop NS_IMETHODIMP nsDragService::NativeDataToTransferable( PCSZ pszText, PCSZ pszTitle, bool isUrl) { nsresult rv = NS_ERROR_FAILURE; // the transferable should have been created on DragEnter if (!mSourceDataItems) return rv; nsCOMPtr genericItem; mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem)); nsCOMPtr trans (do_QueryInterface(genericItem)); if (!trans) return rv; // remove invalid flavors if (!isUrl) { trans->RemoveDataFlavor(kURLMime); trans->RemoveDataFlavor(kHTMLMime); } // if there's no text, exit - but first see if we have the title of // a Url that needs to be rendered; if so, stash it for later use if (!pszText || !*pszText) { if (isUrl && pszTitle && *pszTitle) { nsXPIDLString outTitle; if (CodepageToUnicode(nsDependentCString(pszTitle), getter_Copies(outTitle))) { nsCOMPtr urlPrimitive( do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); if (urlPrimitive ) { urlPrimitive->SetData(outTitle); trans->SetTransferData(kURLDescriptionMime, urlPrimitive, 2*outTitle.Length()); } } } return NS_OK; } nsXPIDLString outText; if (!CodepageToUnicode(nsDependentCString(pszText), getter_Copies(outText))) return rv; if (isUrl) { // if no title was supplied, see if it was stored in the transferable nsXPIDLString outTitle; if (pszTitle && *pszTitle) { if (!CodepageToUnicode(nsDependentCString(pszTitle), getter_Copies(outTitle))) return rv; } else { PRUint32 len; nsCOMPtr genericData; if (NS_SUCCEEDED(trans->GetTransferData(kURLDescriptionMime, getter_AddRefs(genericData), &len))) { nsCOMPtr strObject(do_QueryInterface(genericData)); if (strObject) strObject->GetData(outTitle); } } // construct the Url flavor nsCOMPtr urlPrimitive( do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); if (urlPrimitive ) { if (outTitle.IsEmpty()) { urlPrimitive->SetData(outText); trans->SetTransferData(kURLMime, urlPrimitive, 2*outText.Length()); } else { nsString urlStr( outText + NS_LITERAL_STRING("\n") + outTitle); urlPrimitive->SetData(urlStr); trans->SetTransferData(kURLMime, urlPrimitive, 2*urlStr.Length()); } rv = NS_OK; } // construct the HTML flavor - for supported graphics, // use an IMG tag, for all others create a link nsCOMPtr htmlPrimitive( do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); if (htmlPrimitive ) { nsString htmlStr; nsCOMPtr uri; rv = NS_ERROR_FAILURE; if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), pszText))) { nsCOMPtr url (do_QueryInterface(uri)); if (url) { nsCAutoString extension; url->GetFileExtension(extension); if (!extension.IsEmpty()) { if (extension.LowerCaseEqualsLiteral("gif") || extension.LowerCaseEqualsLiteral("jpg") || extension.LowerCaseEqualsLiteral("png") || extension.LowerCaseEqualsLiteral("jpeg")) rv = NS_OK; } } } if (NS_SUCCEEDED(rv)) htmlStr.Assign(NS_LITERAL_STRING("\"")") ); else htmlStr.Assign(NS_LITERAL_STRING("") + (outTitle.IsEmpty() ? outText : outTitle) + NS_LITERAL_STRING("") ); htmlPrimitive->SetData(htmlStr); trans->SetTransferData(kHTMLMime, htmlPrimitive, 2*htmlStr.Length()); rv = NS_OK; } } // add the Text flavor nsCOMPtr textPrimitive( do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); if (textPrimitive ) { textPrimitive->SetData(nsDependentString(outText)); trans->SetTransferData(kUnicodeMime, textPrimitive, 2*outText.Length()); rv = NS_OK; } // return OK if we put anything in the transferable return rv; } // -------------------------------------------------------------------------- // Helper functions // -------------------------------------------------------------------------- // currently, the same filename is used for every render request; // it is deleted when the drag session ends nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd) { nsresult rv = NS_ERROR_FAILURE; nsXPIDLCString fileName; if (NS_SUCCEEDED(GetTempFileName(getter_Copies(fileName)))) { const char * pszRMF; if (DrgVerifyRMF(pditem, "DRM_OS2FILE", "DRF_TEXT")) pszRMF = OS2FILE_TXTRMF; else pszRMF = OS2FILE_UNKRMF; rv = RequestRendering( pditem, hwnd, pszRMF, fileName.get()); } return rv; } // -------------------------------------------------------------------------- // return a buffer with the rendered file's Url or contents nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult, bool content, char** outText) { nsresult rv = NS_ERROR_FAILURE; // for now, override content flag & always return content content = true; if (usResult & DMFL_RENDEROK) { if (NS_SUCCEEDED(GetAtom( pdxfer->hstrRenderToName, &gTempFile))) { if (content) rv = GetFileContents(gTempFile, outText); else { nsCOMPtr file; if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(gTempFile), true, getter_AddRefs(file)))) { nsCAutoString textStr; NS_GetURLSpecFromFile(file, textStr); if (!textStr.IsEmpty()) { *outText = ToNewCString(textStr); rv = NS_OK; } } } } } // gTempFile will be deleted when ExitSession() is called return rv; } // -------------------------------------------------------------------------- // DTShare uses 1mb of uncommitted named-shared memory // (next time I'll do it differently - rw) nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd) { nsresult rv; void * pMem; #ifdef MOZ_OS2_HIGH_MEMORY APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000, PAG_WRITE | PAG_READ | OBJ_ANY); if (rc != NO_ERROR && rc != ERROR_ALREADY_EXISTS) { // Did the kernel handle OBJ_ANY? // Try again without OBJ_ANY and if the first failure was not caused // by OBJ_ANY then we will get the same failure, else we have taken // care of pre-FP13 systems where the kernel couldn't handle it. rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000, PAG_WRITE | PAG_READ); } #else APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000, PAG_WRITE | PAG_READ); #endif if (rc == ERROR_ALREADY_EXISTS) rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME, PAG_WRITE | PAG_READ); if (rc) rv = NS_ERROR_FAILURE; else rv = RequestRendering( pditem, hwnd, DTSHARE_RMF, DTSHARE_NAME); return rv; } // -------------------------------------------------------------------------- // return a buffer with the rendered text nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult, char** outText) { nsresult rv = NS_ERROR_FAILURE; void * pMem; char * pszText = 0; APIRET rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME, PAG_WRITE | PAG_READ); if (!rc) { if (usResult & DMFL_RENDEROK) { pszText = (char*)nsMemory::Alloc( ((ULONG*)pMem)[0] + 1); if (pszText) { strcpy(pszText, &((char*)pMem)[sizeof(ULONG)] ); RemoveCarriageReturns(pszText); *outText = pszText; rv = NS_OK; } } // using DosGetNamedSharedMem() on memory we allocated appears // to increment its usage ctr, so we have to free it 2x DosFreeMem(pMem); DosFreeMem(pMem); } return rv; } // -------------------------------------------------------------------------- // a generic request dispatcher nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName) { PDRAGTRANSFER pdxfer = DrgAllocDragtransfer( 1); if (!pdxfer) return NS_ERROR_FAILURE; pdxfer->cb = sizeof(DRAGTRANSFER); pdxfer->hwndClient = hwnd; pdxfer->pditem = pditem; pdxfer->hstrSelectedRMF = DrgAddStrHandle( pRMF); pdxfer->hstrRenderToName = 0; pdxfer->ulTargetInfo = pditem->ulItemID; pdxfer->usOperation = (USHORT)DO_COPY; pdxfer->fsReply = 0; // send the msg before setting a render-to name if (pditem->fsControl & DC_PREPAREITEM) DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0); pdxfer->hstrRenderToName = DrgAddStrHandle( pName); // send the msg after setting a render-to name if ((pditem->fsControl & (DC_PREPARE | DC_PREPAREITEM)) == DC_PREPARE) DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0); // ask the source to render the selected item if (!DrgSendTransferMsg( pditem->hwndItem, DM_RENDER, (MPARAM)pdxfer, 0)) return NS_ERROR_FAILURE; return NS_OK; } // -------------------------------------------------------------------------- // return a ptr to a buffer containing the text associated // with a drag atom; the caller frees the buffer nsresult GetAtom( ATOM aAtom, char** outText) { nsresult rv = NS_ERROR_FAILURE; ULONG ulInLength = DrgQueryStrNameLen(aAtom); if (ulInLength) { char* pszText = (char*)nsMemory::Alloc(++ulInLength); if (pszText) { DrgQueryStrName(aAtom, ulInLength, pszText); RemoveCarriageReturns(pszText); *outText = pszText; rv = NS_OK; } } return rv; } // -------------------------------------------------------------------------- // return a ptr to a buffer containing the file path specified // in the dragitem; the caller frees the buffer nsresult GetFileName(PDRAGITEM pditem, char** outText) { nsresult rv = NS_ERROR_FAILURE; ULONG cntCnr = DrgQueryStrNameLen(pditem->hstrContainerName); ULONG cntSrc = DrgQueryStrNameLen(pditem->hstrSourceName); char* pszText = (char*)nsMemory::Alloc(cntCnr+cntSrc+1); if (pszText) { DrgQueryStrName(pditem->hstrContainerName, cntCnr+1, pszText); DrgQueryStrName(pditem->hstrSourceName, cntSrc+1, &pszText[cntCnr]); pszText[cntCnr+cntSrc] = 0; *outText = pszText; rv = NS_OK; } return rv; } // -------------------------------------------------------------------------- // read the file; if successful, return a ptr to its contents; // the caller frees the buffer nsresult GetFileContents(PCSZ pszPath, char** outText) { nsresult rv = NS_ERROR_FAILURE; char* pszText = 0; 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'; RemoveCarriageReturns(pszText); *outText = pszText; rv = NS_OK; } else { nsMemory::Free(pszText); pszText = 0; } } } fclose(fp); } } return rv; } // -------------------------------------------------------------------------- // currently, this returns the same path & filename every time nsresult GetTempFileName(char** outText) { char * pszText = (char*)nsMemory::Alloc(CCHMAXPATH); if (!pszText) return NS_ERROR_FAILURE; const char * pszPath; if (!DosScanEnv("TEMP", &pszPath) || !DosScanEnv("TMP", &pszPath)) strcpy(pszText, pszPath); else if (DosQueryPathInfo(".\\.", FIL_QUERYFULLNAME, pszText, CCHMAXPATH)) pszText[0] = 0; strcat(pszText, "\\"); strcat(pszText, OS2FILE_NAME); *outText = pszText; return NS_OK; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // set the file's .TYPE and .SUBJECT EAs; since this is non-critical // (though highly desirable), errors aren't reported void SaveTypeAndSource(nsILocalFile *file, nsIDOMDocument *domDoc, PCSZ pszType) { if (!file) return; nsCOMPtr os2file(do_QueryInterface(file)); if (!os2file || NS_FAILED(os2file->SetFileTypes(nsDependentCString(pszType)))) return; // since the filetype has to be saved, this function // may be called even if there isn't any document if (!domDoc) return; nsCOMPtr doc(do_QueryInterface(domDoc)); if (!doc) return; // this gets the top-level content URL when frames are used; // when nextDoc is zero, currDoc is the browser window, and // prevDoc points to the content; // note: neither GetParentDocument() nor GetDocumentURI() // refcount the pointers they return, so nsCOMPtr isn't needed nsIDocument *prevDoc; nsIDocument *currDoc = doc; nsIDocument *nextDoc = doc; do { prevDoc = currDoc; currDoc = nextDoc; nextDoc = currDoc->GetParentDocument(); } while (nextDoc); nsIURI* srcUri = prevDoc->GetDocumentURI(); if (!srcUri) return; // identifying content as coming from chrome is none too useful... bool ignore = false; srcUri->SchemeIs("chrome", &ignore); if (ignore) return; nsCAutoString url; srcUri->GetSpec(url); os2file->SetFileSource(url); return; } // -------------------------------------------------------------------------- // to do: this needs to take into account the current page's encoding // if it is different than the PM codepage int UnicodeToCodepage(const nsAString& aString, char **aResult) { nsAutoCharBuffer buffer; PRInt32 bufLength; WideCharToMultiByte(0, PromiseFlatString(aString).get(), aString.Length(), buffer, bufLength); *aResult = ToNewCString(nsDependentCString(buffer.Elements())); return bufLength; } // -------------------------------------------------------------------------- int CodepageToUnicode(const nsACString& aString, PRUnichar **aResult) { nsAutoChar16Buffer buffer; PRInt32 bufLength; MultiByteToWideChar(0, PromiseFlatCString(aString).get(), aString.Length(), buffer, bufLength); *aResult = ToNewUnicode(nsDependentString(buffer.Elements())); return bufLength; } // -------------------------------------------------------------------------- // removes carriage returns in-place; it should only be used on // raw text buffers that haven't been assigned to a string object void RemoveCarriageReturns(char * pszText) { ULONG cnt; char * next; char * source; char * target; target = strchr(pszText, 0x0d); if (!target) return; source = target + 1; while ((next = strchr(source, 0x0d)) != 0) { cnt = next - source; memcpy(target, source, cnt); target += cnt; source = next + 1; } strcpy(target, source); return; } // --------------------------------------------------------------------------