/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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 the Mozilla browser. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Stuart Parmenter */ // Define so header files for openfilename are included #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN #endif #include "nsCOMPtr.h" #include "nsReadableUtils.h" #include "nsNetUtil.h" #include "nsIServiceManager.h" #define NS_IMPL_IDS #include "nsIPlatformCharset.h" #undef NS_IMPL_IDS #include "nsFilePicker.h" #include "nsILocalFile.h" #include "nsIFileChannel.h" #include "nsIURL.h" #include "nsIStringBundle.h" #include #include static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker) //------------------------------------------------------------------------- // // nsFilePicker constructor // //------------------------------------------------------------------------- nsFilePicker::nsFilePicker() { NS_INIT_REFCNT(); mWnd = NULL; mUnicodeEncoder = nsnull; mUnicodeDecoder = nsnull; mDisplayDirectory = do_CreateInstance("@mozilla.org/file/local;1"); } //------------------------------------------------------------------------- // // nsFilePicker destructor // //------------------------------------------------------------------------- nsFilePicker::~nsFilePicker() { NS_IF_RELEASE(mUnicodeEncoder); NS_IF_RELEASE(mUnicodeDecoder); } //------------------------------------------------------------------------- // // Show - Display the file dialog // //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval) { NS_ENSURE_ARG_POINTER(retval); PRBool result = PR_FALSE; char fileBuffer[MAX_PATH+1] = ""; char *converted = ConvertToFileSystemCharset(mDefault.get()); if (nsnull == converted) { mDefault.ToCString(fileBuffer,MAX_PATH); } else { PL_strncpyz(fileBuffer, converted, MAX_PATH+1); nsMemory::Free( converted ); } char htmExt[] = "html"; char *title = ConvertToFileSystemCharset(mTitle.get()); if (nsnull == title) title = ToNewCString(mTitle); char *initialDir; mDisplayDirectory->GetPath(&initialDir); mFile.SetLength(0); if (mMode == modeGetFolder) { char dirBuffer[MAX_PATH+1]; PL_strncpy(dirBuffer, initialDir, MAX_PATH); BROWSEINFO browserInfo; browserInfo.hwndOwner = mWnd; browserInfo.pidlRoot = nsnull; browserInfo.pszDisplayName = (LPSTR)dirBuffer; browserInfo.lpszTitle = title; browserInfo.ulFlags = BIF_RETURNONLYFSDIRS;//BIF_STATUSTEXT | BIF_RETURNONLYFSDIRS; browserInfo.lpfn = nsnull; browserInfo.lParam = nsnull; browserInfo.iImage = nsnull; // XXX UNICODE support is needed here --> DONE LPITEMIDLIST list = ::SHBrowseForFolder(&browserInfo); if (list != NULL) { result = ::SHGetPathFromIDList(list, (LPSTR)fileBuffer); if (result) { mFile.Append(fileBuffer); } // free PIDL LPMALLOC pMalloc = NULL; ::SHGetMalloc(&pMalloc); if(pMalloc) { pMalloc->Free(list); pMalloc->Release(); } } } else { OPENFILENAME ofn; memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); PRInt32 l = (mFilterList.Length()+2)*2; char *filterBuffer = (char*) nsMemory::Alloc(l); int len = WideCharToMultiByte(CP_ACP, 0, mFilterList.get(), mFilterList.Length(), filterBuffer, l, NULL, NULL); filterBuffer[len] = NULL; filterBuffer[len+1] = NULL; if (initialDir && *initialDir) { ofn.lpstrInitialDir = initialDir; } ofn.lpstrTitle = title; ofn.lpstrFilter = filterBuffer; ofn.nFilterIndex = 1; ofn.hwndOwner = mWnd; ofn.lpstrFile = fileBuffer; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_NOCHANGEDIR | OFN_SHAREAWARE | OFN_LONGNAMES | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; // Get file extension from suggested filename // to detect if we are saving an html file //XXX: nsIFile SHOULD HAVE A GetExtension() METHOD! PRInt32 extIndex = mDefault.RFind("."); if ( extIndex >= 0) { nsAutoString ext; mDefault.Right(ext, mDefault.Length() - extIndex); // Should we test for ".cgi", ".asp", ".jsp" and other "generated" html pages? if ( ext.EqualsIgnoreCase(".htm") || ext.EqualsIgnoreCase(".html") || ext.EqualsIgnoreCase(".shtml") ) { // This is supposed to append ".htm" if user doesn't supply an extension //XXX Actually, behavior is sort of weird: // often appends ".html" even if you have an extension // It obeys your extension if you put quotes around name ofn.lpstrDefExt = htmExt; } } if (mMode == modeOpen) { // FILE MUST EXIST! ofn.Flags |= OFN_FILEMUSTEXIST; result = ::GetOpenFileName(&ofn); } else if (mMode == modeSave) { result = ::GetSaveFileName(&ofn); if (!result) { // Error, find out what kind. DWORD error = ::GetLastError(); if (error == ERROR_INVALID_PARAMETER) { // Parameter error; probably the default file name is too long. // Try again, without a starting file name. ofn.lpstrFile[0] = 0; result = ::GetSaveFileName(&ofn); } } } else { NS_ASSERTION(0, "Only load, save and getFolder are supported modes"); } // Remember what filter type the user selected mSelectedType = (PRInt16)ofn.nFilterIndex; // Store the current directory in mDisplayDirectory char* newCurrentDirectory = NS_STATIC_CAST( char*, nsMemory::Alloc( MAX_PATH+1 ) ); VERIFY(::GetCurrentDirectory(MAX_PATH, newCurrentDirectory) > 0); mDisplayDirectory->InitWithPath(newCurrentDirectory); nsMemory::Free( newCurrentDirectory ); // Clean up filter buffers if (filterBuffer) nsMemory::Free( filterBuffer ); // Set user-selected location of file or directory if (result == PR_TRUE) { // I think it also needs a conversion here (to unicode since appending to nsString) // but doing that generates garbage file name, weird. mFile.Append(fileBuffer); } } if (initialDir) nsMemory::Free(initialDir); if (title) nsMemory::Free( title ); if (result) { PRInt16 returnOKorReplace = returnOK; if (mMode == modeSave) { // Windows does not return resultReplace, // we must check if file already exists nsCOMPtr file(do_CreateInstance("@mozilla.org/file/local;1")); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); file->InitWithPath(mFile.get()); PRBool exists = PR_FALSE; file->Exists(&exists); if (exists) returnOKorReplace = returnReplace; } *retval = returnOKorReplace; } else { *retval = returnCancel; } return NS_OK; } NS_IMETHODIMP nsFilePicker::GetFile(nsILocalFile **aFile) { NS_ENSURE_ARG_POINTER(aFile); if (mFile.IsEmpty()) return NS_OK; nsCOMPtr file(do_CreateInstance("@mozilla.org/file/local;1")); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); file->InitWithPath(mFile.get()); NS_ADDREF(*aFile = file); return NS_OK; } //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::GetFileURL(nsIFileURL **aFileURL) { nsCOMPtr file(do_CreateInstance("@mozilla.org/file/local;1")); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); file->InitWithPath(mFile.get()); nsCOMPtr uri; NS_NewFileURI(getter_AddRefs(uri), file); nsCOMPtr fileURL(do_QueryInterface(uri)); NS_ENSURE_TRUE(fileURL, NS_ERROR_FAILURE); NS_ADDREF(*aFileURL = fileURL); return NS_OK; } //------------------------------------------------------------------------- // // Get the file + path // //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::SetDefaultString(const PRUnichar *aString) { mDefault = aString; return NS_OK; } NS_IMETHODIMP nsFilePicker::GetDefaultString(PRUnichar **aString) { return NS_ERROR_FAILURE; } //------------------------------------------------------------------------- // // Set the filter index // //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::GetFilterIndex(PRInt32 *aFilterIndex) { // Windows' filter index is 1-based, we use a 0-based system. *aFilterIndex = mSelectedType - 1; return NS_OK; } NS_IMETHODIMP nsFilePicker::SetFilterIndex(PRInt32 aFilterIndex) { // Windows' filter index is 1-based, we use a 0-based system. mSelectedType = aFilterIndex + 1; return NS_OK; } //------------------------------------------------------------------------- // // Set the display directory // //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::SetDisplayDirectory(nsILocalFile *aDirectory) { mDisplayDirectory = aDirectory; return NS_OK; } //------------------------------------------------------------------------- // // Get the display directory // //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::GetDisplayDirectory(nsILocalFile **aDirectory) { *aDirectory = mDisplayDirectory; NS_IF_ADDREF(*aDirectory); return NS_OK; } //------------------------------------------------------------------------- NS_IMETHODIMP nsFilePicker::InitNative(nsIWidget *aParent, const PRUnichar *aTitle, PRInt16 aMode) { mWnd = (HWND) ((aParent) ? aParent->GetNativeData(NS_NATIVE_WINDOW) : 0); mTitle.SetLength(0); mTitle.Append(aTitle); mMode = aMode; return NS_OK; } //------------------------------------------------------------------------- void nsFilePicker::GetFileSystemCharset(nsString & fileSystemCharset) { static nsAutoString aCharset; nsresult rv; if (aCharset.Length() < 1) { nsCOMPtr platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, aCharset); NS_ASSERTION(NS_SUCCEEDED(rv), "error getting platform charset"); if (NS_FAILED(rv)) aCharset.Assign(NS_LITERAL_STRING("windows-1252")); } fileSystemCharset = aCharset; } //------------------------------------------------------------------------- char * nsFilePicker::ConvertToFileSystemCharset(const PRUnichar *inString) { char *outString = nsnull; nsresult rv = NS_OK; // get file system charset and create a unicode encoder if (nsnull == mUnicodeEncoder) { nsAutoString fileSystemCharset; GetFileSystemCharset(fileSystemCharset); nsCOMPtr ccm = do_GetService(kCharsetConverterManagerCID, &rv); if (NS_SUCCEEDED(rv)) { rv = ccm->GetUnicodeEncoder(&fileSystemCharset, &mUnicodeEncoder); } } // converts from unicode to the file system charset if (NS_SUCCEEDED(rv)) { PRInt32 inLength = nsCRT::strlen(inString); PRInt32 outLength; rv = mUnicodeEncoder->GetMaxLength(inString, inLength, &outLength); if (NS_SUCCEEDED(rv)) { outString = NS_STATIC_CAST( char*, nsMemory::Alloc( outLength+1 ) ); if (nsnull == outString) { return nsnull; } rv = mUnicodeEncoder->Convert(inString, &inLength, outString, &outLength); if (NS_SUCCEEDED(rv)) { outString[outLength] = '\0'; } } } return NS_SUCCEEDED(rv) ? outString : nsnull; } //------------------------------------------------------------------------- PRUnichar * nsFilePicker::ConvertFromFileSystemCharset(const char *inString) { PRUnichar *outString = nsnull; nsresult rv = NS_OK; // get file system charset and create a unicode encoder if (nsnull == mUnicodeDecoder) { nsAutoString fileSystemCharset; GetFileSystemCharset(fileSystemCharset); nsCOMPtr ccm = do_GetService(kCharsetConverterManagerCID, &rv); if (NS_SUCCEEDED(rv)) { rv = ccm->GetUnicodeDecoder(&fileSystemCharset, &mUnicodeDecoder); } } // converts from the file system charset to unicode if (NS_SUCCEEDED(rv)) { PRInt32 inLength = nsCRT::strlen(inString); PRInt32 outLength; rv = mUnicodeDecoder->GetMaxLength(inString, inLength, &outLength); if (NS_SUCCEEDED(rv)) { outString = NS_STATIC_CAST( PRUnichar*, nsMemory::Alloc( (outLength+1) * sizeof( PRUnichar ) ) ); if (nsnull == outString) { return nsnull; } rv = mUnicodeDecoder->Convert(inString, &inLength, outString, &outLength); if (NS_SUCCEEDED(rv)) { outString[outLength] = 0; } } } NS_ASSERTION(NS_SUCCEEDED(rv), "error charset conversion"); return NS_SUCCEEDED(rv) ? outString : nsnull; } NS_IMETHODIMP nsFilePicker::AppendFilter(const PRUnichar *aTitle, const PRUnichar *aFilter) { mFilterList.Append(aTitle); mFilterList.Append(PRUnichar('\0')); mFilterList.Append(aFilter); mFilterList.Append(PRUnichar('\0')); return NS_OK; }