widget/gtk/nsFilePicker.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/gtk/nsFilePicker.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,546 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/Types.h"
    1.10 +
    1.11 +#include <gtk/gtk.h>
    1.12 +
    1.13 +#include "nsGtkUtils.h"
    1.14 +#include "nsIFileURL.h"
    1.15 +#include "nsIURI.h"
    1.16 +#include "nsIWidget.h"
    1.17 +#include "nsIFile.h"
    1.18 +#include "nsIStringBundle.h"
    1.19 +
    1.20 +#include "nsArrayEnumerator.h"
    1.21 +#include "nsMemory.h"
    1.22 +#include "nsEnumeratorUtils.h"
    1.23 +#include "nsNetUtil.h"
    1.24 +#include "nsReadableUtils.h"
    1.25 +#include "mozcontainer.h"
    1.26 +
    1.27 +#include "nsFilePicker.h"
    1.28 +
    1.29 +using namespace mozilla;
    1.30 +
    1.31 +#define MAX_PREVIEW_SIZE 180
    1.32 +
    1.33 +nsIFile *nsFilePicker::mPrevDisplayDirectory = nullptr;
    1.34 +
    1.35 +void
    1.36 +nsFilePicker::Shutdown()
    1.37 +{
    1.38 +  NS_IF_RELEASE(mPrevDisplayDirectory);
    1.39 +}
    1.40 +
    1.41 +static GtkFileChooserAction
    1.42 +GetGtkFileChooserAction(int16_t aMode)
    1.43 +{
    1.44 +  GtkFileChooserAction action;
    1.45 +
    1.46 +  switch (aMode) {
    1.47 +    case nsIFilePicker::modeSave:
    1.48 +    action = GTK_FILE_CHOOSER_ACTION_SAVE;
    1.49 +    break;
    1.50 +
    1.51 +    case nsIFilePicker::modeGetFolder:
    1.52 +    action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
    1.53 +    break;
    1.54 +
    1.55 +    case nsIFilePicker::modeOpen:
    1.56 +    case nsIFilePicker::modeOpenMultiple:
    1.57 +    action = GTK_FILE_CHOOSER_ACTION_OPEN;
    1.58 +    break;
    1.59 +
    1.60 +    default:
    1.61 +    NS_WARNING("Unknown nsIFilePicker mode");
    1.62 +    action = GTK_FILE_CHOOSER_ACTION_OPEN;
    1.63 +    break;
    1.64 +  }
    1.65 +
    1.66 +  return action;
    1.67 +}
    1.68 +
    1.69 +
    1.70 +static void
    1.71 +UpdateFilePreviewWidget(GtkFileChooser *file_chooser,
    1.72 +                        gpointer preview_widget_voidptr)
    1.73 +{
    1.74 +  GtkImage *preview_widget = GTK_IMAGE(preview_widget_voidptr);
    1.75 +  char *image_filename = gtk_file_chooser_get_preview_filename(file_chooser);
    1.76 +
    1.77 +  if (!image_filename) {
    1.78 +    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
    1.79 +    return;
    1.80 +  }
    1.81 +
    1.82 +  gint preview_width = 0;
    1.83 +  gint preview_height = 0;
    1.84 +  GdkPixbufFormat *preview_format = gdk_pixbuf_get_file_info(image_filename,
    1.85 +                                                             &preview_width,
    1.86 +                                                             &preview_height);
    1.87 +  if (!preview_format) {
    1.88 +    g_free(image_filename);
    1.89 +    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
    1.90 +    return;
    1.91 +  }
    1.92 +
    1.93 +  GdkPixbuf *preview_pixbuf;
    1.94 +  // Only scale down images that are too big
    1.95 +  if (preview_width > MAX_PREVIEW_SIZE || preview_height > MAX_PREVIEW_SIZE) {
    1.96 +    preview_pixbuf = gdk_pixbuf_new_from_file_at_size(image_filename,
    1.97 +                                                      MAX_PREVIEW_SIZE,
    1.98 +                                                      MAX_PREVIEW_SIZE,
    1.99 +                                                      nullptr);
   1.100 +  }
   1.101 +  else {
   1.102 +    preview_pixbuf = gdk_pixbuf_new_from_file(image_filename, nullptr);
   1.103 +  }
   1.104 +
   1.105 +  g_free(image_filename);
   1.106 +
   1.107 +  if (!preview_pixbuf) {
   1.108 +    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
   1.109 +    return;
   1.110 +  }
   1.111 +
   1.112 +#if GTK_CHECK_VERSION(2,12,0)
   1.113 +  GdkPixbuf *preview_pixbuf_temp = preview_pixbuf;
   1.114 +  preview_pixbuf = gdk_pixbuf_apply_embedded_orientation(preview_pixbuf_temp);
   1.115 +  g_object_unref(preview_pixbuf_temp);
   1.116 +#endif
   1.117 +
   1.118 +  // This is the easiest way to do center alignment without worrying about containers
   1.119 +  // Minimum 3px padding each side (hence the 6) just to make things nice
   1.120 +  gint x_padding = (MAX_PREVIEW_SIZE + 6 - gdk_pixbuf_get_width(preview_pixbuf)) / 2;
   1.121 +  gtk_misc_set_padding(GTK_MISC(preview_widget), x_padding, 0);
   1.122 +
   1.123 +  gtk_image_set_from_pixbuf(preview_widget, preview_pixbuf);
   1.124 +  g_object_unref(preview_pixbuf);
   1.125 +  gtk_file_chooser_set_preview_widget_active(file_chooser, TRUE);
   1.126 +}
   1.127 +
   1.128 +static nsAutoCString
   1.129 +MakeCaseInsensitiveShellGlob(const char* aPattern) {
   1.130 +  // aPattern is UTF8
   1.131 +  nsAutoCString result;
   1.132 +  unsigned int len = strlen(aPattern);
   1.133 +
   1.134 +  for (unsigned int i = 0; i < len; i++) {
   1.135 +    if (!g_ascii_isalpha(aPattern[i])) {
   1.136 +      // non-ASCII characters will also trigger this path, so unicode
   1.137 +      // is safely handled albeit case-sensitively
   1.138 +      result.Append(aPattern[i]);
   1.139 +      continue;
   1.140 +    }
   1.141 +
   1.142 +    // add the lowercase and uppercase version of a character to a bracket
   1.143 +    // match, so it matches either the lowercase or uppercase char.
   1.144 +    result.Append('[');
   1.145 +    result.Append(g_ascii_tolower(aPattern[i]));
   1.146 +    result.Append(g_ascii_toupper(aPattern[i]));
   1.147 +    result.Append(']');
   1.148 +
   1.149 +  }
   1.150 +
   1.151 +  return result;
   1.152 +}
   1.153 +
   1.154 +NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
   1.155 +
   1.156 +nsFilePicker::nsFilePicker()
   1.157 +  : mSelectedType(0),
   1.158 +    mRunning(false),
   1.159 +    mAllowURLs(false)
   1.160 +{
   1.161 +}
   1.162 +
   1.163 +nsFilePicker::~nsFilePicker()
   1.164 +{
   1.165 +}
   1.166 +
   1.167 +void
   1.168 +ReadMultipleFiles(gpointer filename, gpointer array)
   1.169 +{
   1.170 +  nsCOMPtr<nsIFile> localfile;
   1.171 +  nsresult rv = NS_NewNativeLocalFile(nsDependentCString(static_cast<char*>(filename)),
   1.172 +                                      false,
   1.173 +                                      getter_AddRefs(localfile));
   1.174 +  if (NS_SUCCEEDED(rv)) {
   1.175 +    nsCOMArray<nsIFile>& files = *static_cast<nsCOMArray<nsIFile>*>(array);
   1.176 +    files.AppendObject(localfile);
   1.177 +  }
   1.178 +
   1.179 +  g_free(filename);
   1.180 +}
   1.181 +
   1.182 +void
   1.183 +nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser)
   1.184 +{
   1.185 +  mFiles.Clear();
   1.186 +
   1.187 +  if (mMode == nsIFilePicker::modeOpenMultiple) {
   1.188 +    mFileURL.Truncate();
   1.189 +
   1.190 +    GSList *list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_chooser));
   1.191 +    g_slist_foreach(list, ReadMultipleFiles, static_cast<gpointer>(&mFiles));
   1.192 +    g_slist_free(list);
   1.193 +  } else {
   1.194 +    gchar *filename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(file_chooser));
   1.195 +    mFileURL.Assign(filename);
   1.196 +    g_free(filename);
   1.197 +  }
   1.198 +
   1.199 +  GtkFileFilter *filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(file_chooser));
   1.200 +  GSList *filter_list = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(file_chooser));
   1.201 +
   1.202 +  mSelectedType = static_cast<int16_t>(g_slist_index(filter_list, filter));
   1.203 +  g_slist_free(filter_list);
   1.204 +
   1.205 +  // Remember last used directory.
   1.206 +  nsCOMPtr<nsIFile> file;
   1.207 +  GetFile(getter_AddRefs(file));
   1.208 +  if (file) {
   1.209 +    nsCOMPtr<nsIFile> dir;
   1.210 +    file->GetParent(getter_AddRefs(dir));
   1.211 +    if (dir) {
   1.212 +      dir.swap(mPrevDisplayDirectory);
   1.213 +    }
   1.214 +  }
   1.215 +}
   1.216 +
   1.217 +void
   1.218 +nsFilePicker::InitNative(nsIWidget *aParent,
   1.219 +                         const nsAString& aTitle)
   1.220 +{
   1.221 +  mParentWidget = aParent;
   1.222 +  mTitle.Assign(aTitle);
   1.223 +}
   1.224 +
   1.225 +NS_IMETHODIMP
   1.226 +nsFilePicker::AppendFilters(int32_t aFilterMask)
   1.227 +{
   1.228 +  mAllowURLs = !!(aFilterMask & filterAllowURLs);
   1.229 +  return nsBaseFilePicker::AppendFilters(aFilterMask);
   1.230 +}
   1.231 +
   1.232 +NS_IMETHODIMP
   1.233 +nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
   1.234 +{
   1.235 +  if (aFilter.EqualsLiteral("..apps")) {
   1.236 +    // No platform specific thing we can do here, really....
   1.237 +    return NS_OK;
   1.238 +  }
   1.239 +
   1.240 +  nsAutoCString filter, name;
   1.241 +  CopyUTF16toUTF8(aFilter, filter);
   1.242 +  CopyUTF16toUTF8(aTitle, name);
   1.243 +
   1.244 +  mFilters.AppendElement(filter);
   1.245 +  mFilterNames.AppendElement(name);
   1.246 +
   1.247 +  return NS_OK;
   1.248 +}
   1.249 +
   1.250 +NS_IMETHODIMP
   1.251 +nsFilePicker::SetDefaultString(const nsAString& aString)
   1.252 +{
   1.253 +  mDefault = aString;
   1.254 +
   1.255 +  return NS_OK;
   1.256 +}
   1.257 +
   1.258 +NS_IMETHODIMP
   1.259 +nsFilePicker::GetDefaultString(nsAString& aString)
   1.260 +{
   1.261 +  // Per API...
   1.262 +  return NS_ERROR_FAILURE;
   1.263 +}
   1.264 +
   1.265 +NS_IMETHODIMP
   1.266 +nsFilePicker::SetDefaultExtension(const nsAString& aExtension)
   1.267 +{
   1.268 +  mDefaultExtension = aExtension;
   1.269 +
   1.270 +  return NS_OK;
   1.271 +}
   1.272 +
   1.273 +NS_IMETHODIMP
   1.274 +nsFilePicker::GetDefaultExtension(nsAString& aExtension)
   1.275 +{
   1.276 +  aExtension = mDefaultExtension;
   1.277 +
   1.278 +  return NS_OK;
   1.279 +}
   1.280 +
   1.281 +NS_IMETHODIMP
   1.282 +nsFilePicker::GetFilterIndex(int32_t *aFilterIndex)
   1.283 +{
   1.284 +  *aFilterIndex = mSelectedType;
   1.285 +
   1.286 +  return NS_OK;
   1.287 +}
   1.288 +
   1.289 +NS_IMETHODIMP
   1.290 +nsFilePicker::SetFilterIndex(int32_t aFilterIndex)
   1.291 +{
   1.292 +  mSelectedType = aFilterIndex;
   1.293 +
   1.294 +  return NS_OK;
   1.295 +}
   1.296 +
   1.297 +NS_IMETHODIMP
   1.298 +nsFilePicker::GetFile(nsIFile **aFile)
   1.299 +{
   1.300 +  NS_ENSURE_ARG_POINTER(aFile);
   1.301 +
   1.302 +  *aFile = nullptr;
   1.303 +  nsCOMPtr<nsIURI> uri;
   1.304 +  nsresult rv = GetFileURL(getter_AddRefs(uri));
   1.305 +  if (!uri)
   1.306 +    return rv;
   1.307 +
   1.308 +  nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(uri, &rv));
   1.309 +  NS_ENSURE_SUCCESS(rv, rv);
   1.310 +
   1.311 +  nsCOMPtr<nsIFile> file;
   1.312 +  rv = fileURL->GetFile(getter_AddRefs(file));
   1.313 +  NS_ENSURE_SUCCESS(rv, rv);
   1.314 +
   1.315 +  file.forget(aFile);
   1.316 +  return NS_OK;
   1.317 +}
   1.318 +
   1.319 +NS_IMETHODIMP
   1.320 +nsFilePicker::GetFileURL(nsIURI **aFileURL)
   1.321 +{
   1.322 +  *aFileURL = nullptr;
   1.323 +  return NS_NewURI(aFileURL, mFileURL);
   1.324 +}
   1.325 +
   1.326 +NS_IMETHODIMP
   1.327 +nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
   1.328 +{
   1.329 +  NS_ENSURE_ARG_POINTER(aFiles);
   1.330 +
   1.331 +  if (mMode == nsIFilePicker::modeOpenMultiple) {
   1.332 +    return NS_NewArrayEnumerator(aFiles, mFiles);
   1.333 +  }
   1.334 +
   1.335 +  return NS_ERROR_FAILURE;
   1.336 +}
   1.337 +
   1.338 +NS_IMETHODIMP
   1.339 +nsFilePicker::Show(int16_t *aReturn)
   1.340 +{
   1.341 +  NS_ENSURE_ARG_POINTER(aReturn);
   1.342 +
   1.343 +  nsresult rv = Open(nullptr);
   1.344 +  if (NS_FAILED(rv))
   1.345 +    return rv;
   1.346 +
   1.347 +  while (mRunning) {
   1.348 +    g_main_context_iteration(nullptr, TRUE);
   1.349 +  }
   1.350 +
   1.351 +  *aReturn = mResult;
   1.352 +  return NS_OK;
   1.353 +}
   1.354 +
   1.355 +NS_IMETHODIMP
   1.356 +nsFilePicker::Open(nsIFilePickerShownCallback *aCallback)
   1.357 +{
   1.358 +  // Can't show two dialogs concurrently with the same filepicker
   1.359 +  if (mRunning)
   1.360 +    return NS_ERROR_NOT_AVAILABLE;
   1.361 +
   1.362 +  nsXPIDLCString title;
   1.363 +  title.Adopt(ToNewUTF8String(mTitle));
   1.364 +
   1.365 +  GtkWindow *parent_widget =
   1.366 +    GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET));
   1.367 +
   1.368 +  GtkFileChooserAction action = GetGtkFileChooserAction(mMode);
   1.369 +  const gchar *accept_button = (action == GTK_FILE_CHOOSER_ACTION_SAVE)
   1.370 +                               ? GTK_STOCK_SAVE : GTK_STOCK_OPEN;
   1.371 +  GtkWidget *file_chooser =
   1.372 +      gtk_file_chooser_dialog_new(title, parent_widget, action,
   1.373 +                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
   1.374 +                                  accept_button, GTK_RESPONSE_ACCEPT,
   1.375 +                                  nullptr);
   1.376 +  gtk_dialog_set_alternative_button_order(GTK_DIALOG(file_chooser),
   1.377 +                                          GTK_RESPONSE_ACCEPT,
   1.378 +                                          GTK_RESPONSE_CANCEL,
   1.379 +                                          -1);
   1.380 +  if (mAllowURLs) {
   1.381 +    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(file_chooser), FALSE);
   1.382 +  }
   1.383 +
   1.384 +  if (action == GTK_FILE_CHOOSER_ACTION_OPEN || action == GTK_FILE_CHOOSER_ACTION_SAVE) {
   1.385 +    GtkWidget *img_preview = gtk_image_new();
   1.386 +    gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(file_chooser), img_preview);
   1.387 +    g_signal_connect(file_chooser, "update-preview", G_CALLBACK(UpdateFilePreviewWidget), img_preview);
   1.388 +  }
   1.389 +
   1.390 +  GtkWindow *window = GTK_WINDOW(file_chooser);
   1.391 +  gtk_window_set_modal(window, TRUE);
   1.392 +  if (parent_widget) {
   1.393 +    gtk_window_set_destroy_with_parent(window, TRUE);
   1.394 +  }
   1.395 +
   1.396 +  NS_ConvertUTF16toUTF8 defaultName(mDefault);
   1.397 +  switch (mMode) {
   1.398 +    case nsIFilePicker::modeOpenMultiple:
   1.399 +      gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_chooser), TRUE);
   1.400 +      break;
   1.401 +    case nsIFilePicker::modeSave:
   1.402 +      gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser),
   1.403 +                                        defaultName.get());
   1.404 +      break;
   1.405 +  }
   1.406 +
   1.407 +  nsCOMPtr<nsIFile> defaultPath;
   1.408 +  if (mDisplayDirectory) {
   1.409 +    mDisplayDirectory->Clone(getter_AddRefs(defaultPath));
   1.410 +  } else if (mPrevDisplayDirectory) {
   1.411 +    mPrevDisplayDirectory->Clone(getter_AddRefs(defaultPath));
   1.412 +  }
   1.413 +
   1.414 +  if (defaultPath) {
   1.415 +    if (!defaultName.IsEmpty() && mMode != nsIFilePicker::modeSave) {
   1.416 +      // Try to select the intended file. Even if it doesn't exist, GTK still switches
   1.417 +      // directories.
   1.418 +      defaultPath->AppendNative(defaultName);
   1.419 +      nsAutoCString path;
   1.420 +      defaultPath->GetNativePath(path);
   1.421 +      gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_chooser), path.get());
   1.422 +    } else {
   1.423 +      nsAutoCString directory;
   1.424 +      defaultPath->GetNativePath(directory);
   1.425 +      gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser),
   1.426 +                                          directory.get());
   1.427 +    }
   1.428 +  }
   1.429 +
   1.430 +  gtk_dialog_set_default_response(GTK_DIALOG(file_chooser), GTK_RESPONSE_ACCEPT);
   1.431 +
   1.432 +  int32_t count = mFilters.Length();
   1.433 +  for (int32_t i = 0; i < count; ++i) {
   1.434 +    // This is fun... the GTK file picker does not accept a list of filters
   1.435 +    // so we need to split out each string, and add it manually.
   1.436 +
   1.437 +    char **patterns = g_strsplit(mFilters[i].get(), ";", -1);
   1.438 +    if (!patterns) {
   1.439 +      return NS_ERROR_OUT_OF_MEMORY;
   1.440 +    }
   1.441 +
   1.442 +    GtkFileFilter *filter = gtk_file_filter_new();
   1.443 +    for (int j = 0; patterns[j] != nullptr; ++j) {
   1.444 +      nsAutoCString caseInsensitiveFilter = MakeCaseInsensitiveShellGlob(g_strstrip(patterns[j]));
   1.445 +      gtk_file_filter_add_pattern(filter, caseInsensitiveFilter.get());
   1.446 +    }
   1.447 +
   1.448 +    g_strfreev(patterns);
   1.449 +
   1.450 +    if (!mFilterNames[i].IsEmpty()) {
   1.451 +      // If we have a name for our filter, let's use that.
   1.452 +      const char *filter_name = mFilterNames[i].get();
   1.453 +      gtk_file_filter_set_name(filter, filter_name);
   1.454 +    } else {
   1.455 +      // If we don't have a name, let's just use the filter pattern.
   1.456 +      const char *filter_pattern = mFilters[i].get();
   1.457 +      gtk_file_filter_set_name(filter, filter_pattern);
   1.458 +    }
   1.459 +
   1.460 +    gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), filter);
   1.461 +
   1.462 +    // Set the initially selected filter
   1.463 +    if (mSelectedType == i) {
   1.464 +      gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(file_chooser), filter);
   1.465 +    }
   1.466 +  }
   1.467 +
   1.468 +  gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(file_chooser), TRUE);
   1.469 +
   1.470 +  mRunning = true;
   1.471 +  mCallback = aCallback;
   1.472 +  NS_ADDREF_THIS();
   1.473 +  g_signal_connect(file_chooser, "response", G_CALLBACK(OnResponse), this);
   1.474 +  g_signal_connect(file_chooser, "destroy", G_CALLBACK(OnDestroy), this);
   1.475 +  gtk_widget_show(file_chooser);
   1.476 +
   1.477 +  return NS_OK;
   1.478 +}
   1.479 +
   1.480 +/* static */ void
   1.481 +nsFilePicker::OnResponse(GtkWidget* file_chooser, gint response_id,
   1.482 +                         gpointer user_data)
   1.483 +{
   1.484 +  static_cast<nsFilePicker*>(user_data)->
   1.485 +    Done(file_chooser, response_id);
   1.486 +}
   1.487 +
   1.488 +/* static */ void
   1.489 +nsFilePicker::OnDestroy(GtkWidget* file_chooser, gpointer user_data)
   1.490 +{
   1.491 +  static_cast<nsFilePicker*>(user_data)->
   1.492 +    Done(file_chooser, GTK_RESPONSE_CANCEL);
   1.493 +}
   1.494 +
   1.495 +void
   1.496 +nsFilePicker::Done(GtkWidget* file_chooser, gint response)
   1.497 +{
   1.498 +  mRunning = false;
   1.499 +
   1.500 +  int16_t result;
   1.501 +  switch (response) {
   1.502 +    case GTK_RESPONSE_OK:
   1.503 +    case GTK_RESPONSE_ACCEPT:
   1.504 +    ReadValuesFromFileChooser(file_chooser);
   1.505 +    result = nsIFilePicker::returnOK;
   1.506 +    if (mMode == nsIFilePicker::modeSave) {
   1.507 +      nsCOMPtr<nsIFile> file;
   1.508 +      GetFile(getter_AddRefs(file));
   1.509 +      if (file) {
   1.510 +        bool exists = false;
   1.511 +        file->Exists(&exists);
   1.512 +        if (exists)
   1.513 +          result = nsIFilePicker::returnReplace;
   1.514 +      }
   1.515 +    }
   1.516 +    break;
   1.517 +
   1.518 +    case GTK_RESPONSE_CANCEL:
   1.519 +    case GTK_RESPONSE_CLOSE:
   1.520 +    case GTK_RESPONSE_DELETE_EVENT:
   1.521 +    result = nsIFilePicker::returnCancel;
   1.522 +    break;
   1.523 +
   1.524 +    default:
   1.525 +    NS_WARNING("Unexpected response");
   1.526 +    result = nsIFilePicker::returnCancel;
   1.527 +    break;
   1.528 +  }
   1.529 +
   1.530 +  // A "response" signal won't be sent again but "destroy" will be.
   1.531 +  g_signal_handlers_disconnect_by_func(file_chooser,
   1.532 +                                       FuncToGpointer(OnDestroy), this);
   1.533 +
   1.534 +  // When response_id is GTK_RESPONSE_DELETE_EVENT or when called from
   1.535 +  // OnDestroy, the widget would be destroyed anyway but it is fine if
   1.536 +  // gtk_widget_destroy is called more than once.  gtk_widget_destroy has
   1.537 +  // requests that any remaining references be released, but the reference
   1.538 +  // count will not be decremented again if GtkWindow's reference has already
   1.539 +  // been released.
   1.540 +  gtk_widget_destroy(file_chooser);
   1.541 +
   1.542 +  if (mCallback) {
   1.543 +    mCallback->Done(result);
   1.544 +    mCallback = nullptr;
   1.545 +  } else {
   1.546 +    mResult = result;
   1.547 +  }
   1.548 +  NS_RELEASE_THIS();
   1.549 +}

mercurial