aboutsummaryrefslogblamecommitdiffstats
path: root/embed/mozilla/FilePicker.cpp
blob: 8ae5429f7dff7beb9bdda66cfa745e66019ed11c (plain) (tree)





































                                                                              
                          















                                 

                              

















                                


                                              



                                                                              

                            
 
                                                      


























































                                                                                    
                           
                                                                               
                                                           




                                                                                




                                                                            
         
                                   

                                                                                        
                                                      



                                                                                             

























































































                                                                                         
                                    
                                                                                
 
                                                                    



































































                                                                                            
                      








                                                             

                    



                                               

                                                            





                                     






                                                            
         
                             
















                                                                                
                                                 







                                                  

                                                                   




                                                                     

                                                            


















                                                                                
                                                 


















































                                                                                                  

                                                           

                                                                           


                                                                       





                                                        


                 





                                                                                              


                     
 
/*
 *  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 <glib/gconvert.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 "nsISupportsArray.h"
#include "nsIServiceManager.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"

/* Implementation file */
NS_IMPL_ISUPPORTS1(GFilePicker, nsIFilePicker)

GFilePicker::GFilePicker(PRBool aShowContentCheck, FileFormat *aFileFormats) :
            mShowContentCheck(aShowContentCheck),
            mSaveContentCheck(NULL),
            mFileFormats(aFileFormats)
{
    NS_INIT_ISUPPORTS();

    /* member initializers and constructor code */
    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)
{
    gsize bytesWritten;
    gchar *utf8DefaultString = g_filename_to_utf8(mDefaultString.get(), -1,
                              NULL,
                              &bytesWritten, NULL);

    *aDefaultString = ToNewUnicode(NS_ConvertUTF8toUCS2(utf8DefaultString));
    g_free(utf8DefaultString);

    return NS_OK;
}
NS_IMETHODIMP GFilePicker::SetDefaultString(const PRUnichar *aDefaultString)
{
    if (aDefaultString)
    {
        gsize bytesWritten;
        gchar *localeDefaultString =
            g_filename_from_utf8(NS_ConvertUCS2toUTF8(aDefaultString).get(),
                         -1, NULL,
                         &bytesWritten, NULL);
        mDefaultString = localeDefaultString;                        
        g_free(localeDefaultString);
    }
    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);

        mSaveContentCheck = 
            gtk_check_button_new_with_label(_("Save with content"));

        gtk_box_pack_start(GTK_BOX(bbox), mSaveContentCheck,
                   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 = aMode;

    mFile = do_CreateInstance (NS_LOCAL_FILE_CONTRACTID);

    return NS_OK;
}

NS_METHOD GFilePicker::SanityCheck (PRBool *retIsSane)
{
    *retIsSane = PR_TRUE;
    
    nsresult rv;
    PRBool dirExists, fileExists = PR_TRUE;

    if (mDisplayDirectory)
    {
        rv = mDisplayDirectory->Exists (&dirExists);
        g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
    }
    else
    {
        dirExists = PR_FALSE;
    }

    if (mMode != nsIFilePicker::modeGetFolder)
    {
        rv = mFile->Exists (&fileExists);
        g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
    }

    if (mMode == nsIFilePicker::modeSave && !fileExists)
    {
        return NS_OK;
    }
    
    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));
        gtk_widget_destroy (errorDialog);
        *retIsSane = PR_FALSE;
        return NS_OK;
    }

    PRBool correctType;
    char *errorText;
    if (mMode == nsIFilePicker::modeGetFolder)
    {
        rv = mDisplayDirectory->IsDirectory (&correctType);
        g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
        errorText = g_strdup (_("A file was selected when a "
                    "folder was expected."));
    }
    else
    {
        rv = mFile->IsFile (&correctType);
        g_return_val_if_fail (NS_SUCCEEDED(rv), rv);
        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));
        gtk_widget_destroy (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)
    {
        GtkWidget *menu = gtk_option_menu_get_menu 
            (GTK_OPTION_MENU(mFormatChooser));
        GtkWidget *selected = gtk_menu_get_active (GTK_MENU(menu));

        gint i(0);
        for (GList *iterator = GTK_MENU_SHELL(menu)->children ;
             iterator ; iterator = iterator->next, i++)
        {
            if (iterator->data == selected) 
            {
                mSelectedFileFormat = i;
                break;
            }
        }
    }

    if (GTK_IS_TOGGLE_BUTTON(mSaveContentCheck))
        *retval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mSaveContentCheck)) ?
              returnOKSaveContent : returnOK;
    else
        *retval = returnOK;

    return NS_OK;
}