diff --git a/widget/src/gtk2/nsFilePicker.cpp b/widget/src/gtk2/nsFilePicker.cpp index a1b32d49133..4860813f390 100644 --- a/widget/src/gtk2/nsFilePicker.cpp +++ b/widget/src/gtk2/nsFilePicker.cpp @@ -55,11 +55,11 @@ #include "nsFilePicker.h" -/* from nsFilePicker.js */ -#define XULFILEPICKER_CID \ - { 0x54ae32f8, 0x1dd2, 0x11b2, \ - { 0xa2, 0x09, 0xdf, 0x7c, 0x50, 0x53, 0x70, 0xf8} } -static NS_DEFINE_CID(kXULFilePickerCID, XULFILEPICKER_CID); +#define DECL_FUNC_PTR(func) static _##func##_fn _##func +#define GTK_FILE_CHOOSER(widget) ((GtkFileChooser*) widget) + +PRLibrary *nsFilePicker::mGTK24 = nsnull; +nsILocalFile *nsFilePicker::mPrevDisplayDirectory = nsnull; // XXX total ass. We should really impose a build-time requirement on gtk2.4 // and then check at run-time whether the user is actually running gtk2.4. @@ -90,17 +90,18 @@ typedef GtkWidget* (*_gtk_file_chooser_dialog_new_fn)(const gchar *title, const gchar *third_button_text /* NULL */); typedef void (*_gtk_file_chooser_set_select_multiple_fn)(GtkFileChooser* chooser, gboolean truth); typedef void (*_gtk_file_chooser_set_current_name_fn)(GtkFileChooser* chooser, const gchar* name); +typedef void (*_gtk_file_chooser_set_current_folder_fn)(GtkFileChooser* chooser, const gchar* folder); typedef void (*_gtk_file_chooser_add_filter_fn)(GtkFileChooser* chooser, GtkFileFilter* filter); typedef GtkFileFilter* (*_gtk_file_filter_new_fn)(); typedef void (*_gtk_file_filter_add_pattern_fn)(GtkFileFilter* filter, const gchar* pattern); typedef void (*_gtk_file_filter_set_name_fn)(GtkFileFilter* filter, const gchar* name); -#define DECL_FUNC_PTR(func) static _##func##_fn _##func DECL_FUNC_PTR(gtk_file_chooser_get_filename); DECL_FUNC_PTR(gtk_file_chooser_dialog_new); DECL_FUNC_PTR(gtk_file_chooser_set_select_multiple); DECL_FUNC_PTR(gtk_file_chooser_set_current_name); +DECL_FUNC_PTR(gtk_file_chooser_set_current_folder); DECL_FUNC_PTR(gtk_file_chooser_add_filter); DECL_FUNC_PTR(gtk_file_filter_new); DECL_FUNC_PTR(gtk_file_filter_add_pattern); @@ -117,29 +118,34 @@ LoadVersionedLibrary(const char* libName, const char* libVersion) return PR_LoadLibrary(versionLibName.get()); } -static nsresult -InitGTK24() +/* static */ +nsresult +nsFilePicker::LoadSymbolsGTK24() { - static PRLibrary *libgtk24 = nsnull; + static PRBool initialized; + if (initialized) { + return NS_OK; + } + + initialized = PR_TRUE; #define GET_LIBGTK_FUNC(func) \ PR_BEGIN_MACRO \ - _##func = (_##func##_fn) PR_FindFunctionSymbol(libgtk24, #func); \ + _##func = (_##func##_fn) PR_FindFunctionSymbol(mGTK24, #func); \ if (!_##func) { \ NS_WARNING("Can't load ##func##"); \ return NS_ERROR_NOT_AVAILABLE; \ } \ PR_END_MACRO - // This is easier PRFuncPtr func = PR_FindFunctionSymbolAndLibrary("gtk_file_chooser_get_filename", - &libgtk24); - if (libgtk24) { + &mGTK24); + if (mGTK24) { _gtk_file_chooser_get_filename = (_gtk_file_chooser_get_filename_fn)func; } else { // XXX hmm, this seems to fail when gtk 2.4 is already loaded... - libgtk24 = LoadVersionedLibrary("gtk-2", ".4"); - if (!libgtk24) { + mGTK24 = LoadVersionedLibrary("gtk-2", ".4"); + if (!mGTK24) { return NS_ERROR_NOT_AVAILABLE; } GET_LIBGTK_FUNC(gtk_file_chooser_get_filename); @@ -148,6 +154,7 @@ InitGTK24() GET_LIBGTK_FUNC(gtk_file_chooser_dialog_new); GET_LIBGTK_FUNC(gtk_file_chooser_set_select_multiple); GET_LIBGTK_FUNC(gtk_file_chooser_set_current_name); + GET_LIBGTK_FUNC(gtk_file_chooser_set_current_folder); GET_LIBGTK_FUNC(gtk_file_chooser_add_filter); GET_LIBGTK_FUNC(gtk_file_filter_new); GET_LIBGTK_FUNC(gtk_file_filter_add_pattern); @@ -157,6 +164,14 @@ InitGTK24() return NS_OK; } +void +nsFilePicker::Shutdown() +{ + PR_UnloadLibrary(mGTK24); + + NS_IF_RELEASE(mPrevDisplayDirectory); +} + // Ok, lib loading crap is done... the real code starts here: static GtkFileChooserAction @@ -186,20 +201,8 @@ GetGtkFileChooserAction(PRInt16 aMode) return action; } -nsString nsFilePicker::mLastUsedUnicodeDirectory; -//NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker) - -NS_IMPL_ADDREF(nsFilePicker) -NS_IMPL_RELEASE(nsFilePicker) - -NS_INTERFACE_MAP_BEGIN(nsFilePicker) - if (mXULPicker) { - return mXULPicker->QueryInterface(aIID, aInstancePtr); - } - NS_INTERFACE_MAP_ENTRY(nsIFilePicker) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFilePicker) -NS_INTERFACE_MAP_END +NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker) nsFilePicker::nsFilePicker() : mMode(nsIFilePicker::modeOpen), @@ -207,7 +210,6 @@ nsFilePicker::nsFilePicker() { } - nsFilePicker::~nsFilePicker() { } @@ -216,7 +218,7 @@ void nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser) { // Grab the filename - gchar *filename = _gtk_file_chooser_get_filename ((GtkFileChooser*) (file_chooser)); + gchar *filename = _gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_chooser)); mFile.Assign(filename); g_free(filename); @@ -228,7 +230,7 @@ nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser) file->GetParent(getter_AddRefs(dir)); nsCOMPtr localDir(do_QueryInterface(dir)); if (localDir) { - mDisplayDirectory = localDir; + localDir.swap(mPrevDisplayDirectory); } } } @@ -236,26 +238,12 @@ nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser) NS_IMETHODIMP nsFilePicker::Init(nsIDOMWindow *aParent, const nsAString &aTitle, PRInt16 aMode) { - // The GtkFileChooser API was first exposed in GTK+ 2.4. Make sure we have it, - // else fall back to the XUL picker. - nsresult rv = InitGTK24(); + nsresult rv = LoadSymbolsGTK24(); if (NS_FAILED(rv)) { - NS_WARNING("Couldn't load GTK+ 2.4 -- falling back to XUL file chooser."); - mXULPicker = do_CreateInstance(kXULFilePickerCID); - if (!mXULPicker) { - NS_WARNING("Couldn't load XUL file chooser. Running in circles!"); - return NS_ERROR_NOT_AVAILABLE; - } - - return mXULPicker->Init(aParent, aTitle, aMode); + return rv; } - nsIWidget *parent = DOMWindowToWidget(aParent); - NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE); - - InitNative(parent, aTitle, aMode); - - return NS_OK; + return nsBaseFilePicker::Init(aParent, aTitle, aMode); } void @@ -271,20 +259,12 @@ nsFilePicker::InitNative(nsIWidget *aParent, NS_IMETHODIMP nsFilePicker::AppendFilters(PRInt32 aFilterMask) { - if (mXULPicker) { - return mXULPicker->AppendFilters(aFilterMask); - } - return nsBaseFilePicker::AppendFilters(aFilterMask); } NS_IMETHODIMP nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) { - if (mXULPicker) { - return mXULPicker->AppendFilter(aTitle, aFilter); - } - if (aFilter.Equals(NS_LITERAL_STRING("..apps"))) { // No platform specific thing we can do here, really.... return NS_OK; @@ -303,10 +283,6 @@ nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString) { - if (mXULPicker) { - return mXULPicker->SetDefaultString(aString); - } - mDefault = aString; return NS_OK; @@ -315,10 +291,6 @@ nsFilePicker::SetDefaultString(const nsAString& aString) NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString) { - if (mXULPicker) { - return mXULPicker->GetDefaultString(aString); - } - // Per API... return NS_ERROR_FAILURE; } @@ -326,10 +298,6 @@ nsFilePicker::GetDefaultString(nsAString& aString) NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) { - if (mXULPicker) { - return mXULPicker->SetDefaultExtension(aExtension); - } - mDefaultExtension = aExtension; return NS_OK; @@ -338,10 +306,6 @@ nsFilePicker::SetDefaultExtension(const nsAString& aExtension) NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) { - if (mXULPicker) { - return mXULPicker->GetDefaultExtension(aExtension); - } - aExtension = mDefaultExtension; return NS_OK; @@ -350,10 +314,6 @@ nsFilePicker::GetDefaultExtension(nsAString& aExtension) NS_IMETHODIMP nsFilePicker::GetFilterIndex(PRInt32 *aFilterIndex) { - if (mXULPicker) { - return mXULPicker->GetFilterIndex(aFilterIndex); - } - *aFilterIndex = mSelectedType; return NS_OK; @@ -362,10 +322,6 @@ nsFilePicker::GetFilterIndex(PRInt32 *aFilterIndex) NS_IMETHODIMP nsFilePicker::SetFilterIndex(PRInt32 aFilterIndex) { - if (mXULPicker) { - return mXULPicker->SetFilterIndex(aFilterIndex); - } - mSelectedType = aFilterIndex; return NS_OK; @@ -374,10 +330,6 @@ nsFilePicker::SetFilterIndex(PRInt32 aFilterIndex) NS_IMETHODIMP nsFilePicker::SetDisplayDirectory(nsILocalFile *aDirectory) { - if (mXULPicker) { - return mXULPicker->SetDisplayDirectory(aDirectory); - } - mDisplayDirectory = aDirectory; return NS_OK; @@ -386,10 +338,6 @@ nsFilePicker::SetDisplayDirectory(nsILocalFile *aDirectory) NS_IMETHODIMP nsFilePicker::GetDisplayDirectory(nsILocalFile **aDirectory) { - if (mXULPicker) { - return mXULPicker->GetDisplayDirectory(aDirectory); - } - NS_IF_ADDREF(*aDirectory = mDisplayDirectory); return NS_OK; @@ -398,10 +346,6 @@ nsFilePicker::GetDisplayDirectory(nsILocalFile **aDirectory) NS_IMETHODIMP nsFilePicker::GetFile(nsILocalFile **aFile) { - if (mXULPicker) { - return mXULPicker->GetFile(aFile); - } - NS_ENSURE_ARG_POINTER(aFile); *aFile = nsnull; @@ -422,10 +366,6 @@ nsFilePicker::GetFile(nsILocalFile **aFile) NS_IMETHODIMP nsFilePicker::GetFileURL(nsIFileURL **aFileURL) { - if (mXULPicker) { - return mXULPicker->GetFileURL(aFileURL); - } - nsCOMPtr file; GetFile(getter_AddRefs(file)); @@ -439,10 +379,6 @@ nsFilePicker::GetFileURL(nsIFileURL **aFileURL) NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles) { - if (mXULPicker) { - return mXULPicker->GetFiles(aFiles); - } - NS_ENSURE_ARG_POINTER(aFiles); if (mMode == nsIFilePicker::modeOpenMultiple) { @@ -455,10 +391,6 @@ nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles) NS_IMETHODIMP nsFilePicker::Show(PRInt16 *aReturn) { - if (mXULPicker) { - return mXULPicker->Show(aReturn); - } - NS_ENSURE_ARG_POINTER(aReturn); nsXPIDLCString title; @@ -475,16 +407,30 @@ nsFilePicker::Show(PRInt16 *aReturn) accept_button, GTK_RESPONSE_ACCEPT, NULL); if (mMode == nsIFilePicker::modeOpenMultiple) { - _gtk_file_chooser_set_select_multiple ((GtkFileChooser*) (file_chooser), TRUE); + _gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(file_chooser), TRUE); } if (mMode == nsIFilePicker::modeSave) { char *default_filename = ToNewUTF8String(mDefault); - _gtk_file_chooser_set_current_name((GtkFileChooser*) (file_chooser), + _gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), NS_STATIC_CAST(const gchar*, default_filename)); nsMemory::Free(default_filename); } + gtk_dialog_set_default_response(GTK_DIALOG(file_chooser), GTK_RESPONSE_ACCEPT); + + nsCAutoString directory; + if (mDisplayDirectory) { + mDisplayDirectory->GetNativePath(directory); + } else if (mPrevDisplayDirectory) { + mPrevDisplayDirectory->GetNativePath(directory); + } + + if (!directory.IsEmpty()) { + _gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser), + directory.get()); + } + PRInt32 count = mFilters.Count(); for (PRInt32 i = 0; i < count; ++i) { GtkFileFilter *filter = _gtk_file_filter_new (); @@ -512,7 +458,6 @@ nsFilePicker::Show(PRInt16 *aReturn) --cur; const char* pattern = ToNewCString(Substring(start, cur)); _gtk_file_filter_add_pattern(filter, pattern); - printf("%s\n", pattern); nsMemory::Free((void*)pattern); ++cur; ++cur; start = cur; diff --git a/widget/src/gtk2/nsFilePicker.h b/widget/src/gtk2/nsFilePicker.h index c7ad0d7d1c3..63232aaab91 100644 --- a/widget/src/gtk2/nsFilePicker.h +++ b/widget/src/gtk2/nsFilePicker.h @@ -47,6 +47,7 @@ class nsIWidget; class nsILocalFile; class nsISupportsArray; +class PRLibrary; class nsFilePicker : public nsBaseFilePicker { @@ -60,7 +61,10 @@ public: virtual void InitNative(nsIWidget *aParent, const nsAString& aTitle, PRInt16 aMode); + static void Shutdown(); + protected: + static nsresult LoadSymbolsGTK24(); void ReadValuesFromFileChooser(GtkWidget *file_chooser); @@ -78,10 +82,9 @@ protected: nsCStringArray mFilters; nsCStringArray mFilterNames; - /* If we don't have GTK2.4, we need to proxy calls on to a XUL filepicker */ - nsCOMPtr mXULPicker; - - static nsString mLastUsedUnicodeDirectory; +private: + static nsILocalFile *mPrevDisplayDirectory; + static PRLibrary *mGTK24; }; #endif diff --git a/widget/src/gtk2/nsWidgetFactory.cpp b/widget/src/gtk2/nsWidgetFactory.cpp index 0112dfa7040..e3bc507476e 100644 --- a/widget/src/gtk2/nsWidgetFactory.cpp +++ b/widget/src/gtk2/nsWidgetFactory.cpp @@ -52,6 +52,18 @@ #include "nsSound.h" #include "nsBidiKeyboard.h" +#include "nsIComponentRegistrar.h" +#include "nsComponentManagerUtils.h" +#include "nsAutoPtr.h" +#include + +/* from nsFilePicker.js */ +#define XULFILEPICKER_CID \ + { 0x54ae32f8, 0x1dd2, 0x11b2, \ + { 0xa2, 0x09, 0xdf, 0x7c, 0x50, 0x53, 0x70, 0xf8} } +static NS_DEFINE_CID(kXULFilePickerCID, XULFILEPICKER_CID); +static NS_DEFINE_CID(kNativeFilePickerCID, NS_FILEPICKER_CID); + NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppShell) @@ -63,9 +75,79 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound) +static NS_IMETHODIMP +nsFilePickerConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + *aResult = nsnull; + if (aOuter != nsnull) { + return NS_ERROR_NO_AGGREGATION; + } + + nsCOMPtr picker; + if (gtk_check_version(2,4,0) == NULL) { + picker = new nsFilePicker; + } else { + picker = do_CreateInstance(kXULFilePickerCID); + } + + if (!picker) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return picker->QueryInterface(aIID, aResult); +} + +static NS_IMETHODIMP +nsFilePickerRegisterSelf(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *aLoaderStr, + const char *aType, + const nsModuleComponentInfo *aInfo) +{ + static PRBool been_here; + if (!been_here) { + been_here = PR_TRUE; + + // Make sure we re-register this so it gets picked up after the XUL picker. + return NS_ERROR_FACTORY_REGISTER_AGAIN; + } + + nsresult rv; + nsCOMPtr compReg = do_QueryInterface(aCompMgr, &rv); + + if (NS_SUCCEEDED(rv) && compReg) { + rv = compReg->RegisterFactoryLocation(kNativeFilePickerCID, + "Gtk2 File Picker", + "@mozilla.org/filepicker;1", + aPath, aLoaderStr, aType); + } + + return rv; +} + +static NS_IMETHODIMP +nsFilePickerUnregisterSelf(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *aLoaderStr, + const nsModuleComponentInfo *aInfo) +{ + nsFilePicker::Shutdown(); + + nsresult rv; + nsCOMPtr compReg = do_QueryInterface(aCompMgr, &rv); + + if (NS_SUCCEEDED(rv) && compReg) { + rv = compReg->UnregisterFactoryLocation(kNativeFilePickerCID, + aPath); + } + + return rv; +} + + static const nsModuleComponentInfo components[] = { { "Gtk2 Window", @@ -87,7 +169,9 @@ static const nsModuleComponentInfo components[] = { "Gtk2 File Picker", NS_FILEPICKER_CID, "@mozilla.org/filepicker;1", - nsFilePickerConstructor }, + nsFilePickerConstructor, + nsFilePickerRegisterSelf, + nsFilePickerUnregisterSelf }, { "Gtk2 Sound", NS_SOUND_CID, "@mozilla.org/sound;1",