plover-gtk/transactionhelper.c
changeset 25 e10f418d8e1d
child 38 a29623b68ca2
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/plover-gtk/transactionhelper.c	Sat Nov 15 19:10:51 2014 +0000
     1.3 @@ -0,0 +1,1037 @@
     1.4 +/*
     1.5 + * Copyright (C) 2014  J. Ali Harlow <ali@juiblex.co.uk>
     1.6 + *
     1.7 + * This program is free software; you can redistribute it and/or modify
     1.8 + * it under the terms of the GNU General Public License as published by
     1.9 + * the Free Software Foundation; either version 2 of the License, or
    1.10 + * (at your option) any later version.
    1.11 + *
    1.12 + * This program is distributed in the hope that it will be useful,
    1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.15 + * GNU General Public License for more details.
    1.16 + *
    1.17 + * You should have received a copy of the GNU General Public License along
    1.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    1.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    1.20 + */
    1.21 +
    1.22 +#include "config.h"
    1.23 +#include <stdlib.h>
    1.24 +#include <errno.h>
    1.25 +#include <gtk/gtk.h>
    1.26 +#include <plover/plover.h>
    1.27 +#include <plover/transaction.h>
    1.28 +#include <plover-gtk/transactionhelper.h>
    1.29 +
    1.30 +/*
    1.31 + * A PloverTransactionHelper uses a GtkAssistant to help a user run a
    1.32 + * transaction.
    1.33 + */
    1.34 +
    1.35 +G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT)
    1.36 +
    1.37 +enum {
    1.38 +    CLOSE=0,
    1.39 +    N_SIGNALS
    1.40 +};
    1.41 +
    1.42 +static guint signals[N_SIGNALS];
    1.43 +
    1.44 +static void plover_transaction_helper_finalize(PloverTransactionHelper *helper)
    1.45 +{
    1.46 +    g_free(helper->error_primary_text);
    1.47 +    g_free(helper->base);
    1.48 +    g_free(helper->unsatisfied);
    1.49 +    if (helper->comps)
    1.50 +	plover_comps_free(helper->comps);
    1.51 +    plover_vector_free(helper->report_adding);
    1.52 +    plover_vector_free(helper->report_removing);
    1.53 +}
    1.54 +
    1.55 +static void plover_transaction_helper_dispose(PloverTransactionHelper *helper)
    1.56 +{
    1.57 +    g_clear_error(&helper->error);
    1.58 +    if (helper->error_dialog)
    1.59 +    {
    1.60 +	g_signal_handlers_disconnect_by_data(helper->error_dialog,helper);
    1.61 +	gtk_widget_destroy(helper->error_dialog);
    1.62 +	helper->error_dialog=NULL;
    1.63 +    }
    1.64 +    if (helper->assistant)
    1.65 +    {
    1.66 +	g_signal_handlers_disconnect_by_data(helper->assistant,helper);
    1.67 +	g_clear_object(&helper->assistant);
    1.68 +    }
    1.69 +    g_clear_object(&helper->ui);
    1.70 +    g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL);
    1.71 +    g_slist_free(helper->transactions);
    1.72 +    helper->transactions=NULL;
    1.73 +    g_clear_object(&helper->installed);
    1.74 +    g_clear_object(&helper->upstream);
    1.75 +    g_clear_object(&helper->relocated_upstream);
    1.76 +}
    1.77 +
    1.78 +static void
    1.79 +  plover_transaction_helper_class_init(PloverTransactionHelperClass *klass)
    1.80 +{
    1.81 +    GObjectClass *gobject_class=G_OBJECT_CLASS(klass);
    1.82 +    gobject_class->finalize=
    1.83 +      (void (*)(GObject *))plover_transaction_helper_finalize;
    1.84 +    gobject_class->dispose=
    1.85 +      (void (*)(GObject *))plover_transaction_helper_dispose;
    1.86 +    signals[CLOSE]=g_signal_newv("close",
    1.87 +      G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL,
    1.88 +      g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL);
    1.89 +}
    1.90 +
    1.91 +static void plover_transaction_helper_init(PloverTransactionHelper *helper)
    1.92 +{
    1.93 +    helper->report_adding=plover_vector_new();
    1.94 +    helper->report_removing=plover_vector_new();
    1.95 +}
    1.96 +
    1.97 +static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant,
    1.98 +  PloverTransactionHelper *helper)
    1.99 +{
   1.100 +    gtk_widget_hide(GTK_WIDGET(helper->assistant));
   1.101 +    gtk_assistant_set_current_page(helper->assistant,0);
   1.102 +    g_signal_emit(helper,signals[CLOSE],0);
   1.103 +}
   1.104 +
   1.105 +static void plover_transaction_helper_assistant_close(GtkAssistant *assistant,
   1.106 +  PloverTransactionHelper *helper)
   1.107 +{
   1.108 +    gtk_widget_hide(GTK_WIDGET(helper->assistant));
   1.109 +    gtk_assistant_set_current_page(helper->assistant,0);
   1.110 +    g_signal_emit(helper,signals[CLOSE],0);
   1.111 +}
   1.112 +
   1.113 +static void
   1.114 +  plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper)
   1.115 +{
   1.116 +    gchar *package_list,*add,*remove,*s;
   1.117 +    GtkLabel *label;
   1.118 +    struct plover_vector *report;
   1.119 +    if (helper->report_adding->len)
   1.120 +    {
   1.121 +	plover_vector_sort(helper->report_adding);
   1.122 +	if (helper->report_adding_dependencies)
   1.123 +	{
   1.124 +	    report=plover_vector_dup(helper->report_adding);
   1.125 +	    if (helper->report_adding->len==1)
   1.126 +		plover_vector_append(report,"its dependencies");
   1.127 +	    else
   1.128 +		plover_vector_append(report,"their dependencies");
   1.129 +	    package_list=plover_vector_format_for_display(report);
   1.130 +	    plover_vector_free(report);
   1.131 +	}
   1.132 +	else
   1.133 +	    package_list=
   1.134 +	      plover_vector_format_for_display(helper->report_adding);
   1.135 +	add=g_strconcat("Packages to be installed or updated: ",package_list,
   1.136 +	  ".",NULL);
   1.137 +	g_free(package_list);
   1.138 +    }
   1.139 +    else
   1.140 +	add=NULL;
   1.141 +    if (helper->report_removing->len)
   1.142 +    {
   1.143 +	plover_vector_sort(helper->report_removing);
   1.144 +	if (helper->report_removing_dependants)
   1.145 +	{
   1.146 +	    report=plover_vector_dup(helper->report_removing);
   1.147 +	    if (helper->report_adding->len==1)
   1.148 +		plover_vector_append(report,"its dependants");
   1.149 +	    else
   1.150 +		plover_vector_append(report,"their dependants");
   1.151 +	    package_list=plover_vector_format_for_display(report);
   1.152 +	    plover_vector_free(report);
   1.153 +	}
   1.154 +	else
   1.155 +	    package_list=
   1.156 +	      plover_vector_format_for_display(helper->report_removing);
   1.157 +	remove=g_strconcat("Packages to be removed: ",package_list,".",NULL);
   1.158 +	g_free(package_list);
   1.159 +    }
   1.160 +    else
   1.161 +	remove=NULL;
   1.162 +    label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork"));
   1.163 +    if (add && remove)
   1.164 +	s=g_strconcat("<b>Installation Summary</b>\n\n",remove,"\n\n",add,NULL);
   1.165 +    else if (add || remove)
   1.166 +	s=g_strconcat("<b>Installation Summary</b>\n\n",add?add:remove,NULL);
   1.167 +    else
   1.168 +	s=g_strdup("<b>Installation Summary</b>\n\nNo changes scheduled");
   1.169 +    gtk_label_set_markup(label,s);
   1.170 +    g_free(s);
   1.171 +    g_free(add);
   1.172 +    g_free(remove);
   1.173 +}
   1.174 +
   1.175 +static void plover_transaction_helper_run(PloverTransactionHelper *helper);
   1.176 +
   1.177 +static void plover_transaction_helper_callback(GObject *source,
   1.178 +  GAsyncResult *result,gpointer user_data)
   1.179 +{
   1.180 +    GError *error=NULL;
   1.181 +    PloverTransactionHelper *helper=user_data;
   1.182 +    PloverTransaction *transaction=PLOVER_TRANSACTION(source);
   1.183 +    if (!plover_transaction_commit_finish(transaction,result,&error))
   1.184 +    {
   1.185 +	plover_transaction_helper_set_error(helper,error,
   1.186 +	  "Software installation failed");
   1.187 +	g_error_free(error);
   1.188 +    }
   1.189 +    else
   1.190 +	plover_transaction_helper_run(helper);
   1.191 +    g_signal_handlers_disconnect_by_data(transaction,helper);
   1.192 +    g_object_unref(transaction);
   1.193 +}
   1.194 +
   1.195 +static void plover_transaction_helper_transaction_status_changed(
   1.196 +  PloverTransaction *transaction,const char *status,
   1.197 +  PloverTransactionHelper *helper)
   1.198 +{
   1.199 +    GtkProgressBar *bar;
   1.200 +    bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
   1.201 +    gtk_progress_bar_set_text(bar,status);
   1.202 +}
   1.203 +
   1.204 +static void plover_transaction_helper_run(PloverTransactionHelper *helper)
   1.205 +{
   1.206 +    PloverTransaction *transaction;
   1.207 +    GtkWidget *page;
   1.208 +    page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
   1.209 +    if (helper->transactions)
   1.210 +    {
   1.211 +	if (helper->assistant)
   1.212 +	    gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
   1.213 +	transaction=helper->transactions->data;
   1.214 +	helper->transactions=g_slist_delete_link(helper->transactions,
   1.215 +	  helper->transactions);
   1.216 +	g_signal_connect(transaction,"status-changed",
   1.217 +	  G_CALLBACK(plover_transaction_helper_transaction_status_changed),
   1.218 +	  helper);
   1.219 +	plover_transaction_commit_async(transaction,NULL,
   1.220 +	  plover_transaction_helper_callback,helper);
   1.221 +    }
   1.222 +    else if (helper->assistant)
   1.223 +	gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
   1.224 +}
   1.225 +
   1.226 +static gboolean plover_transaction_helper_pulse(gpointer user_data)
   1.227 +{
   1.228 +    PloverTransactionHelper *helper=user_data;
   1.229 +    GtkWidget *w;
   1.230 +    GtkProgressBar *bar;
   1.231 +    if (!helper->assistant)
   1.232 +	return FALSE;
   1.233 +    w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"));
   1.234 +    bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar"));
   1.235 +    if (gtk_assistant_get_page_complete(helper->assistant,w))
   1.236 +    {
   1.237 +	gtk_progress_bar_set_fraction(bar,1.0);
   1.238 +	helper->pulse_handler=0;
   1.239 +	return FALSE;
   1.240 +    }
   1.241 +    else
   1.242 +    {
   1.243 +	gtk_progress_bar_pulse(bar);
   1.244 +	return TRUE;
   1.245 +    }
   1.246 +}
   1.247 +
   1.248 +static void
   1.249 +  plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper)
   1.250 +{
   1.251 +    GError *error=NULL;
   1.252 +    GtkToggleButton *button;
   1.253 +    PloverTransaction *transaction;
   1.254 +    button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui,
   1.255 +      "SIRemoveExisting"));
   1.256 +    if (gtk_toggle_button_get_active(button))
   1.257 +    {
   1.258 +	transaction=plover_transaction_new_remove(NULL,&error);
   1.259 +	if (!transaction)
   1.260 +	{
   1.261 +	    if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT))
   1.262 +		g_clear_error(&error);
   1.263 +	    if (error)
   1.264 +	    {
   1.265 +		plover_transaction_helper_set_error(helper,error,
   1.266 +		  "Software installation failed");
   1.267 +		g_error_free(error);
   1.268 +		return;
   1.269 +	    }
   1.270 +	}
   1.271 +	else
   1.272 +	    helper->transactions=
   1.273 +	      g_slist_prepend(helper->transactions,transaction);
   1.274 +    }
   1.275 +    /*
   1.276 +     * Note that PloverTransaction does support cancelling a transaction, but
   1.277 +     * there are a number of challenges with using it:
   1.278 +     *	- cancellation is only supported during the file phase if razor
   1.279 +     *	  has atomic rollback,
   1.280 +     *  - cancellation is not supported during post-transaction scripts at all
   1.281 +     *    (since by the time the first script is started the atomic has already
   1.282 +     *    been committed) and these can take quite some time,
   1.283 +     *  - where a transaction has an embedded COMMIT, any rollback won't
   1.284 +     *    go back beyond this point.
   1.285 +     * To support user-cancel, then, we would need some mechanism to:
   1.286 +     *  - Comunicate that the operation is being cancelled and this may take
   1.287 +     *    some time,
   1.288 +     *  - Not allow cancellation at all after the last post-transaction script
   1.289 +     *    phase is started,
   1.290 +     *  - Report the partially completed transaction where cancellation
   1.291 +     *    occurred after a COMMIT point.
   1.292 +     * At present, this doesn't appear worth the effort.
   1.293 +     */
   1.294 +    if (helper->assistant)
   1.295 +	gtk_assistant_commit(helper->assistant);
   1.296 +    plover_transaction_helper_run(helper);
   1.297 +    helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse,
   1.298 +      helper);
   1.299 +}
   1.300 +
   1.301 +static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant,
   1.302 +  GtkWidget *page,PloverTransactionHelper *helper)
   1.303 +{
   1.304 +    if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")))
   1.305 +	plover_transaction_helper_prepare_confirm(helper);
   1.306 +    else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")))
   1.307 +	plover_transaction_helper_prepare_progress(helper);
   1.308 +}
   1.309 +
   1.310 +static void
   1.311 +  plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button,
   1.312 +  PloverTransactionHelper *helper)
   1.313 +{
   1.314 +    GtkWidget *w;
   1.315 +    if (helper->assistant)
   1.316 +    {
   1.317 +	w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
   1.318 +	gtk_assistant_set_page_complete(helper->assistant,w,
   1.319 +	  gtk_toggle_button_get_active(button));
   1.320 +    }
   1.321 +}
   1.322 +
   1.323 +PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui)
   1.324 +{
   1.325 +    gsize len;
   1.326 +    gchar *s,*directory,*contents;
   1.327 +    GError *error=NULL;
   1.328 +    GtkWidget *w;
   1.329 +    PloverTransactionHelper *helper;
   1.330 +    g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL);
   1.331 +    helper=PLOVER_TRANSACTION_HELPER(
   1.332 +      g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL));
   1.333 +    if (ui)
   1.334 +	helper->ui=g_object_ref(ui);
   1.335 +    else
   1.336 +	helper->ui=gtk_builder_new();
   1.337 +    helper->assistant=
   1.338 +      GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation"));
   1.339 +    if (!helper->assistant)
   1.340 +    {
   1.341 +	directory=g_strdup(g_getenv("PLOVER_DATADIR"));
   1.342 +	if (!directory)
   1.343 +	{
   1.344 +#ifdef WIN32
   1.345 +	    s=g_win32_get_package_installation_directory_of_module(NULL);
   1.346 +	    directory=g_build_filename(s,"share","plover",NULL);
   1.347 +	    g_free(s);
   1.348 +#else
   1.349 +	    directory=g_strdup(PLOVER_DATADIR);
   1.350 +#endif
   1.351 +	}
   1.352 +	s=g_build_filename(directory,"software-installation.ui",NULL);
   1.353 +	g_free(directory);
   1.354 +	(void)g_file_get_contents(s,&contents,&len,&error);
   1.355 +	g_free(s);
   1.356 +	if (!error)
   1.357 +	{
   1.358 +	    (void)gtk_builder_add_from_string(helper->ui,contents,len,&error);
   1.359 +	    g_free(contents);
   1.360 +	}
   1.361 +	if (error)
   1.362 +	{
   1.363 +	    g_critical("software-installation.ui: %s",error->message);
   1.364 +	    g_clear_error(&error);
   1.365 +	    g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
   1.366 +	      "Internal error (no user interface)");
   1.367 +	    plover_transaction_helper_set_error(helper,error,
   1.368 +	      "Can't start installer");
   1.369 +	    return helper;
   1.370 +	}
   1.371 +	helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui,
   1.372 +	  "SoftwareInstallation"));
   1.373 +    }
   1.374 +    if (!helper->assistant)
   1.375 +    {
   1.376 +	g_critical("\"SoftwareInstallation\" object not found");
   1.377 +	g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
   1.378 +	  "Internal error (missing wizard)");
   1.379 +	plover_transaction_helper_set_error(helper,error,
   1.380 +	  "Can't start installer");
   1.381 +	g_error_free(error);
   1.382 +	return helper;
   1.383 +    }
   1.384 +    else
   1.385 +	g_object_ref(helper->assistant);
   1.386 +    if (!GTK_IS_ASSISTANT(helper->assistant))
   1.387 +    {
   1.388 +	g_critical("\"SoftwareInstallation\" is not a GtkAssistant");
   1.389 +	g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
   1.390 +	  "Internal error (unexpected wizard type)");
   1.391 +	plover_transaction_helper_set_error(helper,error,
   1.392 +	  "Can't start installer");
   1.393 +	g_error_free(error);
   1.394 +	return helper;
   1.395 +    }
   1.396 +    g_signal_connect(helper->assistant,"cancel",
   1.397 +      G_CALLBACK(plover_transaction_helper_assistant_cancel),helper);
   1.398 +    g_signal_connect(helper->assistant,"close",
   1.399 +      G_CALLBACK(plover_transaction_helper_assistant_close),helper);
   1.400 +    g_signal_connect(helper->assistant,"prepare",
   1.401 +      G_CALLBACK(plover_transaction_helper_assistant_prepare),helper);
   1.402 +    w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
   1.403 +    if (w)
   1.404 +	g_signal_connect(w,"toggled",
   1.405 +	  G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper);
   1.406 +    w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction"));
   1.407 +    if (w)
   1.408 +	gtk_assistant_set_page_complete(helper->assistant,w,TRUE);
   1.409 +    return helper;
   1.410 +}
   1.411 +
   1.412 +PloverPackageSet *
   1.413 +  plover_transaction_helper_get_installed(PloverTransactionHelper *helper)
   1.414 +{
   1.415 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.416 +    return helper->installed;
   1.417 +}
   1.418 +
   1.419 +void plover_transaction_helper_set_installed(PloverTransactionHelper *helper,
   1.420 +  PloverPackageSet *installed)
   1.421 +{
   1.422 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
   1.423 +    g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed));
   1.424 +    g_return_if_fail(helper->installed == NULL);
   1.425 +    helper->installed=g_object_ref(installed);
   1.426 +}
   1.427 +
   1.428 +PloverRepository *
   1.429 +  plover_transaction_helper_get_upstream(PloverTransactionHelper *helper,
   1.430 +  GError **error)
   1.431 +{
   1.432 +    const char *base;
   1.433 +#if 0
   1.434 +    const char *prefix;
   1.435 +    struct razor_relocations *relocations=NULL;
   1.436 +#endif
   1.437 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.438 +    if (!helper->upstream)
   1.439 +    {
   1.440 +#if 0
   1.441 +	prefix=plover_transaction_helper_get_prefix(helper,error);
   1.442 +	if (!prefix)
   1.443 +	    return NULL;
   1.444 +#endif
   1.445 +	base=plover_transaction_helper_get_base(helper);
   1.446 +#if 0
   1.447 +	if (prefix)
   1.448 +	{
   1.449 +	    relocations=razor_relocations_create();
   1.450 +	    razor_relocations_add(relocations,"/usr",prefix);
   1.451 +	}
   1.452 +#endif
   1.453 +	helper->upstream=plover_repository_new_from_yum(base,error);
   1.454 +#if 0
   1.455 +	if (relocations)
   1.456 +	    razor_relocations_destroy(relocations);
   1.457 +#endif
   1.458 +    }
   1.459 +    return helper->upstream;
   1.460 +}
   1.461 +
   1.462 +static PloverPackageSet *plover_transaction_helper_get_relocated_upstream(
   1.463 +  PloverTransactionHelper *helper,GError **error)
   1.464 +{
   1.465 +    const char *prefix;
   1.466 +    struct razor_relocations *relocations=NULL;
   1.467 +    GError *tmp_error=NULL;
   1.468 +    PloverRepository *upstream;
   1.469 +    PloverPackageSet *set;
   1.470 +    if (!helper->relocated_upstream)
   1.471 +    {
   1.472 +	upstream=plover_transaction_helper_get_upstream(helper,error);
   1.473 +	if (!upstream)
   1.474 +	    return NULL;
   1.475 +	prefix=plover_transaction_helper_get_prefix(helper,&tmp_error);
   1.476 +	if (tmp_error)
   1.477 +	{
   1.478 +	    g_propagate_error(error,tmp_error);
   1.479 +	    return NULL;
   1.480 +	}
   1.481 +	set=plover_repository_get_package_set(upstream);
   1.482 +	if (prefix)
   1.483 +	{
   1.484 +	    relocations=razor_relocations_create();
   1.485 +	    razor_relocations_add(relocations,"/usr",prefix);
   1.486 +	    helper->relocated_upstream=
   1.487 +	      plover_package_set_new_from_repository(upstream,relocations,
   1.488 +	      error);
   1.489 +	    if (relocations)
   1.490 +		razor_relocations_destroy(relocations);
   1.491 +	}
   1.492 +	else
   1.493 +	    helper->relocated_upstream=g_object_ref(set);
   1.494 +    }
   1.495 +    return helper->relocated_upstream;
   1.496 +}
   1.497 +
   1.498 +void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper,
   1.499 +  PloverRepository *upstream)
   1.500 +{
   1.501 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
   1.502 +    g_return_if_fail(PLOVER_IS_REPOSITORY(upstream));
   1.503 +    g_return_if_fail(helper->upstream == NULL);
   1.504 +    helper->upstream=g_object_ref(upstream);
   1.505 +}
   1.506 +
   1.507 +const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper)
   1.508 +{
   1.509 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.510 +    return helper->base;
   1.511 +}
   1.512 +
   1.513 +void plover_transaction_helper_set_base(PloverTransactionHelper *helper,
   1.514 +  const char *base)
   1.515 +{
   1.516 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
   1.517 +    g_return_if_fail(helper->transactions == NULL);
   1.518 +    g_free(helper->base);
   1.519 +    helper->base=g_strdup(base);
   1.520 +}
   1.521 +
   1.522 +struct comps *
   1.523 +  plover_transaction_helper_get_comps(PloverTransactionHelper *helper,
   1.524 +  GError **error)
   1.525 +{
   1.526 +    gchar *s;
   1.527 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.528 +    g_return_val_if_fail(helper->base != NULL,NULL);
   1.529 +    if (!helper->comps)
   1.530 +    {
   1.531 +	s=g_strconcat(helper->base,"/repodata/comps.xml",NULL);
   1.532 +	helper->comps=plover_comps_new_from_file(s);
   1.533 +	if (!helper->comps)
   1.534 +	    g_set_error(error,PLOVER_GENERAL_ERROR,
   1.535 +	      PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno));
   1.536 +	g_free(s);
   1.537 +    }
   1.538 +    return helper->comps;
   1.539 +}
   1.540 +
   1.541 +const char *
   1.542 +  plover_transaction_helper_get_prefix(PloverTransactionHelper *helper,
   1.543 +  GError **error)
   1.544 +{
   1.545 +    const char *prefix;
   1.546 +    struct comps *comps;
   1.547 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.548 +    g_return_val_if_fail(helper->base != NULL || helper->installed != NULL,NULL);
   1.549 +    if (helper->base)
   1.550 +    {
   1.551 +	comps=plover_transaction_helper_get_comps(helper,error);
   1.552 +	if (!comps)
   1.553 +	    return NULL;
   1.554 +	return plover_default_prefix_for_vendor(comps->vendor);
   1.555 +    }
   1.556 +    prefix=plover_package_set_guess_prefix(helper->installed,error);
   1.557 +    return prefix;
   1.558 +}
   1.559 +
   1.560 +static int plover_transaction_helper_package_count(void)
   1.561 +{
   1.562 +    int count=0;
   1.563 +    char *install_root;
   1.564 +    struct razor_set *set;
   1.565 +    struct razor_package *package;
   1.566 +    struct razor_package_iterator *pi;
   1.567 +    install_root=getenv("RAZOR_ROOT");
   1.568 +    if (!install_root)
   1.569 +	install_root="";
   1.570 +    set=razor_root_open_read_only(install_root,NULL);
   1.571 +    if (set)
   1.572 +    {
   1.573 +	pi=razor_package_iterator_create(set);
   1.574 +	while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST))
   1.575 +	    count++;
   1.576 +	razor_package_iterator_destroy(pi);
   1.577 +	razor_set_unref(set);
   1.578 +    }
   1.579 +    return count;
   1.580 +}
   1.581 +
   1.582 +static gboolean
   1.583 +  plover_transaction_helper_check_vendor(PloverTransactionHelper *helper,
   1.584 +  GError **error)
   1.585 +{
   1.586 +    int i;
   1.587 +    gchar *prefix=NULL,*s;
   1.588 +    struct comps *comps=NULL;
   1.589 +    GtkWidget *container,*page;
   1.590 +    GtkButton *button;
   1.591 +    GtkLabel *label;
   1.592 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.593 +    if (helper->check_vendor)
   1.594 +    {
   1.595 +	comps=plover_transaction_helper_get_comps(helper,error);
   1.596 +	if (!comps)
   1.597 +	    return FALSE;
   1.598 +	prefix=plover_default_prefix_for_vendor(comps->vendor);
   1.599 +    }
   1.600 +    button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting"));
   1.601 +    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE);
   1.602 +    container=GTK_WIDGET(gtk_builder_get_object(helper->ui,
   1.603 +      "SIIncompatibleInstallation"));
   1.604 +    page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"));
   1.605 +    if (helper->check_vendor && prefix &&
   1.606 +      !plover_installed_files_match_prefix(prefix))
   1.607 +    {
   1.608 +	label=GTK_LABEL(gtk_builder_get_object(helper->ui,
   1.609 +	  "SIIncompatibleInstallationLabel"));
   1.610 +	s=g_strdup_printf("<b>Incompatible Installation</b>\n\n"
   1.611 +	  "The existing installation is not from %s.\n"
   1.612 +	  "In order to continue, all the existing packages must be removed.",
   1.613 +	  comps->vendor);
   1.614 +	gtk_label_set_markup(label,s);
   1.615 +	g_free(s);
   1.616 +	i=plover_transaction_helper_package_count();
   1.617 +	s=g_strdup_printf("Remove %d existing package%s",i,i==1?"":"s");
   1.618 +	gtk_button_set_label(button,s);
   1.619 +	g_free(s);
   1.620 +	gtk_widget_show(container);
   1.621 +	if (helper->assistant)
   1.622 +	    gtk_assistant_set_page_complete(helper->assistant,page,FALSE);
   1.623 +    }
   1.624 +    else
   1.625 +    {
   1.626 +	gtk_widget_hide(container);
   1.627 +	if (helper->assistant)
   1.628 +	    gtk_assistant_set_page_complete(helper->assistant,page,TRUE);
   1.629 +    }
   1.630 +    return TRUE;
   1.631 +}
   1.632 +
   1.633 +void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper,
   1.634 +  gboolean check_vendor)
   1.635 +{
   1.636 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
   1.637 +    if (helper->check_vendor!=check_vendor)
   1.638 +    {
   1.639 +	helper->check_vendor=check_vendor;
   1.640 +	if (helper->transactions)
   1.641 +	    plover_transaction_helper_check_vendor(helper,NULL);
   1.642 +    }
   1.643 +}
   1.644 +
   1.645 +/*
   1.646 + * If plover_transaction_helper_add_transaction() failes with an error
   1.647 + * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET
   1.648 + * then plover_transaction_helper_get_unsatisfied() can be used to
   1.649 + * retrieve a textual description of the problem.
   1.650 + */
   1.651 +
   1.652 +const char *
   1.653 +  plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper)
   1.654 +{
   1.655 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.656 +    return helper->unsatisfied;
   1.657 +}
   1.658 +
   1.659 +gboolean
   1.660 +  plover_transaction_helper_add_transaction(PloverTransactionHelper *helper,
   1.661 +  PloverTransaction *transaction,struct plover_vector *report_packages,
   1.662 +  enum razor_install_action report_action,GError **error)
   1.663 +{
   1.664 +    int i,count;
   1.665 +    gboolean other_packages;
   1.666 +    const char *s,*name;
   1.667 +    enum razor_install_action action;
   1.668 +    struct razor_install_iterator *ii;
   1.669 +    struct razor_set *next;
   1.670 +    struct razor_package *package;
   1.671 +    struct plover_vector *tasked_packages;
   1.672 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.673 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE);
   1.674 +    g_return_val_if_fail(report_action==RAZOR_INSTALL_ACTION_ADD || report_action==RAZOR_INSTALL_ACTION_REMOVE,FALSE);
   1.675 +    g_free(helper->unsatisfied);
   1.676 +    helper->unsatisfied=NULL;
   1.677 +    if (!plover_transaction_resolve(transaction,error))
   1.678 +    {
   1.679 +	s=plover_transaction_get_unsatisfied(transaction);
   1.680 +	helper->unsatisfied=g_strdup(s);
   1.681 +	return FALSE;
   1.682 +    }
   1.683 +    ii=plover_transaction_get_install_iterator(transaction,error);
   1.684 +    if (!ii)
   1.685 +	return FALSE;
   1.686 +    next=plover_transaction_get_next_set(transaction,error);
   1.687 +    if (!next)
   1.688 +	return FALSE;
   1.689 +    tasked_packages=plover_vector_new();
   1.690 +    other_packages=FALSE;
   1.691 +    while (razor_install_iterator_next(ii,&package,&action,&count))
   1.692 +    {
   1.693 +	if (action==report_action)
   1.694 +	{
   1.695 +	    razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name,
   1.696 +	      RAZOR_DETAIL_LAST);
   1.697 +	    if (!report_packages ||
   1.698 +	      plover_vector_contains(report_packages,name))
   1.699 +		plover_vector_append(tasked_packages,name);
   1.700 +	    else
   1.701 +		other_packages=TRUE;
   1.702 +	}
   1.703 +    }
   1.704 +    if (!tasked_packages->len)
   1.705 +    {
   1.706 +	/*
   1.707 +	 * If there are no reportable packages tasked for action there
   1.708 +	 * shouldn't by any packages at all, but let's be paranoid.
   1.709 +	 */
   1.710 +	other_packages=FALSE;
   1.711 +	razor_install_iterator_rewind(ii);
   1.712 +	while (razor_install_iterator_next(ii,&package,&action,&count))
   1.713 +	{
   1.714 +	    if (action==report_action)
   1.715 +	    {
   1.716 +		razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name,
   1.717 +		  RAZOR_DETAIL_LAST);
   1.718 +		plover_vector_append(tasked_packages,name);
   1.719 +	    }
   1.720 +	}
   1.721 +    }
   1.722 +    if (!tasked_packages->len)
   1.723 +    {
   1.724 +	g_set_error(error,PLOVER_GENERAL_ERROR,
   1.725 +	  PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions",
   1.726 +	  report_action==RAZOR_INSTALL_ACTION_ADD?"add":"remove");
   1.727 +	plover_vector_free(tasked_packages);
   1.728 +	return FALSE;
   1.729 +    }
   1.730 +    if (!helper->transactions)
   1.731 +	plover_transaction_helper_check_vendor(helper,error);
   1.732 +    g_object_ref(transaction);
   1.733 +    helper->transactions=g_slist_append(helper->transactions,transaction);
   1.734 +    if (report_action==RAZOR_INSTALL_ACTION_ADD)
   1.735 +    {
   1.736 +	for(i=0;i<tasked_packages->len;i++)
   1.737 +	{
   1.738 +	    s=tasked_packages->strings[i];
   1.739 +	    if (!plover_vector_contains(helper->report_adding,s))
   1.740 +		plover_vector_append(helper->report_adding,s);
   1.741 +	}
   1.742 +	helper->report_adding_dependencies|=other_packages;
   1.743 +    }
   1.744 +    else
   1.745 +    {
   1.746 +	for(i=0;i<tasked_packages->len;i++)
   1.747 +	{
   1.748 +	    s=tasked_packages->strings[i];
   1.749 +	    if (!plover_vector_contains(helper->report_removing,s))
   1.750 +		plover_vector_append(helper->report_removing,s);
   1.751 +	}
   1.752 +	helper->report_removing_dependants|=other_packages;
   1.753 +    }
   1.754 +    plover_vector_free(tasked_packages);
   1.755 +    return TRUE;
   1.756 +}
   1.757 +
   1.758 +static PloverTransaction *
   1.759 +  plover_transaction_helper_new_transaction(PloverTransactionHelper *helper,
   1.760 +  GError **error)
   1.761 +{
   1.762 +    gboolean ok;
   1.763 +    const char *base,*prefix;
   1.764 +    GError *tmp_error=NULL;
   1.765 +    PloverTransaction *transaction;
   1.766 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL);
   1.767 +    prefix=plover_transaction_helper_get_prefix(helper,&tmp_error);
   1.768 +    if (tmp_error)
   1.769 +    {
   1.770 +	g_propagate_error(error,tmp_error);
   1.771 +	return NULL;
   1.772 +    }
   1.773 +    transaction=plover_transaction_new();
   1.774 +    plover_transaction_set_prefix(transaction,prefix);
   1.775 +    plover_transaction_set_installed(transaction,helper->installed);
   1.776 +    if (helper->upstream)
   1.777 +	ok=plover_transaction_set_upstream(transaction,helper->upstream,error);
   1.778 +    else
   1.779 +    {
   1.780 +	base=plover_transaction_helper_get_base(helper);
   1.781 +	ok=plover_transaction_set_upstream_from_yum(transaction,base,error);
   1.782 +    }
   1.783 +    if (!ok)
   1.784 +    {
   1.785 +	g_object_unref(transaction);
   1.786 +	transaction=NULL;
   1.787 +    }
   1.788 +    return transaction;
   1.789 +}
   1.790 +
   1.791 +struct plover_vector *plover_transaction_helper_group_get_default_packages(
   1.792 +  PloverTransactionHelper *helper,const char *group,GError **error)
   1.793 +{
   1.794 +    gboolean changed;
   1.795 +    struct comps *comps;
   1.796 +    struct comps_group *grp;
   1.797 +    struct comps_requirement *pkg;
   1.798 +    struct plover_vector *default_packages;
   1.799 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.800 +    comps=plover_transaction_helper_get_comps(helper,error);
   1.801 +    if (!comps)
   1.802 +	return NULL;
   1.803 +    grp=plover_comps_lookup_group(comps,group);
   1.804 +    if (!grp)
   1.805 +    {
   1.806 +	g_set_error(error,PLOVER_GENERAL_ERROR,
   1.807 +	  PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group);
   1.808 +	return NULL;
   1.809 +    }
   1.810 +    default_packages=plover_vector_new();
   1.811 +    do
   1.812 +    {
   1.813 +	changed=FALSE;
   1.814 +	for(pkg=grp->packages;pkg;pkg=pkg->next)
   1.815 +	{
   1.816 +	    if (plover_vector_contains(default_packages,pkg->name))
   1.817 +		continue;
   1.818 +	    if (pkg->type==COMPS_REQUIREMENT_DEFAULT ||
   1.819 +	      pkg->type==COMPS_REQUIREMENT_MANDATORY ||
   1.820 +	      pkg->type==COMPS_REQUIREMENT_CONDITIONAL &&
   1.821 +	      plover_vector_contains(default_packages,pkg->requires))
   1.822 +	    {
   1.823 +		changed=TRUE;
   1.824 +		plover_vector_append(default_packages,pkg->name);
   1.825 +	    }
   1.826 +	}
   1.827 +    } while(changed);
   1.828 +    return default_packages;
   1.829 +}
   1.830 +
   1.831 +/*
   1.832 + * Returns TRUE if there is work to be done or FALSE if the packages are
   1.833 + * already installed or on error.
   1.834 + */
   1.835 +gboolean
   1.836 +  plover_transaction_helper_install_packages(PloverTransactionHelper *helper,
   1.837 +  struct plover_vector *packages,GError **error)
   1.838 +{
   1.839 +    gboolean retval;
   1.840 +    PloverTransaction *transaction;
   1.841 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.842 +    g_return_val_if_fail(packages != NULL,FALSE);
   1.843 +    if (!packages->len)
   1.844 +    {
   1.845 +	g_set_error(error,PLOVER_GENERAL_ERROR,
   1.846 +	  PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed");
   1.847 +	return FALSE;
   1.848 +    }
   1.849 +    transaction=plover_transaction_helper_new_transaction(helper,error);
   1.850 +    if (!transaction)
   1.851 +	return FALSE;
   1.852 +    if (!plover_transaction_install(transaction,packages->strings,error))
   1.853 +    {
   1.854 +	g_object_unref(transaction);
   1.855 +	return FALSE;
   1.856 +    }
   1.857 +    retval=plover_transaction_helper_add_transaction(helper,transaction,
   1.858 +      packages,RAZOR_INSTALL_ACTION_ADD,error);
   1.859 +    g_object_unref(transaction);
   1.860 +    return retval;
   1.861 +}
   1.862 +
   1.863 +/*
   1.864 + * Returns TRUE if there is work to be done or FALSE if the group is
   1.865 + * already installed or on error.
   1.866 + */
   1.867 +gboolean
   1.868 +  plover_transaction_helper_install_group(PloverTransactionHelper *helper,
   1.869 +  const char *group,GError **error)
   1.870 +{
   1.871 +    gboolean retval;
   1.872 +    struct plover_vector *selected_packages;
   1.873 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.874 +    selected_packages=plover_transaction_helper_group_get_default_packages(
   1.875 +      helper,group,error);
   1.876 +    if (!selected_packages)
   1.877 +	return FALSE;
   1.878 +    if (!selected_packages->len)
   1.879 +    {
   1.880 +	g_set_error(error,PLOVER_GENERAL_ERROR,
   1.881 +	  PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
   1.882 +	plover_vector_free(selected_packages);
   1.883 +	return FALSE;
   1.884 +    }
   1.885 +    retval=plover_transaction_helper_install_packages(helper,selected_packages,
   1.886 +      error);
   1.887 +    plover_vector_free(selected_packages);
   1.888 +    return retval;
   1.889 +}
   1.890 +
   1.891 +/*
   1.892 + * Returns TRUE if there is work to be done or FALSE if the group is
   1.893 + * not installed or on error.
   1.894 + */
   1.895 +gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper,
   1.896 +  const char *group,GError **error)
   1.897 +{
   1.898 +    gboolean retval;
   1.899 +    struct plover_vector *selected_packages;
   1.900 +    PloverTransaction *transaction;
   1.901 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.902 +    selected_packages=plover_transaction_helper_group_get_default_packages(
   1.903 +      helper,group,error);
   1.904 +    if (!selected_packages)
   1.905 +	return FALSE;
   1.906 +    if (!selected_packages->len)
   1.907 +    {
   1.908 +	g_set_error(error,PLOVER_GENERAL_ERROR,
   1.909 +	  PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group);
   1.910 +	plover_vector_free(selected_packages);
   1.911 +	return FALSE;
   1.912 +    }
   1.913 +    transaction=plover_transaction_new();
   1.914 +    plover_transaction_set_installed(transaction,helper->installed);
   1.915 +    if (!plover_transaction_remove(transaction,selected_packages->strings,
   1.916 +      error))
   1.917 +    {
   1.918 +	plover_vector_free(selected_packages);
   1.919 +	g_object_unref(transaction);
   1.920 +	return FALSE;
   1.921 +    }
   1.922 +    retval=plover_transaction_helper_add_transaction(helper,transaction,
   1.923 +      NULL,RAZOR_INSTALL_ACTION_REMOVE,error);
   1.924 +    g_object_unref(transaction);
   1.925 +    plover_vector_free(selected_packages);
   1.926 +    return retval;
   1.927 +}
   1.928 +
   1.929 +/*
   1.930 + * Returns TRUE if there is work to be done or FALSE if all updates have
   1.931 + * already been applied or on error.
   1.932 + */
   1.933 +gboolean plover_transaction_helper_update(PloverTransactionHelper *helper,
   1.934 +  GError **error)
   1.935 +{
   1.936 +    gboolean retval;
   1.937 +    PloverTransaction *transaction;
   1.938 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.939 +    transaction=plover_transaction_helper_new_transaction(helper,error);
   1.940 +    if (!transaction)
   1.941 +	return FALSE;
   1.942 +    if (!plover_transaction_update(transaction,NULL,error))
   1.943 +    {
   1.944 +	g_object_unref(transaction);
   1.945 +	return FALSE;
   1.946 +    }
   1.947 +    retval=plover_transaction_helper_add_transaction(helper,transaction,
   1.948 +      NULL,RAZOR_INSTALL_ACTION_ADD,error);
   1.949 +    g_object_unref(transaction);
   1.950 +    return retval;
   1.951 +}
   1.952 +
   1.953 +gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper)
   1.954 +{
   1.955 +    g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE);
   1.956 +    if (helper->error_dialog)
   1.957 +	return TRUE;
   1.958 +    else if (!helper->assistant)
   1.959 +	return FALSE;
   1.960 +    else
   1.961 +	return gtk_widget_get_visible(GTK_WIDGET(helper->assistant));
   1.962 +}
   1.963 +
   1.964 +void plover_transaction_helper_present(PloverTransactionHelper *helper)
   1.965 +{
   1.966 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
   1.967 +    if (helper->error_dialog)
   1.968 +	gtk_window_present(GTK_WINDOW(helper->error_dialog));
   1.969 +    else if (helper->assistant)
   1.970 +	gtk_window_present(GTK_WINDOW(helper->assistant));
   1.971 +}
   1.972 +
   1.973 +static void
   1.974 +  plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog,
   1.975 +  int response_id,PloverTransactionHelper *helper)
   1.976 +{
   1.977 +    g_signal_handlers_disconnect_by_data(error_dialog,helper);
   1.978 +    if ((GtkWidget *)error_dialog==helper->error_dialog)
   1.979 +    {
   1.980 +	gtk_widget_destroy(helper->error_dialog);
   1.981 +	helper->error_dialog=NULL;
   1.982 +	if (helper->assistant)
   1.983 +	{
   1.984 +	    gtk_widget_hide(GTK_WIDGET(helper->assistant));
   1.985 +	    gtk_assistant_set_current_page(helper->assistant,0);
   1.986 +	}
   1.987 +	g_signal_emit(helper,signals[CLOSE],0);
   1.988 +    }
   1.989 +}
   1.990 +
   1.991 +const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper,
   1.992 +  const GError **error)
   1.993 +{
   1.994 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
   1.995 +    if (!helper->error_dialog)
   1.996 +	return NULL;
   1.997 +    if (error)
   1.998 +	*error=helper->error;
   1.999 +    return helper->error_primary_text;
  1.1000 +}
  1.1001 +
  1.1002 +void plover_transaction_helper_set_error(PloverTransactionHelper *helper,
  1.1003 +  const GError *error,const char *primary_text)
  1.1004 +{
  1.1005 +    GtkMessageType type;
  1.1006 +    GtkWindow *window;
  1.1007 +    g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper));
  1.1008 +    g_return_if_fail(error != NULL);
  1.1009 +    g_return_if_fail(primary_text != NULL);
  1.1010 +    if (helper->pulse_handler)
  1.1011 +    {
  1.1012 +	g_source_remove(helper->pulse_handler);
  1.1013 +	helper->pulse_handler=0;
  1.1014 +    }
  1.1015 +    if (helper->error_dialog)
  1.1016 +    {
  1.1017 +	gtk_widget_destroy(helper->error_dialog);
  1.1018 +	helper->error_dialog=NULL;
  1.1019 +    }
  1.1020 +    g_free(helper->error_primary_text);
  1.1021 +    helper->error_primary_text=g_strdup(primary_text);
  1.1022 +    g_clear_error(&helper->error);
  1.1023 +    helper->error=g_error_copy(error);
  1.1024 +    if (g_error_matches(error,PLOVER_GENERAL_ERROR,
  1.1025 +      PLOVER_GENERAL_ERROR_NO_WORK))
  1.1026 +	type=GTK_MESSAGE_INFO;
  1.1027 +    else
  1.1028 +	type=GTK_MESSAGE_ERROR;
  1.1029 +    if (helper->assistant)
  1.1030 +	window=GTK_WINDOW(helper->assistant);
  1.1031 +    else
  1.1032 +    	window=NULL;
  1.1033 +    helper->error_dialog=gtk_message_dialog_new(window,
  1.1034 +      GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text);
  1.1035 +    gtk_message_dialog_format_secondary_text(
  1.1036 +      GTK_MESSAGE_DIALOG(helper->error_dialog),error->message);
  1.1037 +    gtk_widget_show(helper->error_dialog);
  1.1038 +    g_signal_connect(helper->error_dialog,"response",
  1.1039 +      G_CALLBACK(plover_transaction_helper_error_dialog_response),helper);
  1.1040 +}