/* $Id$ */
/*
 * Copyright (c) 2003,2005 Jasper Huijsmans <jasper@xfce.org>
 * Copyright (c) 2003 Benedikt Meurer <benny@xfce.org>
 *
 * This 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.
 *
 * This 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 this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <ctype.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <glib.h>
#include <gtk/gtk.h>

#include "dialogs.h"
#include "xfce-gtk-extensions.h"

#ifndef PATH_MAX
#define DEFAULT_LENGTH 1024
#else
#if (PATH_MAX < 1024)
#define DEFAULT_LENGTH 1024
#else
#define DEFAULT_LENGTH PATH_MAX
#endif
#endif

/* max. length of a message */
#define MAXMESSAGELEN   2048

/* Darwin does not have a variable named environ
   but has a function which you can get the environ
   variable with.  */
#if defined(__APPLE__) && defined(__MACH__)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif

/* create a header with optional icon (may be NULL) in larger bold font;
 * background and foreground colors are taken from gtk */

static void
private_cb_eventbox_style_set (GtkWidget * widget, GtkStyle * old_style)
{
    static gboolean recursive = 0;
    GtkStyle *style;

    if (recursive > 0)
	return;

    ++recursive;
    style = gtk_widget_get_style (widget);
    gtk_widget_modify_bg (widget, GTK_STATE_NORMAL,
			  &style->bg[GTK_STATE_SELECTED]);
    --recursive;
}

static void
private_cb_label_style_set (GtkWidget * widget, GtkStyle * old_style)
{
    static gboolean recursive = 0;
    GtkStyle *style;

    if (recursive > 0)
	return;

    ++recursive;
    style = gtk_widget_get_style (widget);
    gtk_widget_modify_fg (widget, GTK_STATE_NORMAL,
			  &style->fg[GTK_STATE_SELECTED]);
    --recursive;
}

/* compat stub, remove when the time has come */
GtkWidget*
create_header_with_image (GtkWidget *image, const char *text)
{
        return xfce_create_header_with_image (image, text);
}

/**
 * xfce_create_header_with_image:
 * @image: a GtkImage or NULL if no image should be displayed in the header.
 * @text: the text to be displayed in the header.
 *
 * Creates a header with an optional @image (may be NULL) in larger bold
 * font. Background and foreground colors are taken from Gtk+ style.
 *
 * Return value: the container widget that contains the header widgets.
 **/
GtkWidget*
xfce_create_header_with_image (GtkWidget   *image,
                               const gchar *text)
{
    GtkWidget *eventbox, *label, *hbox;
    GtkStyle *style;
    char *markup;

    eventbox = gtk_event_box_new ();
    gtk_widget_show (eventbox);

    hbox = gtk_hbox_new (FALSE, 12);
    gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
    gtk_widget_show (hbox);
    gtk_container_add (GTK_CONTAINER (eventbox), hbox);

    if (image)
    {
	gtk_widget_show (image);
	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
    }

    style = gtk_widget_get_style (eventbox);
    gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL,
			  &style->bg[GTK_STATE_SELECTED]);

    markup =
	g_strconcat ("<span size=\"larger\" weight=\"bold\">", text,
		     "</span>", NULL);
    label = gtk_label_new (markup);
    g_free (markup);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
    gtk_widget_show (label);
    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
    
    style = gtk_widget_get_style (label);
    gtk_widget_modify_fg (label, GTK_STATE_NORMAL,
			  &style->fg[GTK_STATE_SELECTED]);

    g_signal_connect_after (G_OBJECT (eventbox), "style_set",
			    G_CALLBACK (private_cb_eventbox_style_set), NULL);
    g_signal_connect_after (G_OBJECT (label), "style_set",
			    G_CALLBACK (private_cb_label_style_set), NULL);

    return eventbox;
}

/* compat stub, remove when the time has come */
GtkWidget*
create_header (GdkPixbuf *icon, const char *text)
{
    return xfce_create_header (icon, text);
}

/**
 * xfce_create_header:
 * @icon: a GdkPixbuf or NULL if no icon should be displayed in the header.
 * @text: a text to be displayed in the header.
 *
 * Creates a header with an optional @icon (may be NULL) in larger bold
 * font. Background and foreground colors are taken from Gtk+ style.
 *
 * Return value: the container widget that contains the header widgets.
 **/
GtkWidget*
xfce_create_header (GdkPixbuf *icon, const gchar *text)
{
    GtkWidget *image = NULL;
    
    if (icon)
	image = gtk_image_new_from_pixbuf (icon);

    return xfce_create_header_with_image (image, text);
}

/* compat stub */
GtkWidget*
mixed_button_new (const char *stock, const char *text)
{
    return xfce_create_mixed_button (stock, text);
}

/**
 * xfce_create_mixed_button:
 * @stock: a stock item name.
 * @text: a text to display.
 *
 * Creates a button with both @stock icon and @text.
 *
 * Return value: the newly created mixed button widget.
 **/
GtkWidget*
xfce_create_mixed_button (const gchar *stock, const gchar *text)
{
    GtkWidget *button, *align, *image, *hbox, *label;

    button = gtk_button_new ();
    label = gtk_label_new_with_mnemonic (text);
    gtk_label_set_mnemonic_widget (GTK_LABEL (label), button);

    image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON);
    hbox = gtk_hbox_new (FALSE, 2);

    align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);

    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
    gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);

    gtk_container_add (GTK_CONTAINER (button), align);
    gtk_container_add (GTK_CONTAINER (align), hbox);
    gtk_widget_show_all (align);

    return button;
}

/* compat stub */
GtkWidget*
small_label (const gchar *text)
{
    return xfce_create_small_label (text);
}

/**
 * xfce_create_small_label:
 * @text: label text.
 *
 * Creates a small italic label using Gtk's markup functionality.
 *
 * Return value: the newly created label.
 **/
GtkWidget*
xfce_create_small_label (const gchar *text)
{
    GtkWidget *label;
    gchar *tmp;

    g_return_val_if_fail (text != NULL, NULL);

    tmp = g_strdup_printf ("<small><i>%s</i></small>", text);
    label = gtk_label_new (tmp);
    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
    g_free (tmp);

    return label;
}

static void
message_dialog (GtkMessageType type, const gchar * message)
{
    GtkWidget *dlg;

    dlg = gtk_message_dialog_new (NULL,
				  GTK_DIALOG_MODAL,
				  type, GTK_BUTTONS_CLOSE, "%s", message);

    xfce_gtk_window_center_on_monitor_with_pointer (GTK_WINDOW (dlg));
    gtk_dialog_run (GTK_DIALOG (dlg));
    gtk_widget_destroy (dlg);
}

/**
 * xfce_err:
 * @format: printf-style format string.
 * @Varargs: argument list.
 * 
 * Displays a modal error dialog to the user and blocks until the users
 * clicks the close button.
 */
void
xfce_err (const gchar * format, ...)
{
    va_list ap;

    va_start (ap, format);
    xfce_verr (format, ap);
    va_end (ap);
}

/**
 * xfce_verr:
 * @format: printf-style format string.
 * @ap: variable argument list pointer.
 *
 * Displays a modal error dialog to the user and blocks until the users
 * clicks the close button.
 */
void
xfce_verr (const gchar * format, va_list ap)
{
    gchar message_buffer[MAXMESSAGELEN];

    g_return_if_fail (format != NULL);

    g_vsnprintf (message_buffer, sizeof (message_buffer), format, ap);
    message_dialog (GTK_MESSAGE_ERROR, message_buffer);
}

/**
 * xfce_warn:
 * @format: printf-style format string.
 * @Varargs: argument list.
 * 
 * Displays a modal warning dialog to the user and blocks until the users
 * clicks the close button.
 */
void
xfce_warn (const gchar * format, ...)
{
    va_list ap;

    va_start (ap, format);
    xfce_vwarn (format, ap);
    va_end (ap);
}

/**
 * xfce_vwarn:
 * @format: printf-style format string.
 * @ap: variable argument list pointer.
 *
 * Displays a modal warning dialog to the user and blocks until the users
 * clicks the close button.
 */
void
xfce_vwarn (const gchar * format, va_list ap)
{
    gchar message_buffer[MAXMESSAGELEN];

    g_return_if_fail (format != NULL);

    g_vsnprintf (message_buffer, sizeof (message_buffer), format, ap);
    message_dialog (GTK_MESSAGE_WARNING, message_buffer);
}

/**
 * xfce_info:
 * @format: printf-style format string.
 * @Varargs: argument list.
 * 
 * Displays a modal info dialog to the user and blocks until the users
 * clicks the close button.
 */
void
xfce_info (const gchar * format, ...)
{
    va_list ap;

    va_start (ap, format);
    xfce_vinfo (format, ap);
    va_end (ap);
}

/**
 * xfce_vinfo:
 * @format: printf-style format string.
 * @ap: variable argument list pointer.
 *
 * Displays a modal info dialog to the user and blocks until the users
 * clicks the close button.
 */
void
xfce_vinfo (const gchar * format, va_list ap)
{
    gchar message_buffer[MAXMESSAGELEN];

    g_return_if_fail (format != NULL);

    g_vsnprintf (message_buffer, sizeof (message_buffer), format, ap);
    message_dialog (GTK_MESSAGE_INFO, message_buffer);
}

/* wrappers around gtk_message_dialog (OBSOLETE) */
void
show_info (const char *text)
{
    message_dialog (GTK_MESSAGE_INFO, text);
}

void
show_warning (const char *text)
{
    message_dialog (GTK_MESSAGE_WARNING, text);
}

void
show_error (const char *text)
{
    message_dialog (GTK_MESSAGE_ERROR, text);
}

/* compat stub */
gboolean
confirm (const char *text, const char *stock, const char *action)
{
    return xfce_confirm (text, stock, action);
}

/**
 * xfce_confirm:
 * @text:     a question text
 * @stock_id: a stock item name
 * @action:   if non-NULL, this text is used on the confirm button together
 *            with the @stock icon.
 *
 * Runs a modal confirmation dialog, that has a 'cancel' and a 'confirm'
 * button. The 'confirm' button text can be set by @action if given.
 *
 * If @stock_id is equal to GTK_STOCK_YES, the 'cancel' button becomes a 'no' button.
 *
 * Return value: TRUE if the user confirms, else FALSE.
 **/
gboolean
xfce_confirm (const gchar *text,
              const gchar *stock_id,
              const gchar *action)
{
    GtkWidget *dialog, *button;
    int response = GTK_RESPONSE_NONE;

    dialog = gtk_message_dialog_new (NULL,
                                     GTK_DIALOG_MODAL,
                                     GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
                                     "%s", text);
    if (strcmp (stock_id, GTK_STOCK_YES) == 0)
        button = gtk_button_new_from_stock (GTK_STOCK_NO);
    else
        button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);

    gtk_widget_show (button);
    gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
                                  GTK_RESPONSE_NO);

    if (action)
        button = mixed_button_new (stock_id, action);
    else
        button = gtk_button_new_from_stock (stock_id);
    gtk_widget_show (button);
    gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
                                  GTK_RESPONSE_YES);

    gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);

    xfce_gtk_window_center_on_monitor_with_pointer (GTK_WINDOW (dialog));
    response = gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_hide (dialog);
    gtk_widget_destroy (dialog);

    if (response == GTK_RESPONSE_YES)
        return TRUE;
    else
        return FALSE;
}

static GtkWidget *
xfce_message_dialog_new_valist(GtkWindow *parent,
                               const gchar *title,
                               const gchar *icon_id,
                               const gchar *primary_text,
                               const gchar *secondary_text,
                               const gchar *first_button_type,
                               va_list args)
{
    GtkWidget *dialog, *label;
    GtkWidget *hbox, *align;
    GtkWidget *image;
    gchar *markup = NULL;
    gint default_response = G_MININT;
    gchar *primary_escaped, *secondary_escaped;

    /* create the dialog */
    dialog = gtk_dialog_new ();

    gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
    gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
    gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);

    /* set title if needed */
    if (title)
        gtk_window_set_title (GTK_WINDOW (dialog), title);
    else
        gtk_window_set_title (GTK_WINDOW (dialog), "");

    /* add image */
    hbox = gtk_hbox_new (FALSE, 12);

    if (icon_id)
    {
        image = gtk_image_new_from_stock (icon_id, GTK_ICON_SIZE_DIALOG);
        gtk_misc_set_alignment (GTK_MISC (image), 0, 0);

        gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, FALSE, 0);
    }

    /* add the text */
    markup = NULL;

    if (primary_text && secondary_text)
    {
        primary_escaped = g_markup_escape_text (primary_text, -1);
        secondary_escaped = g_markup_escape_text (secondary_text, -1);
        markup = g_strdup_printf ("<span weight='bold' size='large'>%s</span>"
                                  "\n\n%s", primary_escaped, secondary_escaped);
        g_free (primary_escaped);
        g_free (secondary_escaped);
    }
    else if (primary_text)
    {
        primary_escaped = g_markup_escape_text (primary_text, -1);
        markup =
            g_strdup_printf ("<span weight='bold' size='large'>%s</span>",
                             primary_escaped);
        g_free (primary_escaped);
    }
    else
    {
        markup = g_markup_escape_text (secondary_text, -1);
    }

    label = gtk_label_new (NULL);

    gtk_label_set_markup (GTK_LABEL (label), markup);
    gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
    gtk_label_set_selectable (GTK_LABEL (label), TRUE);

    gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);

    g_free (markup);

    gtk_widget_show_all (hbox);
    gtk_box_pack_start (GTK_BOX ((GTK_DIALOG (dialog))->vbox), hbox,
                        FALSE, FALSE, 0);

    align = gtk_alignment_new (0, 0, 0, 0);
    gtk_widget_set_size_request (align, 12, 12);
    gtk_widget_show (align);
    gtk_box_pack_start (GTK_BOX ((GTK_DIALOG (dialog))->vbox), align,
                        FALSE, FALSE, 0);

    /* sizing according to GNOME HIG, 
     * except with a border of 8 instead of 12 */
    gtk_container_set_border_width (GTK_CONTAINER (dialog), 2);
    gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);

    /* add the buttons */
    if (first_button_type)
    {
        const gchar *text;
        gint response_id;

        text = first_button_type;

        while (text != NULL)
        {
            GtkWidget *button;

            if (strcmp (text, XFCE_CUSTOM_PIXBUF_BUTTON) == 0)
            {
                GdkPixbuf *icon, *scaled;
                GtkWidget *align, *image, *hbox, *label;
                gint w, h;

                text = va_arg (args, gchar *);
                icon = va_arg (args, GdkPixbuf *);
                default_response = response_id = va_arg (args, int);

                button = gtk_button_new ();
                label = gtk_label_new_with_mnemonic (text);
                gtk_label_set_mnemonic_widget (GTK_LABEL (label), button);

                gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &w, &h);

                if ((gdk_pixbuf_get_width (icon) != w) &&
                    (gdk_pixbuf_get_height (icon) != h))
                {
                    scaled = gdk_pixbuf_scale_simple (icon, w, h,
                                                      GDK_INTERP_BILINEAR);

                    g_object_unref (G_OBJECT (icon));
                    icon = scaled;
                }

                image = gtk_image_new_from_pixbuf (icon);

                hbox = gtk_hbox_new (FALSE, 2);
                align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);

                gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
                gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);

                gtk_container_add (GTK_CONTAINER (button), align);
                gtk_container_add (GTK_CONTAINER (align), hbox);
                gtk_widget_show_all (align);
            }
            else if (strcmp (text, XFCE_CUSTOM_STOCK_BUTTON) == 0)
            {
                gchar *icon_stock;

                text = va_arg (args, gchar *);
                icon_stock = va_arg (args, gchar *);
                default_response = response_id = va_arg (args, int);

                button = xfce_create_mixed_button (icon_stock, text);
            }
            else if (strcmp (text, XFCE_CUSTOM_BUTTON) == 0)
            {
                text = va_arg (args, gchar *);
                default_response = response_id = va_arg (args, int);

                button = gtk_button_new_with_label (text);
            }
            else
            {
                default_response = response_id = va_arg (args, int);

                button = gtk_button_new_from_stock (text);
            }

            GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
            gtk_widget_show (button);
            gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
                                          response_id);

            text = va_arg (args, gchar *);
        }
    }
    
    if (default_response != G_MININT)
        gtk_dialog_set_default_response (GTK_DIALOG (dialog), default_response);

    if (parent)
    {
        gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
        gtk_window_set_position (GTK_WINDOW (dialog),
                                 GTK_WIN_POS_CENTER_ON_PARENT);
    }
    else
    {
        xfce_gtk_window_center_on_monitor_with_pointer (GTK_WINDOW (dialog));
    }
    
    return dialog;
}

/**
 * xfce_message_dialog:
 * @parent            : Transient parent of the dialog, or %NULL
 * @title             : Title of the dialog, or %NULL
 * @icon_id           : Gtk stock icon to show in the dialog
 * @primary_text      : Text shown in large bold font
 * @secondary_text    : Text shown in normal font
 * @first_button_type : Type of the first button, or %NULL
 * @Varargs           : %NULL ended list of parameters and response ID
 *
 * xfce_message_dialog() creates a dialog widget as in
 * xfce_message_dialog_new(), and then runs the dialog as a modal dialog and
 * returns the response id selected by the user.
 *
 * See xfce_message_dialog_new() for more information.
 *
 * Returns: The selected response id.
 **/
gint
xfce_message_dialog(GtkWindow *parent,
                    const gchar *title,
                    const gchar *icon_id,
                    const gchar *primary_text,
                    const gchar *secondary_text,
                    const gchar *first_button_type,
                    ...)
{
    GtkWidget *dlg;
    va_list args;
    gint response;
    
    va_start(args, first_button_type);
    
    dlg = xfce_message_dialog_new_valist(parent, title, icon_id,
                                         primary_text, secondary_text,
                                         first_button_type, args);
    
    va_end(args);
    
    /* show dialog and return */
    response = gtk_dialog_run(GTK_DIALOG(dlg));
    gtk_widget_hide(dlg);
    gtk_widget_destroy(dlg);

    return response;
}

/**
 * xfce_message_dialog_new:
 * @parent            : Transient parent of the dialog, or %NULL
 * @title             : Title of the dialog, or %NULL
 * @icon_id           : Gtk stock icon to show in the dialog
 * @primary_text      : Text shown in large bold font
 * @secondary_text    : Text shown in normal font
 * @first_button_type : Type of the first button, or %NULL
 * @Varargs           : %NULL ended list of parameters and response ID
 *
 * xfce_message_dialog_new() allows you to easily create a dialog respecting 
 * the GNOME HIG; it accepts gtk stock buttons, or custom buttons using a 
 * gtk stock icon and text or a #GdkPixbuf and text, or just text. 
 * 
 * It returns a message dialog with transient parent @parent (or %NULL 
 * for none) and title @title (or %NULL for a default title) containing 
 * the icon @icon, large and bold @primary_text, normal @secondary_text and 
 * some buttons. 
 *
 * The buttons are defined by first_button_type and the next arguments in
 * following format @type, @param1[, @param2, @param3]; there are four types 
 * of button :
 * <itemizedlist>
 *  <listitem>
 *  <para>
 *  %GTK_STOCK_* : @param1 is used for the response ID, others aren't used.
 *  </para>
 *  </listitem>
 *  <listitem>
 *  <para>
 *  %XFCE_CUSTOM_BUTTON : @param1 is used for the button text and @param2 for 
 *  the the response ID, @param3 isn't used.
 *  </para>
 *  </listitem>
 *  <listitem>
 *  <para>
 *  %XFCE_CUSTOM_STOCK_BUTTON : @param1 is used for the button text, @param2 
 *  for the gtk stock icon name and @param3 for the response ID.
 *  </para>
 *  </listitem>
 *  <listitem>
 *  <para>
 *  %XFCE_CUSTOM_PIXBUF_BUTTON : @param1 is used for the button text, @param2 
 *  for the GdkPixbuf pointer and @param3 for the response ID.
 *  </para>
 *  </listitem>
 * </itemizedlist>  
 * Here's a simple example:
 * <informalexample><programlisting>
 *  GtkWidget *dlg = xfce_message_dialog (parent, "Question",
 *                                        GTK_STOCK_DIALOG_QUESTION,
 *                                        "There are unsaved modifications",
 *                                        "The menu has been modified, do you want to save it before quitting?",
 *                                        XFCE_CUSTOM_STOCK_BUTTON, "Forget modifications", 
 *                                        GTK_STOCK_QUIT, GTK_RESPONSE_NO,
 *                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 *                                        GTK_STOCK_SAVE, GTK_RESPONSE_YES, 
 *                                        NULL);
 * </programlisting></informalexample>
 *
 * Return value: A new #GtkDialog.
 **/
GtkWidget *
xfce_message_dialog_new(GtkWindow *parent,
                        const gchar *title,
                        const gchar *icon_id,
                        const gchar *primary_text,
                        const gchar *secondary_text,
                        const gchar *first_button_type,
                        ...)
{
    GtkWidget *dlg;
    va_list args;
    
    va_start(args, first_button_type);
    dlg = xfce_message_dialog_new_valist(parent, title, icon_id,
                                         primary_text, secondary_text,
                                         first_button_type, args);
    va_end(args);
    
    return dlg;
}

/* command execution with error dialog */

gboolean
exec_command (gchar * command)
{
    gboolean success = TRUE;
    GError *error = NULL;	/* this must be NULL to prevent crash :( */

    g_return_val_if_fail (command != NULL, FALSE);

    if (!g_spawn_command_line_async (command, &error))
    {
	char *msg = g_strcompress (error->message);
	char *text =
	    g_strconcat ("Could not run command: ", command, ":\n", msg,
			 NULL);
	show_error (text);
	g_free (msg);
	g_free (text);
	g_error_free (error);
	success = FALSE;
    }
    return success;
}

gboolean
exec_command_full_with_envp (gchar ** argv, gchar ** envp)
{
    gboolean success = TRUE;
    gboolean retval;
    GError *error = NULL;	/* this must be NULL to prevent crash :( */

    g_return_val_if_fail (argv != NULL, FALSE);

    if (envp == NULL)
    {
	envp = environ;
    }

    retval =
	g_spawn_async (NULL, argv, envp, G_SPAWN_SEARCH_PATH, NULL, NULL,
		       NULL, &error);
    if (!retval)
    {
	gchar *msg = g_strcompress (error->message);
	char *text =
	    g_strconcat ("Could not run command: ", argv[0], ":\n", msg,
			 NULL);
	g_error_free (error);
	g_free (msg);
	show_error (text);
	g_free (text);
	success = FALSE;
    }

    return success;
}

/* vi:set ts=8 sw=4: */
