/* * Copyright (C) 2001 Philip Langdale * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Things to be aware of: * * This filepicker, like the mozilla one, does not make an attempt * to verify the validity of the initial directory you pass it. * It does check that the user doesn't give it a garbage path * during use, but it is the caller's responsibility to give a * sensible initial path. * * At the current moment, we instantiate the filepicker directly * in our contenthandler where there is path verification code * and else where through our C wrapper, which also does verification. * If, at a future date, you need to instantiate filepicker without * using the C wrapper, please verify the initial path. See * ContentHandler for a way to do this. */ #include "ephy-string.h" #include "ephy-gui.h" #include "eel-gconf-extensions.h" #include #include #include #include #include #include #include #include #include #include #include #include "nsIFilePicker.h" #include "nsCRT.h" #include "nsCOMPtr.h" #include "nsIFactory.h" #include "nsISupportsArray.h" #include "nsIServiceManager.h" #include "nsXPComFactory.h" #include "nsString.h" #include "nsXPIDLString.h" #include "nsIPrefService.h" #include "nsIURI.h" #include "nsIFileURL.h" #include "nsIChannel.h" #include "nsIFileChannel.h" #include "nsNetCID.h" #include "nsILocalFile.h" #include "nsIPromptService.h" #include "nsReadableUtils.h" #include #include "FilePicker.h" #include "MozillaPrivate.h" void filePicker_save_content_cb(GtkToggleButton *aButton, GFilePicker *aFilePicker); /* Implementation file */ NS_IMPL_ISUPPORTS1(GFilePicker, nsIFilePicker) GFilePicker::GFilePicker(PRBool showContentCheck, FileFormat *fileFormats) : mSaveContent(PR_FALSE) { NS_INIT_ISUPPORTS(); /* member initializers and constructor code */ mShowContentCheck = showContentCheck; mFileFormats = fileFormats; mFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID); mDisplayDirectory = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID); mDisplayDirectory->InitWithNativePath(nsDependentCString(g_get_home_dir())); } GFilePicker::~GFilePicker() { /* destructor code */ } //////////////////////////////////////////////////////////////////////////////// // begin nsIFilePicker impl //////////////////////////////////////////////////////////////////////////////// /* void init (in nsIDOMWindowInternal parent, in wstring title, in short mode); */ NS_IMETHODIMP GFilePicker::Init(nsIDOMWindowInternal *aParent, const PRUnichar *aTitle, PRInt16 aMode) { mParent = do_QueryInterface(aParent); mParentWidget = MozillaFindGtkParent(mParent); mTitle = NS_ConvertUCS2toUTF8(aTitle); mMode = aMode; return NS_OK; } /* void appendFilters (in long filterMask); */ NS_IMETHODIMP GFilePicker::AppendFilters(PRInt32 aFilterMask) { //This function cannot be implemented due to the crippled //nature of GtkFileSelection, but NS_ERROR_NOT_IMPLEMENTED //is interpreted as a terminal error by some callers. return NS_OK; } /* void appendFilter (in wstring title, in wstring filter); */ NS_IMETHODIMP GFilePicker::AppendFilter(const PRUnichar *aTitle, const PRUnichar *aFilter) { //GtkFileSelection is crippled, so we can't provide a short-list //of filters to choose from. We provide minimal functionality //by using the most recent AppendFilter call as the active filter. mFilter = NS_ConvertUCS2toUTF8(aFilter); return NS_OK; } /* attribute long filterIndex; */ NS_IMETHODIMP GFilePicker::GetFilterIndex(PRInt32 *aFilterIndex) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP GFilePicker::SetFilterIndex(PRInt32 aFilterIndex) { return NS_OK; } /* attribute wstring defaultString; */ NS_IMETHODIMP GFilePicker::GetDefaultString(PRUnichar * *aDefaultString) { *aDefaultString = ToNewUnicode(NS_ConvertUTF8toUCS2(mDefaultString)); return NS_OK; } NS_IMETHODIMP GFilePicker::SetDefaultString(const PRUnichar *aDefaultString) { if (aDefaultString) mDefaultString = NS_ConvertUCS2toUTF8(aDefaultString); else mDefaultString = ""; return NS_OK; } /* attribute wstring defaultExtension; */ // Again, due to the crippled file selector, we can't really // do anything here. NS_IMETHODIMP GFilePicker::GetDefaultExtension(PRUnichar * *aDefaultExtension) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP GFilePicker::SetDefaultExtension(const PRUnichar *aDefaultExtension) { return NS_OK; } /* attribute nsILocalFile displayDirectory; */ NS_IMETHODIMP GFilePicker::GetDisplayDirectory(nsILocalFile * *aDisplayDirectory) { NS_IF_ADDREF(*aDisplayDirectory = mDisplayDirectory); return NS_OK; } NS_IMETHODIMP GFilePicker::SetDisplayDirectory(nsILocalFile * aDisplayDirectory) { mDisplayDirectory = aDisplayDirectory; return NS_OK; } /* readonly attribute nsILocalFile file; */ NS_IMETHODIMP GFilePicker::GetFile(nsILocalFile * *aFile) { NS_IF_ADDREF(*aFile = mFile); return NS_OK; } /* readonly attribute nsIFileURL fileURL; */ NS_IMETHODIMP GFilePicker::GetFileURL(nsIFileURL * *aFileURL) { nsCOMPtr fileURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID); fileURL->SetFile(mFile); NS_IF_ADDREF(*aFileURL = fileURL); return NS_OK; } /* readonly attribute nsISimpleEnumerator files; */ NS_IMETHODIMP GFilePicker::GetFiles(nsISimpleEnumerator * *aFiles) { return NS_ERROR_NOT_IMPLEMENTED; } /* short show (); */ NS_IMETHODIMP GFilePicker::Show(PRInt16 *_retval) { mFileSelector = gtk_file_selection_new(mTitle.get()); nsCAutoString cFileName; if(mMode == nsIFilePicker::modeGetFolder) cFileName.Assign(""); else cFileName = mDefaultString; nsCAutoString cDirName; mDisplayDirectory->GetNativePath(cDirName); nsCAutoString cFullPath; cFullPath.Assign(cDirName + NS_LITERAL_CSTRING("/") + cFileName); gtk_file_selection_set_filename(GTK_FILE_SELECTION(mFileSelector), cFullPath.get()); if (!mFilter.IsEmpty()) { gtk_file_selection_complete(GTK_FILE_SELECTION(mFileSelector), mFilter.get()); } if (mParentWidget) gtk_window_set_transient_for(GTK_WINDOW(mFileSelector), GTK_WINDOW(mParentWidget)); if (mShowContentCheck) { GtkWidget *bbox = gtk_hbutton_box_new (); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); gtk_box_set_spacing(GTK_BOX(bbox), 0); gtk_box_pack_end(GTK_BOX(GTK_FILE_SELECTION(mFileSelector)->action_area), bbox, TRUE, TRUE, 0); GtkWidget *saveContent = gtk_check_button_new_with_label(_("Save with content")); g_signal_connect(G_OBJECT(saveContent), "clicked", G_CALLBACK(filePicker_save_content_cb), (gpointer)this); gtk_box_pack_start(GTK_BOX(bbox), saveContent, FALSE, FALSE, 0); gtk_widget_show_all(bbox); } if (mFileFormats) { mFormatChooser = gtk_option_menu_new(); GtkMenu *options = GTK_MENU(gtk_menu_new()); FileFormat *current = mFileFormats; while (current->description != NULL) { /* FIXME: the label should include the extensions too */ gchar *label = current->description; GtkWidget *item = gtk_menu_item_new_with_label(label); gtk_widget_show(item); gtk_menu_shell_append(GTK_MENU_SHELL(options), item); current++; } gtk_option_menu_set_menu(GTK_OPTION_MENU(mFormatChooser), GTK_WIDGET(options)); gtk_widget_show(mFormatChooser); gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION (mFileSelector)->action_area), mFormatChooser, FALSE, TRUE, 0); } else { mFormatChooser = NULL; } if (mMode == nsIFilePicker::modeGetFolder) { gtk_widget_set_sensitive(GTK_FILE_SELECTION(mFileSelector) ->file_list, FALSE); } gtk_window_set_modal(GTK_WINDOW(mFileSelector), TRUE); gint retVal = gtk_dialog_run(GTK_DIALOG(mFileSelector)); if (retVal == GTK_RESPONSE_OK) { HandleFilePickerResult(_retval); } else { *_retval = returnCancel; } gtk_widget_hide(mFileSelector); gtk_widget_destroy(mFileSelector); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // begin local public methods impl //////////////////////////////////////////////////////////////////////////////// NS_METHOD GFilePicker::InitWithGtkWidget (GtkWidget *aParentWidget, const char *aTitle, PRInt16 aMode) { mParentWidget = aParentWidget; mTitle = nsDependentCString(aTitle); mMode = mMode; mFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID); return NS_OK; } NS_METHOD GFilePicker::SanityCheck (PRBool *retIsSane) { *retIsSane = PR_TRUE; PRBool dirExists, fileExists = PR_TRUE; if (mDisplayDirectory) { mDisplayDirectory->Exists (&dirExists); } else { dirExists = PR_FALSE; } if (mMode == nsIFilePicker::modeOpen) { mFile->Exists (&fileExists); } if (!dirExists || !fileExists) { GtkWidget *errorDialog = gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The specified path does not exist.")); if (mParentWidget) gtk_window_set_transient_for(GTK_WINDOW(errorDialog), GTK_WINDOW(mFileSelector)); gtk_window_set_modal (GTK_WINDOW(errorDialog), TRUE); gtk_dialog_run (GTK_DIALOG(errorDialog)); *retIsSane = PR_FALSE; return NS_OK; } PRBool correctType; char *errorText; if (mMode == nsIFilePicker::modeGetFolder) { mDisplayDirectory->IsDirectory (&correctType); errorText = g_strdup (_("A file was selected when a " "folder was expected.")); } else { mFile->IsFile (&correctType); errorText = g_strdup (_("A folder was selected when a " "file was expected.")); } if(!correctType) { GtkWidget *errorDialog = gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, errorText); if (mParentWidget) gtk_window_set_transient_for(GTK_WINDOW(errorDialog), GTK_WINDOW(mFileSelector)); gtk_window_set_modal (GTK_WINDOW(errorDialog), TRUE); gtk_dialog_run (GTK_DIALOG(errorDialog)); *retIsSane = PR_FALSE; } g_free (errorText); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // begin local private methods impl //////////////////////////////////////////////////////////////////////////////// NS_METHOD GFilePicker::HandleFilePickerResult(PRInt16 *retval) { *retval = returnCancel; nsresult rv; const char *fileName = gtk_file_selection_get_filename(GTK_FILE_SELECTION(mFileSelector)); if (!fileName || strlen(fileName) == 0) return NS_ERROR_FAILURE; if (mMode == nsIFilePicker::modeSave) { if (!ephy_gui_confirm_overwrite_file (mFileSelector, fileName)) { return NS_OK; } } const nsACString &cFileName = nsDependentCString(fileName); mFile->InitWithNativePath(cFileName); if (mMode == nsIFilePicker::modeGetFolder) { mDisplayDirectory->InitWithNativePath(cFileName); mDefaultString = ""; } else { nsCOMPtr directory; mFile->GetParent(getter_AddRefs(directory)); mDisplayDirectory = do_QueryInterface(directory); mFile->GetNativeLeafName(mDefaultString); } PRBool passesSanityCheck; rv = SanityCheck(&passesSanityCheck); if (NS_SUCCEEDED(rv) && !passesSanityCheck) return NS_ERROR_FAILURE; if (mFormatChooser) { gint i = 0; GtkWidget *menu = gtk_option_menu_get_menu (GTK_OPTION_MENU(mFormatChooser)); GList *iterator = GTK_MENU_SHELL(menu)->children; GtkWidget *selected = gtk_menu_get_active (GTK_MENU(menu)); while (iterator) { if (iterator->data == selected) { mSelectedFileFormat = i; break; } iterator = iterator->next; i++; } } *retval = mSaveContent ? returnOKSaveContent : returnOK; return NS_OK; } //------------------------------------------------------------------------------ NS_DEF_FACTORY (GFilePicker, GFilePicker); /** * NS_NewFilePickerFactory: */ nsresult NS_NewFilePickerFactory(nsIFactory** aFactory) { NS_ENSURE_ARG_POINTER(aFactory); *aFactory = nsnull; nsGFilePickerFactory *result = new nsGFilePickerFactory; if (result == NULL) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(result); *aFactory = result; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // begin FileSelector callbacks. //////////////////////////////////////////////////////////////////////////////// void filePicker_save_content_cb(GtkToggleButton *aButton, GFilePicker *aFilePicker) { aFilePicker->mSaveContent = gtk_toggle_button_get_active (aButton) ? PR_TRUE : PR_FALSE; }