+
+ if (EV_WINDOW_IS_PRESENTATION (ev_window))
+ gtk_widget_grab_focus (ev_window->priv->presentation_view);
+ else
+ gtk_widget_grab_focus (ev_window->priv->view);
+
+ return FALSE;
+}
+
+static void
+ev_window_set_document (EvWindow *ev_window, EvDocument *document)
+{
+ if (ev_window->priv->document == document)
+ return;
+
+ if (ev_window->priv->document)
+ g_object_unref (ev_window->priv->document);
+ ev_window->priv->document = g_object_ref (document);
+
+ ev_window_update_max_min_scale (ev_window);
+
+ ev_window_set_message_area (ev_window, NULL);
+
+ if (ev_document_get_n_pages (document) <= 0) {
+ ev_window_warning_message (ev_window, "%s",
+ _("The document contains no pages"));
+ } else if (!ev_document_check_dimensions (document)) {
+ ev_window_warning_message (ev_window, "%s",
+ _("The document contains only empty pages"));
+ }
+
+ if (EV_WINDOW_IS_PRESENTATION (ev_window)) {
+ gtk_widget_destroy (ev_window->priv->presentation_view);
+ ev_window->priv->presentation_view = NULL;
+ ev_window_run_presentation (ev_window);
+ }
+
+ if (ev_window->priv->setup_document_idle > 0)
+ g_source_remove (ev_window->priv->setup_document_idle);
+
+ ev_window->priv->setup_document_idle = g_idle_add ((GSourceFunc)ev_window_setup_document, ev_window);
+}
+
+static void
+ev_window_document_changed (EvWindow *ev_window,
+ gpointer user_data)
+{
+ ev_window_reload_document (ev_window, NULL);
+}
+
+static void
+ev_window_password_view_unlock (EvWindow *ev_window)
+{
+ const gchar *password;
+
+ g_assert (ev_window->priv->load_job);
+
+ password = ev_password_view_get_password (EV_PASSWORD_VIEW (ev_window->priv->password_view));
+ ev_job_load_set_password (EV_JOB_LOAD (ev_window->priv->load_job), password);
+ ev_job_scheduler_push_job (ev_window->priv->load_job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+ev_window_clear_load_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->load_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->load_job))
+ ev_job_cancel (ev_window->priv->load_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->load_job, ev_window_load_job_cb, ev_window);
+ g_object_unref (ev_window->priv->load_job);
+ ev_window->priv->load_job = NULL;
+ }
+}
+
+static void
+ev_window_clear_reload_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->reload_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->reload_job))
+ ev_job_cancel (ev_window->priv->reload_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->reload_job, ev_window_reload_job_cb, ev_window);
+ g_object_unref (ev_window->priv->reload_job);
+ ev_window->priv->reload_job = NULL;
+ }
+}
+
+static void
+ev_window_clear_local_uri (EvWindow *ev_window)
+{
+ if (ev_window->priv->local_uri) {
+ ev_tmp_uri_unlink (ev_window->priv->local_uri);
+ g_free (ev_window->priv->local_uri);
+ ev_window->priv->local_uri = NULL;
+ }
+}
+
+static void
+ev_window_handle_link (EvWindow *ev_window,
+ EvLinkDest *dest)
+{
+ if (dest) {
+ EvLink *link;
+ EvLinkAction *link_action;
+
+ link_action = ev_link_action_new_dest (dest);
+ link = ev_link_new (NULL, link_action);
+ ev_view_handle_link (EV_VIEW (ev_window->priv->view), link);
+ g_object_unref (link);
+ }
+}
+
+/* This callback will executed when load job will be finished.
+ *
+ * Since the flow of the error dialog is very confusing, we assume that both
+ * document and uri will go away after this function is called, and thus we need
+ * to ref/dup them. Additionally, it needs to clear
+ * ev_window->priv->password_{uri,document}, and thus people who call this
+ * function should _not_ necessarily expect those to exist after being
+ * called. */
+static void
+ev_window_load_job_cb (EvJob *job,
+ gpointer data)
+{
+ EvWindow *ev_window = EV_WINDOW (data);
+ EvDocument *document = EV_JOB (job)->document;
+ EvJobLoad *job_load = EV_JOB_LOAD (job);
+
+ g_assert (job_load->uri);
+
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE);
+
+ /* Success! */
+ if (!ev_job_is_failed (job)) {
+ ev_document_model_set_document (ev_window->priv->model, document);
+
+ setup_chrome_from_metadata (ev_window);
+ update_chrome_actions (ev_window);
+ setup_document_from_metadata (ev_window);
+ setup_view_from_metadata (ev_window);
+
+ ev_window_add_recent (ev_window, ev_window->priv->uri);
+
+ ev_window_title_set_type (ev_window->priv->title,
+ EV_WINDOW_TITLE_DOCUMENT);
+ if (job_load->password) {
+ GPasswordSave flags;
+
+ flags = ev_password_view_get_password_save_flags (
+ EV_PASSWORD_VIEW (ev_window->priv->password_view));
+ ev_keyring_save_password (ev_window->priv->uri,
+ job_load->password,
+ flags);
+ }
+
+ ev_window_handle_link (ev_window, ev_window->priv->dest);
+ /* Already unrefed by ev_link_action
+ * FIXME: link action should inc dest ref counting
+ * or not unref it at all
+ */
+ ev_window->priv->dest = NULL;
+
+ switch (ev_window->priv->window_mode) {
+ case EV_WINDOW_MODE_FULLSCREEN:
+ ev_window_run_fullscreen (ev_window);
+ break;
+ case EV_WINDOW_MODE_PRESENTATION:
+ ev_window_run_presentation (ev_window);
+ break;
+ default:
+ break;
+ }
+
+ if (ev_window->priv->search_string && EV_IS_DOCUMENT_FIND (document)) {
+ ev_window_cmd_edit_find (NULL, ev_window);
+ egg_find_bar_set_search_string (EGG_FIND_BAR (ev_window->priv->find_bar),
+ ev_window->priv->search_string);
+ }
+
+ g_free (ev_window->priv->search_string);
+ ev_window->priv->search_string = NULL;
+
+ /* Create a monitor for the document */
+ ev_window->priv->monitor = ev_file_monitor_new (ev_window->priv->uri);
+ g_signal_connect_swapped (ev_window->priv->monitor, "changed",
+ G_CALLBACK (ev_window_document_changed),
+ ev_window);
+
+ ev_window_clear_load_job (ev_window);
+ return;
+ }
+
+ if (g_error_matches (job->error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_ENCRYPTED)) {
+ gchar *password;
+
+ setup_view_from_metadata (ev_window);
+
+ /* First look whether password is in keyring */
+ password = ev_keyring_lookup_password (ev_window->priv->uri);
+ if (password) {
+ if (job_load->password && strcmp (password, job_load->password) == 0) {
+ /* Password in kering is wrong */
+ ev_job_load_set_password (job_load, NULL);
+ /* FIXME: delete password from keyring? */
+ } else {
+ ev_job_load_set_password (job_load, password);
+ ev_job_scheduler_push_job (job, EV_JOB_PRIORITY_NONE);
+ g_free (password);
+ return;
+ }
+
+ g_free (password);
+ }
+
+ /* We need to ask the user for a password */
+ ev_window_title_set_uri (ev_window->priv->title,
+ ev_window->priv->uri);
+ ev_window_title_set_type (ev_window->priv->title,
+ EV_WINDOW_TITLE_PASSWORD);
+
+ ev_password_view_set_uri (EV_PASSWORD_VIEW (ev_window->priv->password_view),
+ job_load->uri);
+
+ ev_window_set_page_mode (ev_window, PAGE_MODE_PASSWORD);
+
+ ev_job_load_set_password (job_load, NULL);
+ ev_password_view_ask_password (EV_PASSWORD_VIEW (ev_window->priv->password_view));
+ } else {
+ ev_window_error_message (ev_window, job->error,
+ "%s", _("Unable to open document"));
+ ev_window_clear_load_job (ev_window);
+ }
+}
+
+static void
+ev_window_reload_job_cb (EvJob *job,
+ EvWindow *ev_window)
+{
+ GtkWidget *widget;
+
+ if (ev_job_is_failed (job)) {
+ ev_window_clear_reload_job (ev_window);
+ ev_window->priv->in_reload = FALSE;
+ if (ev_window->priv->dest) {
+ g_object_unref (ev_window->priv->dest);
+ ev_window->priv->dest = NULL;
+ }
+
+ return;
+ }
+
+ ev_document_model_set_document (ev_window->priv->model,
+ job->document);
+ if (ev_window->priv->dest) {
+ ev_window_handle_link (ev_window, ev_window->priv->dest);
+ /* Already unrefed by ev_link_action
+ * FIXME: link action should inc dest ref counting
+ * or not unref it at all
+ */
+ ev_window->priv->dest = NULL;
+ }
+
+ /* Restart the search after reloading */
+ widget = gtk_window_get_focus (GTK_WINDOW (ev_window));
+ if (widget && gtk_widget_get_ancestor (widget, EGG_TYPE_FIND_BAR)) {
+ find_bar_search_changed_cb (EGG_FIND_BAR (ev_window->priv->find_bar),
+ NULL, ev_window);
+ }
+
+ ev_window_clear_reload_job (ev_window);
+ ev_window->priv->in_reload = FALSE;
+}
+
+/**
+ * ev_window_get_uri:
+ * @ev_window: The instance of the #EvWindow.
+ *
+ * It returns the uri of the document showed in the #EvWindow.
+ *
+ * Returns: the uri of the document showed in the #EvWindow.
+ */
+const char *
+ev_window_get_uri (EvWindow *ev_window)
+{
+ return ev_window->priv->uri;
+}
+
+/**
+ * ev_window_close_dialogs:
+ * @ev_window: The window where dialogs will be closed.
+ *
+ * It looks for password, print and properties dialogs and closes them and
+ * frees them from memory. If there is any print job it does free it too.
+ */
+static void
+ev_window_close_dialogs (EvWindow *ev_window)
+{
+ if (ev_window->priv->print_dialog)
+ gtk_widget_destroy (ev_window->priv->print_dialog);
+ ev_window->priv->print_dialog = NULL;
+
+ if (ev_window->priv->properties)
+ gtk_widget_destroy (ev_window->priv->properties);
+ ev_window->priv->properties = NULL;
+}
+
+static void
+ev_window_clear_progress_idle (EvWindow *ev_window)
+{
+ if (ev_window->priv->progress_idle > 0)
+ g_source_remove (ev_window->priv->progress_idle);
+ ev_window->priv->progress_idle = 0;
+}
+
+static void
+reset_progress_idle (EvWindow *ev_window)
+{
+ ev_window->priv->progress_idle = 0;
+}
+
+static void
+ev_window_show_progress_message (EvWindow *ev_window,
+ guint interval,
+ GSourceFunc function)
+{
+ if (ev_window->priv->progress_idle > 0)
+ g_source_remove (ev_window->priv->progress_idle);
+ ev_window->priv->progress_idle =
+ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ interval, function,
+ ev_window,
+ (GDestroyNotify)reset_progress_idle);
+}
+
+static void
+ev_window_reset_progress_cancellable (EvWindow *ev_window)
+{
+ if (ev_window->priv->progress_cancellable)
+ g_cancellable_reset (ev_window->priv->progress_cancellable);
+ else
+ ev_window->priv->progress_cancellable = g_cancellable_new ();
+}
+
+static void
+ev_window_progress_response_cb (EvProgressMessageArea *area,
+ gint response,
+ EvWindow *ev_window)
+{
+ if (response == GTK_RESPONSE_CANCEL)
+ g_cancellable_cancel (ev_window->priv->progress_cancellable);
+ ev_window_set_message_area (ev_window, NULL);
+}
+
+static gboolean
+show_loading_progress (EvWindow *ev_window)
+{
+ GtkWidget *area;
+ gchar *text;
+ gchar *display_name;
+
+ if (ev_window->priv->message_area)
+ return FALSE;
+
+ text = g_uri_unescape_string (ev_window->priv->uri, NULL);
+ display_name = g_markup_escape_text (text, -1);
+ g_free (text);
+ text = g_strdup_printf (_("Loading document from “%s”"),
+ display_name);
+
+ area = ev_progress_message_area_new (GTK_STOCK_OPEN,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+
+ g_free (text);
+ g_free (display_name);
+
+ return FALSE;
+}
+
+static void
+ev_window_load_remote_failed (EvWindow *ev_window,
+ GError *error)
+{
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE);
+ ev_window->priv->in_reload = FALSE;
+ ev_window_error_message (ev_window, error,
+ "%s", _("Unable to open document"));
+ g_free (ev_window->priv->local_uri);
+ ev_window->priv->local_uri = NULL;
+ ev_window->priv->uri_mtime = 0;
+}
+
+static void
+set_uri_mtime (GFile *source,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GFileInfo *info;
+ GError *error = NULL;
+
+ info = g_file_query_info_finish (source, async_result, &error);
+
+ if (error) {
+ ev_window->priv->uri_mtime = 0;
+ g_error_free (error);
+ } else {
+ GTimeVal mtime;
+
+ g_file_info_get_modification_time (info, &mtime);
+ ev_window->priv->uri_mtime = mtime.tv_sec;
+ g_object_unref (info);
+ }
+
+ g_object_unref (source);
+}
+
+static void
+mount_volume_ready_cb (GFile *source,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ g_file_mount_enclosing_volume_finish (source, async_result, &error);
+
+ if (error) {
+ ev_window_load_remote_failed (ev_window, error);
+ g_object_unref (source);
+ g_error_free (error);
+ } else {
+ /* Volume successfully mounted,
+ try opening the file again */
+ ev_window_load_file_remote (ev_window, source);
+ }
+}
+
+static void
+window_open_file_copy_ready_cb (GFile *source,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ ev_window_clear_progress_idle (ev_window);
+ ev_window_set_message_area (ev_window, NULL);
+
+ g_file_copy_finish (source, async_result, &error);
+ if (!error) {
+ ev_job_scheduler_push_job (ev_window->priv->load_job, EV_JOB_PRIORITY_NONE);
+ g_file_query_info_async (source,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ 0, G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback)set_uri_mtime,
+ ev_window);
+ return;
+ }
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) {
+ GMountOperation *operation;
+
+ operation = gtk_mount_operation_new (GTK_WINDOW (ev_window));
+ g_file_mount_enclosing_volume (source,
+ G_MOUNT_MOUNT_NONE,
+ operation, NULL,
+ (GAsyncReadyCallback)mount_volume_ready_cb,
+ ev_window);
+ g_object_unref (operation);
+ } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ ev_window_clear_load_job (ev_window);
+ ev_window_clear_local_uri (ev_window);
+ g_free (ev_window->priv->uri);
+ ev_window->priv->uri = NULL;
+ g_object_unref (source);
+
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE);
+ } else {
+ ev_window_load_remote_failed (ev_window, error);
+ g_object_unref (source);
+ }
+
+ g_error_free (error);
+}
+
+static void
+window_open_file_copy_progress_cb (goffset n_bytes,
+ goffset total_bytes,
+ EvWindow *ev_window)
+{
+ gchar *status;
+ gdouble fraction;
+
+ if (!ev_window->priv->message_area)
+ return;
+
+ if (total_bytes <= 0)
+ return;
+
+ fraction = n_bytes / (gdouble)total_bytes;
+ status = g_strdup_printf (_("Downloading document (%d%%)"),
+ (gint)(fraction * 100));
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+
+ g_free (status);
+}
+
+static void
+ev_window_load_file_remote (EvWindow *ev_window,
+ GFile *source_file)
+{
+ GFile *target_file;
+
+ if (!ev_window->priv->local_uri) {
+ char *base_name, *template;
+ GFile *tmp_file;
+ GError *err = NULL;
+
+ /* We'd like to keep extension of source uri since
+ * it helps to resolve some mime types, say cbz.
+ */
+ base_name = g_file_get_basename (source_file);
+ template = g_strdup_printf ("document.XXXXXX-%s", base_name);
+ g_free (base_name);
+
+ tmp_file = ev_mkstemp_file (template, &err);
+ g_free (template);
+ if (tmp_file == NULL) {
+ ev_window_error_message (ev_window, err,
+ "%s", _("Failed to load remote file."));
+ g_error_free (err);
+ return;
+ }
+
+ ev_window->priv->local_uri = g_file_get_uri (tmp_file);
+ g_object_unref (tmp_file);
+
+ ev_job_load_set_uri (EV_JOB_LOAD (ev_window->priv->load_job),
+ ev_window->priv->local_uri);
+ }
+
+ ev_window_reset_progress_cancellable (ev_window);
+
+ target_file = g_file_new_for_uri (ev_window->priv->local_uri);
+ g_file_copy_async (source_file, target_file,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ ev_window->priv->progress_cancellable,
+ (GFileProgressCallback)window_open_file_copy_progress_cb,
+ ev_window,
+ (GAsyncReadyCallback)window_open_file_copy_ready_cb,
+ ev_window);
+ g_object_unref (target_file);
+
+ ev_window_show_progress_message (ev_window, 1,
+ (GSourceFunc)show_loading_progress);
+}
+
+void
+ev_window_open_uri (EvWindow *ev_window,
+ const char *uri,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string)
+{
+ GFile *source_file;
+
+ ev_window->priv->in_reload = FALSE;
+
+ if (ev_window->priv->uri &&
+ g_ascii_strcasecmp (ev_window->priv->uri, uri) == 0) {
+ ev_window_reload_document (ev_window, dest);
+ return;
+ }
+
+ if (ev_window->priv->monitor) {
+ g_object_unref (ev_window->priv->monitor);
+ ev_window->priv->monitor = NULL;
+ }
+
+ ev_window_close_dialogs (ev_window);
+ ev_window_clear_load_job (ev_window);
+ ev_window_clear_local_uri (ev_window);
+
+ ev_window->priv->window_mode = mode;
+
+ if (ev_window->priv->uri)
+ g_free (ev_window->priv->uri);
+ ev_window->priv->uri = g_strdup (uri);
+
+ if (ev_window->priv->metadata)
+ g_object_unref (ev_window->priv->metadata);
+
+ source_file = g_file_new_for_uri (uri);
+ if (!ev_file_is_temp (source_file) && ev_is_metadata_supported_for_file (source_file)) {
+ ev_window->priv->metadata = ev_metadata_new (source_file);
+ ev_window->priv->is_new_doc = ev_metadata_is_empty (ev_window->priv->metadata);
+ } else {
+ ev_window->priv->metadata = NULL;
+ }
+
+ if (ev_window->priv->search_string)
+ g_free (ev_window->priv->search_string);
+ ev_window->priv->search_string = search_string ?
+ g_strdup (search_string) : NULL;
+
+ if (ev_window->priv->dest)
+ g_object_unref (ev_window->priv->dest);
+ ev_window->priv->dest = dest ? g_object_ref (dest) : NULL;
+
+ setup_size_from_metadata (ev_window);
+ setup_model_from_metadata (ev_window);
+
+ ev_window->priv->load_job = ev_job_load_new (uri);
+ g_signal_connect (ev_window->priv->load_job,
+ "finished",
+ G_CALLBACK (ev_window_load_job_cb),
+ ev_window);
+
+ if (!g_file_is_native (source_file) && !ev_window->priv->local_uri) {
+ ev_window_load_file_remote (ev_window, source_file);
+ } else {
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), TRUE);
+ g_object_unref (source_file);
+ ev_job_scheduler_push_job (ev_window->priv->load_job, EV_JOB_PRIORITY_NONE);
+ }
+}
+
+void
+ev_window_open_document (EvWindow *ev_window,
+ EvDocument *document,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string)
+{
+ if (document == ev_window->priv->document)
+ return;
+
+ ev_window_close_dialogs (ev_window);
+ ev_window_clear_load_job (ev_window);
+ ev_window_clear_local_uri (ev_window);
+
+ if (ev_window->priv->monitor) {
+ g_object_unref (ev_window->priv->monitor);
+ ev_window->priv->monitor = NULL;
+ }
+
+ if (ev_window->priv->uri)
+ g_free (ev_window->priv->uri);
+ ev_window->priv->uri = g_strdup (ev_document_get_uri (document));
+
+ setup_size_from_metadata (ev_window);
+ setup_model_from_metadata (ev_window);
+
+ ev_document_model_set_document (ev_window->priv->model, document);
+
+ setup_document_from_metadata (ev_window);
+ setup_view_from_metadata (ev_window);
+
+ if (dest) {
+ EvLink *link;
+ EvLinkAction *link_action;
+
+ link_action = ev_link_action_new_dest (dest);
+ link = ev_link_new (NULL, link_action);
+ ev_view_handle_link (EV_VIEW (ev_window->priv->view), link);
+ /* FIXME: link action should inc dest ref counting
+ * or not unref it at all
+ */
+ g_object_ref (dest);
+ g_object_unref (link);
+ }
+
+ switch (mode) {
+ case EV_WINDOW_MODE_FULLSCREEN:
+ ev_window_run_fullscreen (ev_window);
+ break;
+ case EV_WINDOW_MODE_PRESENTATION:
+ ev_window_run_presentation (ev_window);
+ break;
+ default:
+ break;
+ }
+
+ if (search_string && EV_IS_DOCUMENT_FIND (document)) {
+ ev_window_cmd_edit_find (NULL, ev_window);
+ egg_find_bar_set_search_string (EGG_FIND_BAR (ev_window->priv->find_bar),
+ search_string);
+ }
+
+ /* Create a monitor for the document */
+ ev_window->priv->monitor = ev_file_monitor_new (ev_window->priv->uri);
+ g_signal_connect_swapped (ev_window->priv->monitor, "changed",
+ G_CALLBACK (ev_window_document_changed),
+ ev_window);
+}
+
+static void
+ev_window_reload_local (EvWindow *ev_window)
+{
+ const gchar *uri;
+
+ uri = ev_window->priv->local_uri ? ev_window->priv->local_uri : ev_window->priv->uri;
+ ev_window->priv->reload_job = ev_job_load_new (uri);
+ g_signal_connect (ev_window->priv->reload_job, "finished",
+ G_CALLBACK (ev_window_reload_job_cb),
+ ev_window);
+ ev_job_scheduler_push_job (ev_window->priv->reload_job, EV_JOB_PRIORITY_NONE);
+}
+
+static gboolean
+show_reloading_progress (EvWindow *ev_window)
+{
+ GtkWidget *area;
+ gchar *text;
+
+ if (ev_window->priv->message_area)
+ return FALSE;
+
+ text = g_strdup_printf (_("Reloading document from %s"),
+ ev_window->priv->uri);
+ area = ev_progress_message_area_new (GTK_STOCK_REFRESH,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+ g_free (text);
+
+ return FALSE;
+}
+
+static void
+reload_remote_copy_ready_cb (GFile *remote,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ ev_window_clear_progress_idle (ev_window);
+
+ g_file_copy_finish (remote, async_result, &error);
+ if (error) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ ev_window_error_message (ev_window, error,
+ "%s", _("Failed to reload document."));
+ g_error_free (error);
+ } else {
+ ev_window_reload_local (ev_window);
+ }
+
+ g_object_unref (remote);
+}
+
+static void
+reload_remote_copy_progress_cb (goffset n_bytes,
+ goffset total_bytes,
+ EvWindow *ev_window)
+{
+ gchar *status;
+ gdouble fraction;
+
+ if (!ev_window->priv->message_area)
+ return;
+
+ if (total_bytes <= 0)
+ return;
+
+ fraction = n_bytes / (gdouble)total_bytes;
+ status = g_strdup_printf (_("Downloading document (%d%%)"),
+ (gint)(fraction * 100));
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+
+ g_free (status);
+}
+
+static void
+query_remote_uri_mtime_cb (GFile *remote,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GFileInfo *info;
+ GTimeVal mtime;
+ GError *error = NULL;
+
+ info = g_file_query_info_finish (remote, async_result, &error);
+ if (error) {
+ g_error_free (error);
+ g_object_unref (remote);
+ ev_window_reload_local (ev_window);
+
+ return;
+ }
+
+ g_file_info_get_modification_time (info, &mtime);
+ if (ev_window->priv->uri_mtime != mtime.tv_sec) {
+ GFile *target_file;
+
+ /* Remote file has changed */
+ ev_window->priv->uri_mtime = mtime.tv_sec;
+
+ ev_window_reset_progress_cancellable (ev_window);
+
+ target_file = g_file_new_for_uri (ev_window->priv->local_uri);
+ g_file_copy_async (remote, target_file,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ ev_window->priv->progress_cancellable,
+ (GFileProgressCallback)reload_remote_copy_progress_cb,
+ ev_window,
+ (GAsyncReadyCallback)reload_remote_copy_ready_cb,
+ ev_window);
+ g_object_unref (target_file);
+ ev_window_show_progress_message (ev_window, 1,
+ (GSourceFunc)show_reloading_progress);
+ } else {
+ g_object_unref (remote);
+ ev_window_reload_local (ev_window);
+ }
+
+ g_object_unref (info);
+}
+
+static void
+ev_window_reload_remote (EvWindow *ev_window)
+{
+ GFile *remote;
+
+ remote = g_file_new_for_uri (ev_window->priv->uri);
+ /* Reload the remote uri only if it has changed */
+ g_file_query_info_async (remote,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ 0, G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback)query_remote_uri_mtime_cb,
+ ev_window);
+}
+
+static void
+ev_window_reload_document (EvWindow *ev_window,
+ EvLinkDest *dest)
+{
+ gint page;
+
+
+ ev_window_clear_reload_job (ev_window);
+ ev_window->priv->in_reload = TRUE;
+
+ page = ev_document_model_get_page (ev_window->priv->model);
+
+ if (ev_window->priv->dest)
+ g_object_unref (ev_window->priv->dest);
+ ev_window->priv->dest = dest ? g_object_ref (dest) : NULL;
+
+ if (ev_window->priv->local_uri) {
+ ev_window_reload_remote (ev_window);
+ } else {
+ ev_window_reload_local (ev_window);
+ }
+}
+
+static void
+file_open_dialog_response_cb (GtkWidget *chooser,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ GSList *uris;
+ gchar *uri;
+
+ uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (chooser));
+
+ ev_application_open_uri_list (EV_APP, uris,
+ gtk_window_get_screen (GTK_WINDOW (ev_window)),
+ gtk_get_current_event_time ());
+
+ g_slist_foreach (uris, (GFunc)g_free, NULL);
+ g_slist_free (uris);
+
+ uri = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (chooser));
+ ev_application_set_filechooser_uri (EV_APP,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ uri);
+ g_free (uri);
+ }
+
+ gtk_widget_destroy (chooser);
+}
+
+static void
+ev_window_cmd_file_open (GtkAction *action, EvWindow *window)
+{
+ GtkWidget *chooser;
+ const gchar *default_uri;
+ gchar *parent_uri = NULL;
+
+ chooser = gtk_file_chooser_dialog_new (_("Open Document"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL);
+
+ ev_document_factory_add_filters (chooser, NULL);
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), FALSE);
+
+ default_uri = ev_application_get_filechooser_uri (EV_APP, GTK_FILE_CHOOSER_ACTION_OPEN);
+ if (!default_uri && window->priv->uri) {
+ GFile *file, *parent;
+
+ file = g_file_new_for_uri (window->priv->uri);
+ parent = g_file_get_parent (file);
+ if (parent) {
+ parent_uri = g_file_get_uri (parent);
+ default_uri = parent_uri;
+ g_object_unref (parent);
+ }
+ g_object_unref (file);
+ }
+
+ if (default_uri) {
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (chooser), default_uri);
+ } else {
+ const gchar *folder;
+
+ folder = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
+ folder ? folder : g_get_home_dir ());
+ }
+ g_free (parent_uri);
+
+ g_signal_connect (chooser, "response",
+ G_CALLBACK (file_open_dialog_response_cb),
+ window);
+
+ gtk_widget_show (chooser);
+}
+
+static void
+ev_window_open_copy_at_dest (EvWindow *window,
+ EvLinkDest *dest)
+{
+ EvWindow *new_window = EV_WINDOW (ev_window_new ());
+
+ ev_window_n_copies++;
+
+ if (window->priv->metadata)
+ new_window->priv->metadata = g_object_ref (window->priv->metadata);
+ ev_window_open_document (new_window,
+ window->priv->document,
+ dest, 0, NULL);
+ gtk_window_present (GTK_WINDOW (new_window));
+}
+
+static void
+ev_window_cmd_file_open_copy (GtkAction *action, EvWindow *window)
+{
+ ev_window_open_copy_at_dest (window, NULL);
+}
+
+static void
+ev_window_cmd_recent_file_activate (GtkAction *action,
+ EvWindow *window)
+{
+ GtkRecentInfo *info;
+ const gchar *uri;
+
+ info = g_object_get_data (G_OBJECT (action), "gtk-recent-info");
+ g_assert (info != NULL);
+
+ uri = gtk_recent_info_get_uri (info);
+
+ ev_application_open_uri_at_dest (EV_APP, uri,
+ gtk_window_get_screen (GTK_WINDOW (window)),
+ NULL, 0, NULL, gtk_get_current_event_time ());
+}
+
+static void
+ev_window_open_recent_action_item_activated (EvOpenRecentAction *action,
+ const gchar *uri,
+ EvWindow *window)
+{
+ ev_application_open_uri_at_dest (EV_APP, uri,
+ gtk_window_get_screen (GTK_WINDOW (window)),
+ NULL, 0, NULL, gtk_get_current_event_time ());
+}
+
+static void
+ev_window_add_recent (EvWindow *window, const char *filename)
+{
+ gtk_recent_manager_add_item (window->priv->recent_manager, filename);
+}
+
+static gint
+compare_recent_items (GtkRecentInfo *a, GtkRecentInfo *b)
+{
+ gboolean has_ev_a, has_ev_b;
+ const gchar *evince = g_get_application_name ();
+
+ has_ev_a = gtk_recent_info_has_application (a, evince);
+ has_ev_b = gtk_recent_info_has_application (b, evince);
+
+ if (has_ev_a && has_ev_b) {
+ time_t time_a, time_b;
+
+ time_a = gtk_recent_info_get_modified (a);
+ time_b = gtk_recent_info_get_modified (b);
+
+ return (time_b - time_a);
+ } else if (has_ev_a) {
+ return -1;
+ } else if (has_ev_b) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Doubles underscore to avoid spurious menu accels.
+ */
+static gchar *
+ev_window_get_recent_file_label (gint index, const gchar *filename)
+{
+ GString *str;
+ gint length;
+ const gchar *p;
+ const gchar *end;
+ gboolean is_rtl;
+
+ is_rtl = (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL);
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ length = strlen (filename);
+ str = g_string_sized_new (length + 10);
+ g_string_printf (str, "%s_%d. ", is_rtl ? "\xE2\x80\x8F" : "", index);
+
+ p = filename;
+ end = filename + length;
+
+ while (p != end) {
+ const gchar *next;
+ next = g_utf8_next_char (p);
+
+ switch (*p) {
+ case '_':
+ g_string_append (str, "__");
+ break;
+ default:
+ g_string_append_len (str, p, next - p);
+ break;
+ }
+
+ p = next;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+ev_window_recent_action_connect_proxy_cb (GtkActionGroup *action_group,
+ GtkAction *action,
+ GtkWidget *proxy,
+ gpointer data)
+{
+ GtkLabel *label;
+
+ if (!GTK_IS_MENU_ITEM (proxy))
+ return;
+
+ label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (proxy)));
+
+ gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_MIDDLE);
+ gtk_label_set_max_width_chars (label, MAX_RECENT_ITEM_LEN);
+}
+
+static void
+ev_window_setup_recent (EvWindow *ev_window)
+{
+ GList *items, *l;
+ guint n_items = 0;
+ const gchar *evince = g_get_application_name ();
+ static guint i = 0;
+
+ if (ev_window->priv->recent_ui_id > 0) {
+ gtk_ui_manager_remove_ui (ev_window->priv->ui_manager,
+ ev_window->priv->recent_ui_id);
+ gtk_ui_manager_ensure_update (ev_window->priv->ui_manager);
+ }
+ ev_window->priv->recent_ui_id = gtk_ui_manager_new_merge_id (ev_window->priv->ui_manager);
+
+ if (ev_window->priv->recent_action_group) {
+ gtk_ui_manager_remove_action_group (ev_window->priv->ui_manager,
+ ev_window->priv->recent_action_group);
+ g_object_unref (ev_window->priv->recent_action_group);
+ }
+ ev_window->priv->recent_action_group = gtk_action_group_new ("RecentFilesActions");
+ g_signal_connect (ev_window->priv->recent_action_group, "connect-proxy",
+ G_CALLBACK (ev_window_recent_action_connect_proxy_cb), NULL);
+
+ gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
+ ev_window->priv->recent_action_group, -1);
+
+ items = gtk_recent_manager_get_items (ev_window->priv->recent_manager);
+ items = g_list_sort (items, (GCompareFunc) compare_recent_items);
+
+ for (l = items; l && l->data; l = g_list_next (l)) {
+ GtkRecentInfo *info;
+ GtkAction *action;
+ gchar *action_name;
+ gchar *label;
+ const gchar *mime_type;
+ gchar *content_type;
+ GIcon *icon = NULL;
+
+ info = (GtkRecentInfo *) l->data;
+
+ if (!gtk_recent_info_has_application (info, evince) ||
+ (gtk_recent_info_is_local (info) && !gtk_recent_info_exists (info)))
+ continue;
+
+ action_name = g_strdup_printf ("RecentFile%u", i++);
+ label = ev_window_get_recent_file_label (
+ n_items + 1, gtk_recent_info_get_display_name (info));
+
+ mime_type = gtk_recent_info_get_mime_type (info);
+ content_type = g_content_type_from_mime_type (mime_type);
+ if (content_type != NULL) {
+ icon = g_content_type_get_icon (content_type);
+ g_free (content_type);
+ }
+
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", action_name,
+ "label", label,
+ "gicon", icon,
+ "always-show-image", TRUE,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (action),
+ "gtk-recent-info",
+ gtk_recent_info_ref (info),
+ (GDestroyNotify) gtk_recent_info_unref);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (ev_window_cmd_recent_file_activate),
+ (gpointer) ev_window);
+
+ gtk_action_group_add_action (ev_window->priv->recent_action_group,
+ action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ev_window->priv->ui_manager,
+ ev_window->priv->recent_ui_id,
+ "/MainMenu/FileMenu/RecentFilesMenu",
+ label,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ g_free (action_name);
+ g_free (label);
+ if (icon != NULL)
+ g_object_unref (icon);
+
+ if (++n_items == 5)
+ break;
+ }
+
+ g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (items);
+}
+
+static gboolean
+show_saving_progress (GFile *dst)
+{
+ EvWindow *ev_window;
+ GtkWidget *area;
+ gchar *text;
+ gchar *uri;
+ EvSaveType save_type;
+
+ ev_window = EV_WINDOW (g_object_get_data (G_OBJECT (dst), "ev-window"));
+ ev_window->priv->progress_idle = 0;
+
+ if (ev_window->priv->message_area)
+ return FALSE;
+
+ save_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dst), "save-type"));
+ uri = g_file_get_uri (dst);
+ switch (save_type) {
+ case EV_SAVE_DOCUMENT:
+ text = g_strdup_printf (_("Saving document to %s"), uri);
+ break;
+ case EV_SAVE_ATTACHMENT:
+ text = g_strdup_printf (_("Saving attachment to %s"), uri);
+ break;
+ case EV_SAVE_IMAGE:
+ text = g_strdup_printf (_("Saving image to %s"), uri);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ g_free (uri);
+ area = ev_progress_message_area_new (GTK_STOCK_SAVE,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+ g_free (text);
+
+ return FALSE;
+}
+
+static void
+window_save_file_copy_ready_cb (GFile *src,
+ GAsyncResult *async_result,
+ GFile *dst)
+{
+ EvWindow *ev_window;
+ GError *error = NULL;
+
+ ev_window = EV_WINDOW (g_object_get_data (G_OBJECT (dst), "ev-window"));
+ ev_window_clear_progress_idle (ev_window);
+
+ if (g_file_copy_finish (src, async_result, &error)) {
+ ev_tmp_file_unlink (src);
+ return;
+ }
+
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ gchar *name;
+
+ name = g_file_get_basename (dst);
+ ev_window_error_message (ev_window, error,
+ _("The file could not be saved as “%s”."),
+ name);
+ g_free (name);
+ }
+ ev_tmp_file_unlink (src);
+ g_error_free (error);
+}
+
+static void
+window_save_file_copy_progress_cb (goffset n_bytes,
+ goffset total_bytes,
+ GFile *dst)
+{
+ EvWindow *ev_window;
+ EvSaveType save_type;
+ gchar *status;
+ gdouble fraction;
+
+ ev_window = EV_WINDOW (g_object_get_data (G_OBJECT (dst), "ev-window"));
+
+ if (!ev_window->priv->message_area)
+ return;
+
+ if (total_bytes <= 0)
+ return;
+
+ fraction = n_bytes / (gdouble)total_bytes;
+ save_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dst), "save-type"));
+
+ switch (save_type) {
+ case EV_SAVE_DOCUMENT:
+ status = g_strdup_printf (_("Uploading document (%d%%)"),
+ (gint)(fraction * 100));
+ break;
+ case EV_SAVE_ATTACHMENT:
+ status = g_strdup_printf (_("Uploading attachment (%d%%)"),
+ (gint)(fraction * 100));
+ break;
+ case EV_SAVE_IMAGE:
+ status = g_strdup_printf (_("Uploading image (%d%%)"),
+ (gint)(fraction * 100));
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+
+ g_free (status);
+}
+
+static void
+ev_window_save_remote (EvWindow *ev_window,
+ EvSaveType save_type,
+ GFile *src,
+ GFile *dst)
+{
+ ev_window_reset_progress_cancellable (ev_window);
+ g_object_set_data (G_OBJECT (dst), "ev-window", ev_window);
+ g_object_set_data (G_OBJECT (dst), "save-type", GINT_TO_POINTER (save_type));
+ g_file_copy_async (src, dst,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ ev_window->priv->progress_cancellable,
+ (GFileProgressCallback)window_save_file_copy_progress_cb,
+ dst,
+ (GAsyncReadyCallback)window_save_file_copy_ready_cb,
+ dst);
+ ev_window->priv->progress_idle =
+ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ 1,
+ (GSourceFunc)show_saving_progress,
+ dst,
+ NULL);
+}
+
+static void
+ev_window_clear_save_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->save_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->save_job))
+ ev_job_cancel (ev_window->priv->save_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->save_job,
+ ev_window_save_job_cb,
+ ev_window);
+ g_object_unref (ev_window->priv->save_job);
+ ev_window->priv->save_job = NULL;
+ }
+}
+
+static void
+ev_window_save_job_cb (EvJob *job,
+ EvWindow *window)
+{
+ if (ev_job_is_failed (job)) {
+ ev_window_error_message (window, job->error,
+ _("The file could not be saved as “%s”."),
+ EV_JOB_SAVE (job)->uri);
+ } else {
+ ev_window_add_recent (window, EV_JOB_SAVE (job)->uri);
+ }
+
+ ev_window_clear_save_job (window);
+}
+
+static void
+file_save_dialog_response_cb (GtkWidget *fc,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ gchar *uri;
+ GFile *file, *parent;
+
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (fc);
+ return;
+ }
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
+ file = g_file_new_for_uri (uri);
+ parent = g_file_get_parent (file);
+ g_object_unref (file);
+ if (parent) {
+ gchar *folder_uri;
+
+ folder_uri = g_file_get_uri (parent);
+ ev_application_set_filechooser_uri (EV_APP,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ folder_uri);
+ g_free (folder_uri);
+ g_object_unref (parent);
+ }
+
+ /* FIXME: remote copy should be done here rather than in the save job,
+ * so that we can track progress and cancel the operation
+ */
+
+ ev_window_clear_save_job (ev_window);
+ ev_window->priv->save_job = ev_job_save_new (ev_window->priv->document,
+ uri, ev_window->priv->uri);
+ g_signal_connect (ev_window->priv->save_job, "finished",
+ G_CALLBACK (ev_window_save_job_cb),
+ ev_window);
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (ev_window->priv->save_job, EV_JOB_PRIORITY_NONE);
+
+ g_free (uri);
+ gtk_widget_destroy (fc);
+}
+
+static void
+ev_window_cmd_save_as (GtkAction *action, EvWindow *ev_window)
+{
+ GtkWidget *fc;
+ gchar *base_name;
+ GFile *file;
+ const gchar *default_uri;
+
+ fc = gtk_file_chooser_dialog_new (
+ _("Save a Copy"),
+ GTK_WINDOW (ev_window), GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ ev_document_factory_add_filters (fc, ev_window->priv->document);
+ gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (fc),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fc), FALSE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fc), TRUE);
+ file = g_file_new_for_uri (ev_window->priv->uri);
+ base_name = g_file_get_basename (file);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fc), base_name);
+
+ default_uri = ev_application_get_filechooser_uri (EV_APP, GTK_FILE_CHOOSER_ACTION_SAVE);
+ if (default_uri) {
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (fc), default_uri);
+ } else {
+ const gchar *folder;
+
+ folder = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fc),
+ folder ? folder : g_get_home_dir ());
+ }
+
+ g_object_unref (file);
+ g_free (base_name);
+
+ g_signal_connect (fc, "response",
+ G_CALLBACK (file_save_dialog_response_cb),
+ ev_window);
+
+ gtk_widget_show (fc);