mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-09-19 07:41:26 +00:00
be706e1600
reviewed by: John Sullivan * libnautilus-extensions/nautilus-stock-dialogs.c: (nautilus_timed_wait_start_with_duration), (nautilus_timed_wait_start): * libnautilus-extensions/nautilus-stock-dialogs.h: * libnautilus-extensions/nautilus-trash-directory.c: (find_directory_start): Increase delay before putting up "searching for trash dialog"
544 lines
14 KiB
C
544 lines
14 KiB
C
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
|
|
|
|
/* nautilus-stock-dialogs.c: Various standard dialogs for Nautilus.
|
|
|
|
Copyright (C) 2000 Eazel, Inc.
|
|
|
|
The Gnome Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
The Gnome Library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with the Gnome Library; see the file COPYING.LIB. If not,
|
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA.
|
|
|
|
Authors: Darin Adler <darin@eazel.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "nautilus-stock-dialogs.h"
|
|
|
|
#include "nautilus-glib-extensions.h"
|
|
#include "nautilus-gnome-extensions.h"
|
|
#include "nautilus-string.h"
|
|
#include <gtk/gtkbox.h>
|
|
#include <gtk/gtklabel.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtksignal.h>
|
|
#include <libgnome/gnome-i18n.h>
|
|
#include <libgnomeui/gnome-messagebox.h>
|
|
#include <libgnomeui/gnome-stock.h>
|
|
#include <libgnomeui/gnome-uidefs.h>
|
|
|
|
#define TIMED_WAIT_STANDARD_DURATION 5000
|
|
#define TIMED_WAIT_MIN_TIME_UP 3000
|
|
|
|
typedef struct {
|
|
NautilusCancelCallback cancel_callback;
|
|
gpointer callback_data;
|
|
|
|
/* Parameters for creation of the window. */
|
|
char *window_title;
|
|
char *wait_message;
|
|
GtkWindow *parent_window;
|
|
|
|
/* Timer to determine when we need to create the window. */
|
|
guint timeout_handler_id;
|
|
|
|
/* Window, once it's created. */
|
|
GnomeDialog *dialog;
|
|
|
|
/* system time (microseconds) when dialog was created */
|
|
gint64 dialog_creation_time;
|
|
|
|
} TimedWait;
|
|
|
|
static GHashTable *timed_wait_hash_table;
|
|
|
|
static void find_message_label_callback (GtkWidget *widget,
|
|
gpointer callback_data);
|
|
|
|
static void timed_wait_cancel_callback (GtkObject *object, gpointer callback_data);
|
|
|
|
|
|
static guint
|
|
timed_wait_hash (gconstpointer value)
|
|
{
|
|
const TimedWait *wait;
|
|
|
|
wait = value;
|
|
|
|
return GPOINTER_TO_UINT (wait->cancel_callback)
|
|
^ GPOINTER_TO_UINT (wait->callback_data);
|
|
}
|
|
|
|
static gboolean
|
|
timed_wait_hash_equal (gconstpointer value1, gconstpointer value2)
|
|
{
|
|
const TimedWait *wait1, *wait2;
|
|
|
|
wait1 = value1;
|
|
wait2 = value2;
|
|
|
|
return wait1->cancel_callback == wait2->cancel_callback
|
|
&& wait1->callback_data == wait2->callback_data;
|
|
}
|
|
|
|
static void
|
|
add_label_to_dialog (GnomeDialog *dialog, const char *message)
|
|
{
|
|
GtkLabel *message_widget;
|
|
|
|
if (message == NULL) {
|
|
return;
|
|
}
|
|
|
|
message_widget = GTK_LABEL (gtk_label_new (message));
|
|
gtk_label_set_line_wrap (message_widget, TRUE);
|
|
gtk_label_set_justify (message_widget,
|
|
GTK_JUSTIFY_LEFT);
|
|
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
|
|
GTK_WIDGET (message_widget),
|
|
TRUE, TRUE, GNOME_PAD);
|
|
}
|
|
|
|
static gboolean
|
|
timed_wait_delayed_destroy_dialog_callback (gpointer callback_data)
|
|
{
|
|
gtk_object_destroy (GTK_OBJECT (callback_data));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
timed_wait_free (TimedWait *wait, gboolean do_min_time_up_test)
|
|
{
|
|
guint32 time_up;
|
|
|
|
g_assert (g_hash_table_lookup (timed_wait_hash_table, wait) != NULL);
|
|
|
|
g_hash_table_remove (timed_wait_hash_table, wait);
|
|
|
|
g_free (wait->window_title);
|
|
g_free (wait->wait_message);
|
|
if (wait->parent_window != NULL) {
|
|
gtk_widget_unref (GTK_WIDGET (wait->parent_window));
|
|
}
|
|
if (wait->timeout_handler_id != 0) {
|
|
gtk_timeout_remove (wait->timeout_handler_id);
|
|
}
|
|
if (wait->dialog != NULL) {
|
|
|
|
/* Make sure to detach from the "destroy" signal, or we'll
|
|
* double-free
|
|
*/
|
|
|
|
gtk_signal_disconnect_by_func (GTK_OBJECT (wait->dialog),
|
|
timed_wait_cancel_callback, wait);
|
|
|
|
/* compute time up in milliseconds
|
|
*/
|
|
time_up = (nautilus_get_system_time () - wait->dialog_creation_time) / 1000;
|
|
|
|
if (do_min_time_up_test && time_up < TIMED_WAIT_MIN_TIME_UP) {
|
|
gtk_timeout_add (TIMED_WAIT_MIN_TIME_UP - time_up, timed_wait_delayed_destroy_dialog_callback, wait->dialog);
|
|
} else {
|
|
gtk_object_destroy (GTK_OBJECT (wait->dialog));
|
|
}
|
|
}
|
|
|
|
/* And the wait object itself. */
|
|
g_free (wait);
|
|
}
|
|
|
|
static void
|
|
timed_wait_cancel_callback (GtkObject *object, gpointer callback_data)
|
|
{
|
|
TimedWait *wait;
|
|
|
|
wait = callback_data;
|
|
|
|
g_assert (GNOME_DIALOG (object) == wait->dialog);
|
|
|
|
if (wait->cancel_callback != NULL) {
|
|
(* wait->cancel_callback) (wait->callback_data);
|
|
}
|
|
|
|
timed_wait_free (wait, FALSE);
|
|
}
|
|
|
|
static gboolean
|
|
timed_wait_callback (gpointer callback_data)
|
|
{
|
|
TimedWait *wait;
|
|
GnomeDialog *dialog;
|
|
|
|
wait = callback_data;
|
|
|
|
/* Put up the timed wait window. */
|
|
dialog = GNOME_DIALOG (gnome_dialog_new (wait->window_title,
|
|
wait->cancel_callback != NULL ? GNOME_STOCK_BUTTON_CANCEL : NULL,
|
|
NULL));
|
|
gtk_window_set_wmclass (GTK_WINDOW (dialog), "dialog", "Nautilus");
|
|
add_label_to_dialog (dialog, wait->wait_message);
|
|
gnome_dialog_set_close (dialog, TRUE);
|
|
wait->dialog_creation_time = nautilus_get_system_time ();
|
|
gtk_widget_show_all (GTK_WIDGET (dialog));
|
|
|
|
/* FIXME bugzilla.eazel.com 2441:
|
|
* Could parent here, but it's complicated because we
|
|
* don't want this window to go away just because the parent
|
|
* would go away first.
|
|
*/
|
|
|
|
/* Make the dialog cancel the timed wait when it goes away.
|
|
* Connect to "destroy" instead of "clicked" since we want
|
|
* to be called no matter how the dialog goes away.
|
|
*/
|
|
gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
|
|
timed_wait_cancel_callback, wait);
|
|
|
|
wait->timeout_handler_id = 0;
|
|
wait->dialog = dialog;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
nautilus_timed_wait_start_with_duration (int duration,
|
|
NautilusCancelCallback cancel_callback,
|
|
gpointer callback_data,
|
|
const char *window_title,
|
|
const char *wait_message,
|
|
GtkWindow *parent_window)
|
|
{
|
|
TimedWait *wait;
|
|
|
|
g_return_if_fail (callback_data != NULL);
|
|
g_return_if_fail (window_title != NULL);
|
|
g_return_if_fail (wait_message != NULL);
|
|
g_return_if_fail (parent_window == NULL || GTK_IS_WINDOW (parent_window));
|
|
|
|
/* Create the timed wait record. */
|
|
wait = g_new0 (TimedWait, 1);
|
|
wait->window_title = g_strdup (window_title);
|
|
wait->wait_message = g_strdup (wait_message);
|
|
wait->cancel_callback = cancel_callback;
|
|
wait->callback_data = callback_data;
|
|
wait->parent_window = parent_window;
|
|
|
|
if (parent_window != NULL) {
|
|
gtk_widget_ref (GTK_WIDGET (parent_window));
|
|
}
|
|
|
|
/* Start the timer. */
|
|
wait->timeout_handler_id = gtk_timeout_add (duration, timed_wait_callback, wait);
|
|
|
|
/* Put in the hash table so we can find it later. */
|
|
if (timed_wait_hash_table == NULL) {
|
|
timed_wait_hash_table = g_hash_table_new
|
|
(timed_wait_hash, timed_wait_hash_equal);
|
|
}
|
|
g_assert (g_hash_table_lookup (timed_wait_hash_table, wait) == NULL);
|
|
g_hash_table_insert (timed_wait_hash_table, wait, wait);
|
|
g_assert (g_hash_table_lookup (timed_wait_hash_table, wait) == wait);
|
|
}
|
|
|
|
void
|
|
nautilus_timed_wait_start (NautilusCancelCallback cancel_callback,
|
|
gpointer callback_data,
|
|
const char *window_title,
|
|
const char *wait_message,
|
|
GtkWindow *parent_window)
|
|
{
|
|
nautilus_timed_wait_start_with_duration
|
|
(TIMED_WAIT_STANDARD_DURATION,
|
|
cancel_callback, callback_data,
|
|
window_title, wait_message, parent_window);
|
|
}
|
|
|
|
void
|
|
nautilus_timed_wait_stop (NautilusCancelCallback cancel_callback,
|
|
gpointer callback_data)
|
|
{
|
|
TimedWait key;
|
|
TimedWait *wait;
|
|
|
|
g_return_if_fail (callback_data != NULL);
|
|
|
|
key.cancel_callback = cancel_callback;
|
|
key.callback_data = callback_data;
|
|
wait = g_hash_table_lookup (timed_wait_hash_table, &key);
|
|
|
|
g_return_if_fail (wait != NULL);
|
|
|
|
timed_wait_free (wait, TRUE);
|
|
}
|
|
|
|
static const char **
|
|
convert_varargs_to_name_array (va_list args)
|
|
{
|
|
GPtrArray *resizeable_array;
|
|
const char *name;
|
|
const char **plain_ole_array;
|
|
|
|
resizeable_array = g_ptr_array_new ();
|
|
|
|
do {
|
|
name = va_arg (args, const char *);
|
|
g_ptr_array_add (resizeable_array, (gpointer) name);
|
|
} while (name != NULL);
|
|
|
|
plain_ole_array = (const char **) resizeable_array->pdata;
|
|
|
|
g_ptr_array_free (resizeable_array, FALSE);
|
|
|
|
return plain_ole_array;
|
|
}
|
|
|
|
static gboolean
|
|
delete_event_callback (gpointer data,
|
|
gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (GNOME_IS_DIALOG (data), FALSE);
|
|
gtk_signal_emit_stop_by_name (GTK_OBJECT (data),
|
|
"delete_event");
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
nautilus_simple_dialog (GtkWidget *parent, gboolean ignore_close_box,
|
|
const char *text, const char *title, ...)
|
|
{
|
|
va_list button_title_args;
|
|
const char **button_titles;
|
|
GtkWidget *dialog;
|
|
GtkWidget *top_widget;
|
|
int result;
|
|
|
|
|
|
/* Create the dialog. */
|
|
va_start (button_title_args, title);
|
|
button_titles = convert_varargs_to_name_array (button_title_args);
|
|
va_end (button_title_args);
|
|
dialog = gnome_dialog_newv (title, button_titles);
|
|
g_free (button_titles);
|
|
|
|
/* Allow close. */
|
|
if (ignore_close_box) {
|
|
gtk_signal_connect (GTK_OBJECT (dialog),
|
|
"delete_event",
|
|
GTK_SIGNAL_FUNC (delete_event_callback),
|
|
NULL);
|
|
|
|
}
|
|
|
|
gnome_dialog_set_close (GNOME_DIALOG (dialog), TRUE);
|
|
|
|
gtk_window_set_wmclass (GTK_WINDOW (dialog), "dialog", "Nautilus");
|
|
|
|
/* Parent it if asked to. */
|
|
if (parent != NULL) {
|
|
top_widget = gtk_widget_get_toplevel (parent);
|
|
if (GTK_IS_WINDOW (top_widget)) {
|
|
gnome_dialog_set_parent (GNOME_DIALOG (dialog), GTK_WINDOW (top_widget));
|
|
}
|
|
}
|
|
|
|
/* Title it if asked to. */
|
|
add_label_to_dialog (GNOME_DIALOG (dialog), text);
|
|
|
|
/* Run it. */
|
|
gtk_widget_show_all (dialog);
|
|
result = gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
|
|
while (result == -1 && ignore_close_box) {
|
|
gtk_widget_show (GTK_WIDGET (dialog));
|
|
result = gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
find_message_label (GtkWidget *widget, const char *message)
|
|
{
|
|
char *text;
|
|
|
|
/* Turn on the flag if we find a label with the message
|
|
* in it.
|
|
*/
|
|
if (GTK_IS_LABEL (widget)) {
|
|
gtk_label_get (GTK_LABEL (widget), &text);
|
|
if (nautilus_strcmp (text, message) == 0) {
|
|
gtk_object_set_data (GTK_OBJECT (gtk_widget_get_toplevel (widget)),
|
|
"message label", widget);
|
|
}
|
|
}
|
|
|
|
/* Recurse for children. */
|
|
if (GTK_IS_CONTAINER (widget)) {
|
|
gtk_container_foreach (GTK_CONTAINER (widget),
|
|
find_message_label_callback,
|
|
(char *) message);
|
|
}
|
|
}
|
|
|
|
static void
|
|
find_message_label_callback (GtkWidget *widget, gpointer callback_data)
|
|
{
|
|
find_message_label (widget, callback_data);
|
|
}
|
|
|
|
static GnomeDialog *
|
|
show_message_box (const char *message,
|
|
const char *dialog_title,
|
|
const char *type,
|
|
const char *button_one,
|
|
const char *button_two,
|
|
GtkWindow *parent)
|
|
{
|
|
GtkWidget *box;
|
|
GtkLabel *message_label;
|
|
|
|
g_assert (dialog_title != NULL);
|
|
|
|
box = gnome_message_box_new (message, type, button_one, button_two, NULL);
|
|
gtk_window_set_title (GTK_WINDOW (box), dialog_title);
|
|
gtk_window_set_wmclass (GTK_WINDOW (box), "stock_dialog", "Nautilus");
|
|
|
|
/* A bit of a hack. We want to use gnome_message_box_new,
|
|
* but we want the message to be wrapped. So, we search
|
|
* for the label with this message so we can mark it.
|
|
*/
|
|
find_message_label (box, message);
|
|
message_label = GTK_LABEL (gtk_object_get_data (GTK_OBJECT (box), "message label"));
|
|
gtk_label_set_line_wrap (message_label, TRUE);
|
|
|
|
if (parent != NULL) {
|
|
gnome_dialog_set_parent (GNOME_DIALOG (box), parent);
|
|
}
|
|
gtk_widget_show (box);
|
|
return GNOME_DIALOG (box);
|
|
}
|
|
|
|
static GnomeDialog *
|
|
show_ok_box (const char *message,
|
|
const char *dialog_title,
|
|
const char *type,
|
|
GtkWindow *parent)
|
|
{
|
|
return show_message_box (message, dialog_title, type, GNOME_STOCK_BUTTON_OK, NULL, parent);
|
|
}
|
|
|
|
GnomeDialog *
|
|
nautilus_info_dialog (const char *info,
|
|
const char *dialog_title,
|
|
GtkWindow *parent)
|
|
{
|
|
return show_ok_box (info,
|
|
dialog_title == NULL ? _("Info") : dialog_title,
|
|
GNOME_MESSAGE_BOX_INFO, parent);
|
|
}
|
|
|
|
GnomeDialog *
|
|
nautilus_warning_dialog (const char *warning,
|
|
const char *dialog_title,
|
|
GtkWindow *parent)
|
|
{
|
|
return show_ok_box (warning,
|
|
dialog_title == NULL ? _("Warning") : dialog_title,
|
|
GNOME_MESSAGE_BOX_WARNING, parent);
|
|
}
|
|
|
|
GnomeDialog *
|
|
nautilus_error_dialog (const char *error,
|
|
const char *dialog_title,
|
|
GtkWindow *parent)
|
|
{
|
|
return show_ok_box (error,
|
|
dialog_title == NULL ? _("Error") : dialog_title,
|
|
GNOME_MESSAGE_BOX_ERROR, parent);
|
|
}
|
|
|
|
static void
|
|
clicked_callback (GnomeDialog *dialog,
|
|
int button_number,
|
|
const char *detailed_error_message)
|
|
{
|
|
GtkLabel *label;
|
|
|
|
switch (button_number) {
|
|
case 0: /* Details */
|
|
label = GTK_LABEL (gtk_object_get_data (GTK_OBJECT (dialog), "message label"));
|
|
gtk_label_set_text (label, detailed_error_message);
|
|
gtk_widget_hide (GTK_WIDGET (nautilus_gnome_dialog_get_button_by_index (dialog, 0)));
|
|
break;
|
|
case 1: /* OK */
|
|
gnome_dialog_close (dialog);
|
|
break;
|
|
}
|
|
}
|
|
|
|
GnomeDialog *
|
|
nautilus_error_dialog_with_details (const char *error_message,
|
|
const char *dialog_title,
|
|
const char *detailed_error_message,
|
|
GtkWindow *parent)
|
|
{
|
|
GnomeDialog *dialog;
|
|
|
|
g_return_val_if_fail (error_message != NULL, NULL);
|
|
g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
|
|
|
|
if (detailed_error_message == NULL
|
|
|| strcmp (error_message, detailed_error_message) == 0) {
|
|
return nautilus_error_dialog (error_message, dialog_title, parent);
|
|
}
|
|
|
|
dialog = show_message_box (error_message,
|
|
dialog_title == NULL ? _("Error") : dialog_title,
|
|
GNOME_MESSAGE_BOX_ERROR,
|
|
_("Details"), GNOME_STOCK_BUTTON_OK, parent);
|
|
|
|
/* Show the details when you click on the details button. */
|
|
gnome_dialog_set_close (dialog, FALSE);
|
|
gtk_signal_connect_full (GTK_OBJECT (dialog), "clicked",
|
|
clicked_callback, NULL, g_strdup (detailed_error_message),
|
|
g_free, FALSE, FALSE);
|
|
|
|
return dialog;
|
|
}
|
|
|
|
/**
|
|
* nautilus_yes_no_dialog:
|
|
*
|
|
* Create a dialog asking a question with two choices.
|
|
* The caller needs to set up any necessary callbacks
|
|
* for the buttons.
|
|
* @question: The text of the question.
|
|
* @yes_label: The label of the "yes" button.
|
|
* @no_label: The label of the "no" button.
|
|
* @parent: The parent window for this dialog.
|
|
*/
|
|
GnomeDialog *
|
|
nautilus_yes_no_dialog (const char *question,
|
|
const char *dialog_title,
|
|
const char *yes_label,
|
|
const char *no_label,
|
|
GtkWindow *parent)
|
|
{
|
|
return show_message_box (question,
|
|
dialog_title == NULL ? _("Question") : dialog_title,
|
|
GNOME_MESSAGE_BOX_QUESTION,
|
|
yes_label,
|
|
no_label,
|
|
parent);
|
|
}
|