/*
* 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 <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkcheckbutton.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtkfilesel.h>
#include <gtk/gtkhbbox.h>
#include <gtk/gtkoptionmenu.h>
#include <gtk/gtkmessagedialog.h>
#include <libgnome/gnome-i18n.h>
#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 <libgnome/gnome-util.h>
#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<nsIFileURL> 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<nsIFile> 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;
}