diff -r 000000000000 -r 066e557ac453 plover-gtk/transactionhelper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plover-gtk/transactionhelper.c Fri Apr 15 13:05:48 2016 +0100 @@ -0,0 +1,1037 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include + +/* + * A PloverTransactionHelper uses a GtkAssistant to help a user run a + * transaction. + */ + +G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT) + +enum { + CLOSE=0, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +static void plover_transaction_helper_finalize(PloverTransactionHelper *helper) +{ + g_free(helper->error_primary_text); + g_free(helper->base); + g_free(helper->unsatisfied); + if (helper->comps) + plover_comps_free(helper->comps); + plover_vector_free(helper->report_adding); + plover_vector_free(helper->report_removing); +} + +static void plover_transaction_helper_dispose(PloverTransactionHelper *helper) +{ + g_clear_error(&helper->error); + if (helper->error_dialog) + { + g_signal_handlers_disconnect_by_data(helper->error_dialog,helper); + gtk_widget_destroy(helper->error_dialog); + helper->error_dialog=NULL; + } + if (helper->assistant) + { + g_signal_handlers_disconnect_by_data(helper->assistant,helper); + g_clear_object(&helper->assistant); + } + g_clear_object(&helper->ui); + g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL); + g_slist_free(helper->transactions); + helper->transactions=NULL; + g_clear_object(&helper->installed); + g_clear_object(&helper->upstream); + g_clear_object(&helper->relocated_upstream); +} + +static void + plover_transaction_helper_class_init(PloverTransactionHelperClass *klass) +{ + GObjectClass *gobject_class=G_OBJECT_CLASS(klass); + gobject_class->finalize= + (void (*)(GObject *))plover_transaction_helper_finalize; + gobject_class->dispose= + (void (*)(GObject *))plover_transaction_helper_dispose; + signals[CLOSE]=g_signal_newv("close", + G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, + g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); +} + +static void plover_transaction_helper_init(PloverTransactionHelper *helper) +{ + helper->report_adding=plover_vector_new(); + helper->report_removing=plover_vector_new(); +} + +static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant, + PloverTransactionHelper *helper) +{ + gtk_widget_hide(GTK_WIDGET(helper->assistant)); + gtk_assistant_set_current_page(helper->assistant,0); + g_signal_emit(helper,signals[CLOSE],0); +} + +static void plover_transaction_helper_assistant_close(GtkAssistant *assistant, + PloverTransactionHelper *helper) +{ + gtk_widget_hide(GTK_WIDGET(helper->assistant)); + gtk_assistant_set_current_page(helper->assistant,0); + g_signal_emit(helper,signals[CLOSE],0); +} + +static void + plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper) +{ + gchar *package_list,*add,*remove,*s; + GtkLabel *label; + struct plover_vector *report; + if (helper->report_adding->len) + { + plover_vector_sort(helper->report_adding); + if (helper->report_adding_dependencies) + { + report=plover_vector_dup(helper->report_adding); + if (helper->report_adding->len==1) + plover_vector_append(report,"its dependencies"); + else + plover_vector_append(report,"their dependencies"); + package_list=plover_vector_format_for_display(report); + plover_vector_free(report); + } + else + package_list= + plover_vector_format_for_display(helper->report_adding); + add=g_strconcat("Packages to be installed or updated: ",package_list, + ".",NULL); + g_free(package_list); + } + else + add=NULL; + if (helper->report_removing->len) + { + plover_vector_sort(helper->report_removing); + if (helper->report_removing_dependants) + { + report=plover_vector_dup(helper->report_removing); + if (helper->report_adding->len==1) + plover_vector_append(report,"its dependants"); + else + plover_vector_append(report,"their dependants"); + package_list=plover_vector_format_for_display(report); + plover_vector_free(report); + } + else + package_list= + plover_vector_format_for_display(helper->report_removing); + remove=g_strconcat("Packages to be removed: ",package_list,".",NULL); + g_free(package_list); + } + else + remove=NULL; + label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork")); + if (add && remove) + s=g_strconcat("Installation Summary\n\n",remove,"\n\n",add,NULL); + else if (add || remove) + s=g_strconcat("Installation Summary\n\n",add?add:remove,NULL); + else + s=g_strdup("Installation Summary\n\nNo changes scheduled"); + gtk_label_set_markup(label,s); + g_free(s); + g_free(add); + g_free(remove); +} + +static void plover_transaction_helper_run(PloverTransactionHelper *helper); + +static void plover_transaction_helper_callback(GObject *source, + GAsyncResult *result,gpointer user_data) +{ + GError *error=NULL; + PloverTransactionHelper *helper=user_data; + PloverTransaction *transaction=PLOVER_TRANSACTION(source); + if (!plover_transaction_commit_finish(transaction,result,&error)) + { + plover_transaction_helper_set_error(helper,error, + "Software installation failed"); + g_error_free(error); + } + else + plover_transaction_helper_run(helper); + g_signal_handlers_disconnect_by_data(transaction,helper); + g_object_unref(transaction); +} + +static void plover_transaction_helper_transaction_status_changed( + PloverTransaction *transaction,const char *status, + PloverTransactionHelper *helper) +{ + GtkProgressBar *bar; + bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); + gtk_progress_bar_set_text(bar,status); +} + +static void plover_transaction_helper_run(PloverTransactionHelper *helper) +{ + PloverTransaction *transaction; + GtkWidget *page; + page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); + if (helper->transactions) + { + if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,FALSE); + transaction=helper->transactions->data; + helper->transactions=g_slist_delete_link(helper->transactions, + helper->transactions); + g_signal_connect(transaction,"status-changed", + G_CALLBACK(plover_transaction_helper_transaction_status_changed), + helper); + plover_transaction_commit_async(transaction,NULL, + plover_transaction_helper_callback,helper); + } + else if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,TRUE); +} + +static gboolean plover_transaction_helper_pulse(gpointer user_data) +{ + PloverTransactionHelper *helper=user_data; + GtkWidget *w; + GtkProgressBar *bar; + if (!helper->assistant) + return FALSE; + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); + bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); + if (gtk_assistant_get_page_complete(helper->assistant,w)) + { + gtk_progress_bar_set_fraction(bar,1.0); + helper->pulse_handler=0; + return FALSE; + } + else + { + gtk_progress_bar_pulse(bar); + return TRUE; + } +} + +static void + plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper) +{ + GError *error=NULL; + GtkToggleButton *button; + PloverTransaction *transaction; + button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui, + "SIRemoveExisting")); + if (gtk_toggle_button_get_active(button)) + { + transaction=plover_transaction_new_remove(NULL,&error); + if (!transaction) + { + if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT)) + g_clear_error(&error); + if (error) + { + plover_transaction_helper_set_error(helper,error, + "Software installation failed"); + g_error_free(error); + return; + } + } + else + helper->transactions= + g_slist_prepend(helper->transactions,transaction); + } + /* + * Note that PloverTransaction does support cancelling a transaction, but + * there are a number of challenges with using it: + * - cancellation is only supported during the file phase if razor + * has atomic rollback, + * - cancellation is not supported during post-transaction scripts at all + * (since by the time the first script is started the atomic has already + * been committed) and these can take quite some time, + * - where a transaction has an embedded COMMIT, any rollback won't + * go back beyond this point. + * To support user-cancel, then, we would need some mechanism to: + * - Comunicate that the operation is being cancelled and this may take + * some time, + * - Not allow cancellation at all after the last post-transaction script + * phase is started, + * - Report the partially completed transaction where cancellation + * occurred after a COMMIT point. + * At present, this doesn't appear worth the effort. + */ + if (helper->assistant) + gtk_assistant_commit(helper->assistant); + plover_transaction_helper_run(helper); + helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse, + helper); +} + +static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant, + GtkWidget *page,PloverTransactionHelper *helper) +{ + if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"))) + plover_transaction_helper_prepare_confirm(helper); + else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"))) + plover_transaction_helper_prepare_progress(helper); +} + +static void + plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button, + PloverTransactionHelper *helper) +{ + GtkWidget *w; + if (helper->assistant) + { + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); + gtk_assistant_set_page_complete(helper->assistant,w, + gtk_toggle_button_get_active(button)); + } +} + +PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui) +{ + gsize len; + gchar *s,*directory,*contents; + GError *error=NULL; + GtkWidget *w; + PloverTransactionHelper *helper; + g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL); + helper=PLOVER_TRANSACTION_HELPER( + g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL)); + if (ui) + helper->ui=g_object_ref(ui); + else + helper->ui=gtk_builder_new(); + helper->assistant= + GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation")); + if (!helper->assistant) + { + directory=g_strdup(g_getenv("PLOVER_DATADIR")); + if (!directory) + { +#ifdef WIN32 + s=g_win32_get_package_installation_directory_of_module(NULL); + directory=g_build_filename(s,"share","plover",NULL); + g_free(s); +#else + directory=g_strdup(PLOVER_DATADIR); +#endif + } + s=g_build_filename(directory,"software-installation.ui",NULL); + g_free(directory); + (void)g_file_get_contents(s,&contents,&len,&error); + g_free(s); + if (!error) + { + (void)gtk_builder_add_from_string(helper->ui,contents,len,&error); + g_free(contents); + } + if (error) + { + g_critical("software-installation.ui: %s",error->message); + g_clear_error(&error); + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error (no user interface)"); + plover_transaction_helper_set_error(helper,error, + "Can't start installer"); + return helper; + } + helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui, + "SoftwareInstallation")); + } + if (!helper->assistant) + { + g_critical("\"SoftwareInstallation\" object not found"); + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error (missing wizard)"); + plover_transaction_helper_set_error(helper,error, + "Can't start installer"); + g_error_free(error); + return helper; + } + else + g_object_ref(helper->assistant); + if (!GTK_IS_ASSISTANT(helper->assistant)) + { + g_critical("\"SoftwareInstallation\" is not a GtkAssistant"); + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error (unexpected wizard type)"); + plover_transaction_helper_set_error(helper,error, + "Can't start installer"); + g_error_free(error); + return helper; + } + g_signal_connect(helper->assistant,"cancel", + G_CALLBACK(plover_transaction_helper_assistant_cancel),helper); + g_signal_connect(helper->assistant,"close", + G_CALLBACK(plover_transaction_helper_assistant_close),helper); + g_signal_connect(helper->assistant,"prepare", + G_CALLBACK(plover_transaction_helper_assistant_prepare),helper); + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); + if (w) + g_signal_connect(w,"toggled", + G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper); + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction")); + if (w) + gtk_assistant_set_page_complete(helper->assistant,w,TRUE); + return helper; +} + +PloverPackageSet * + plover_transaction_helper_get_installed(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + return helper->installed; +} + +void plover_transaction_helper_set_installed(PloverTransactionHelper *helper, + PloverPackageSet *installed) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed)); + g_return_if_fail(helper->installed == NULL); + helper->installed=g_object_ref(installed); +} + +PloverRepository * + plover_transaction_helper_get_upstream(PloverTransactionHelper *helper, + GError **error) +{ + const char *base; +#if 0 + const char *prefix; + struct razor_relocations *relocations=NULL; +#endif + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + if (!helper->upstream) + { +#if 0 + prefix=plover_transaction_helper_get_prefix(helper,error); + if (!prefix) + return NULL; +#endif + base=plover_transaction_helper_get_base(helper); +#if 0 + if (prefix) + { + relocations=razor_relocations_create(); + razor_relocations_add(relocations,"/usr",prefix); + } +#endif + helper->upstream=plover_repository_new_from_yum(base,error); +#if 0 + if (relocations) + razor_relocations_destroy(relocations); +#endif + } + return helper->upstream; +} + +static PloverPackageSet *plover_transaction_helper_get_relocated_upstream( + PloverTransactionHelper *helper,GError **error) +{ + const char *prefix; + struct razor_relocations *relocations=NULL; + GError *tmp_error=NULL; + PloverRepository *upstream; + PloverPackageSet *set; + if (!helper->relocated_upstream) + { + upstream=plover_transaction_helper_get_upstream(helper,error); + if (!upstream) + return NULL; + prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); + if (tmp_error) + { + g_propagate_error(error,tmp_error); + return NULL; + } + set=plover_repository_get_package_set(upstream); + if (prefix) + { + relocations=razor_relocations_create(); + razor_relocations_add(relocations,"/usr",prefix); + helper->relocated_upstream= + plover_package_set_new_from_repository(upstream,relocations, + error); + if (relocations) + razor_relocations_destroy(relocations); + } + else + helper->relocated_upstream=g_object_ref(set); + } + return helper->relocated_upstream; +} + +void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper, + PloverRepository *upstream) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(PLOVER_IS_REPOSITORY(upstream)); + g_return_if_fail(helper->upstream == NULL); + helper->upstream=g_object_ref(upstream); +} + +const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + return helper->base; +} + +void plover_transaction_helper_set_base(PloverTransactionHelper *helper, + const char *base) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(helper->transactions == NULL); + g_free(helper->base); + helper->base=g_strdup(base); +} + +struct comps * + plover_transaction_helper_get_comps(PloverTransactionHelper *helper, + GError **error) +{ + gchar *s; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + g_return_val_if_fail(helper->base != NULL,NULL); + if (!helper->comps) + { + s=g_strconcat(helper->base,"/repodata/comps.xml",NULL); + helper->comps=plover_comps_new_from_file(s); + if (!helper->comps) + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno)); + g_free(s); + } + return helper->comps; +} + +const char * + plover_transaction_helper_get_prefix(PloverTransactionHelper *helper, + GError **error) +{ + const char *prefix; + struct comps *comps; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + g_return_val_if_fail(helper->base != NULL || helper->installed != NULL,NULL); + if (helper->base) + { + comps=plover_transaction_helper_get_comps(helper,error); + if (!comps) + return NULL; + return plover_default_prefix_for_vendor(comps->vendor); + } + prefix=plover_package_set_guess_prefix(helper->installed,error); + return prefix; +} + +static int plover_transaction_helper_package_count(void) +{ + int count=0; + char *install_root; + struct razor_set *set; + struct razor_package *package; + struct razor_package_iterator *pi; + install_root=getenv("RAZOR_ROOT"); + if (!install_root) + install_root=""; + set=razor_root_open_read_only(install_root,NULL); + if (set) + { + pi=razor_package_iterator_create(set); + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST)) + count++; + razor_package_iterator_destroy(pi); + razor_set_unref(set); + } + return count; +} + +static gboolean + plover_transaction_helper_check_vendor(PloverTransactionHelper *helper, + GError **error) +{ + int i; + gchar *prefix=NULL,*s; + struct comps *comps=NULL; + GtkWidget *container,*page; + GtkButton *button; + GtkLabel *label; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + if (helper->check_vendor) + { + comps=plover_transaction_helper_get_comps(helper,error); + if (!comps) + return FALSE; + prefix=plover_default_prefix_for_vendor(comps->vendor); + } + button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE); + container=GTK_WIDGET(gtk_builder_get_object(helper->ui, + "SIIncompatibleInstallation")); + page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); + if (helper->check_vendor && prefix && + !plover_installed_files_match_prefix(prefix)) + { + label=GTK_LABEL(gtk_builder_get_object(helper->ui, + "SIIncompatibleInstallationLabel")); + s=g_strdup_printf("Incompatible Installation\n\n" + "The existing installation is not from %s.\n" + "In order to continue, all the existing packages must be removed.", + comps->vendor); + gtk_label_set_markup(label,s); + g_free(s); + i=plover_transaction_helper_package_count(); + s=g_strdup_printf("Remove %d existing package%s",i,i==1?"":"s"); + gtk_button_set_label(button,s); + g_free(s); + gtk_widget_show(container); + if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,FALSE); + } + else + { + gtk_widget_hide(container); + if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,TRUE); + } + return TRUE; +} + +void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper, + gboolean check_vendor) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + if (helper->check_vendor!=check_vendor) + { + helper->check_vendor=check_vendor; + if (helper->transactions) + plover_transaction_helper_check_vendor(helper,NULL); + } +} + +/* + * If plover_transaction_helper_add_transaction() failes with an error + * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET + * then plover_transaction_helper_get_unsatisfied() can be used to + * retrieve a textual description of the problem. + */ + +const char * + plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + return helper->unsatisfied; +} + +gboolean + plover_transaction_helper_add_transaction(PloverTransactionHelper *helper, + PloverTransaction *transaction,struct plover_vector *report_packages, + enum razor_install_action report_action,GError **error) +{ + int i,count; + gboolean other_packages; + const char *s,*name; + enum razor_install_action action; + struct razor_install_iterator *ii; + struct razor_set *next; + struct razor_package *package; + struct plover_vector *tasked_packages; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(report_action==RAZOR_INSTALL_ACTION_ADD || report_action==RAZOR_INSTALL_ACTION_REMOVE,FALSE); + g_free(helper->unsatisfied); + helper->unsatisfied=NULL; + if (!plover_transaction_resolve(transaction,error)) + { + s=plover_transaction_get_unsatisfied(transaction); + helper->unsatisfied=g_strdup(s); + return FALSE; + } + ii=plover_transaction_get_install_iterator(transaction,error); + if (!ii) + return FALSE; + next=plover_transaction_get_next_set(transaction,error); + if (!next) + return FALSE; + tasked_packages=plover_vector_new(); + other_packages=FALSE; + while (razor_install_iterator_next(ii,&package,&action,&count)) + { + if (action==report_action) + { + razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST); + if (!report_packages || + plover_vector_contains(report_packages,name)) + plover_vector_append(tasked_packages,name); + else + other_packages=TRUE; + } + } + if (!tasked_packages->len) + { + /* + * If there are no reportable packages tasked for action there + * shouldn't by any packages at all, but let's be paranoid. + */ + other_packages=FALSE; + razor_install_iterator_rewind(ii); + while (razor_install_iterator_next(ii,&package,&action,&count)) + { + if (action==report_action) + { + razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST); + plover_vector_append(tasked_packages,name); + } + } + } + if (!tasked_packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions", + report_action==RAZOR_INSTALL_ACTION_ADD?"add":"remove"); + plover_vector_free(tasked_packages); + return FALSE; + } + if (!helper->transactions) + plover_transaction_helper_check_vendor(helper,error); + g_object_ref(transaction); + helper->transactions=g_slist_append(helper->transactions,transaction); + if (report_action==RAZOR_INSTALL_ACTION_ADD) + { + for(i=0;ilen;i++) + { + s=tasked_packages->strings[i]; + if (!plover_vector_contains(helper->report_adding,s)) + plover_vector_append(helper->report_adding,s); + } + helper->report_adding_dependencies|=other_packages; + } + else + { + for(i=0;ilen;i++) + { + s=tasked_packages->strings[i]; + if (!plover_vector_contains(helper->report_removing,s)) + plover_vector_append(helper->report_removing,s); + } + helper->report_removing_dependants|=other_packages; + } + plover_vector_free(tasked_packages); + return TRUE; +} + +static PloverTransaction * + plover_transaction_helper_new_transaction(PloverTransactionHelper *helper, + GError **error) +{ + gboolean ok; + const char *base,*prefix; + GError *tmp_error=NULL; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); + if (tmp_error) + { + g_propagate_error(error,tmp_error); + return NULL; + } + transaction=plover_transaction_new(); + plover_transaction_set_prefix(transaction,prefix); + plover_transaction_set_installed(transaction,helper->installed); + if (helper->upstream) + ok=plover_transaction_set_upstream(transaction,helper->upstream,error); + else + { + base=plover_transaction_helper_get_base(helper); + ok=plover_transaction_set_upstream_from_yum(transaction,base,error); + } + if (!ok) + { + g_object_unref(transaction); + transaction=NULL; + } + return transaction; +} + +struct plover_vector *plover_transaction_helper_group_get_default_packages( + PloverTransactionHelper *helper,const char *group,GError **error) +{ + gboolean changed; + struct comps *comps; + struct comps_group *grp; + struct comps_requirement *pkg; + struct plover_vector *default_packages; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + comps=plover_transaction_helper_get_comps(helper,error); + if (!comps) + return NULL; + grp=plover_comps_lookup_group(comps,group); + if (!grp) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group); + return NULL; + } + default_packages=plover_vector_new(); + do + { + changed=FALSE; + for(pkg=grp->packages;pkg;pkg=pkg->next) + { + if (plover_vector_contains(default_packages,pkg->name)) + continue; + if (pkg->type==COMPS_REQUIREMENT_DEFAULT || + pkg->type==COMPS_REQUIREMENT_MANDATORY || + pkg->type==COMPS_REQUIREMENT_CONDITIONAL && + plover_vector_contains(default_packages,pkg->requires)) + { + changed=TRUE; + plover_vector_append(default_packages,pkg->name); + } + } + } while(changed); + return default_packages; +} + +/* + * Returns TRUE if there is work to be done or FALSE if the packages are + * already installed or on error. + */ +gboolean + plover_transaction_helper_install_packages(PloverTransactionHelper *helper, + struct plover_vector *packages,GError **error) +{ + gboolean retval; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + g_return_val_if_fail(packages != NULL,FALSE); + if (!packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed"); + return FALSE; + } + transaction=plover_transaction_helper_new_transaction(helper,error); + if (!transaction) + return FALSE; + if (!plover_transaction_install(transaction,packages->strings,error)) + { + g_object_unref(transaction); + return FALSE; + } + retval=plover_transaction_helper_add_transaction(helper,transaction, + packages,RAZOR_INSTALL_ACTION_ADD,error); + g_object_unref(transaction); + return retval; +} + +/* + * Returns TRUE if there is work to be done or FALSE if the group is + * already installed or on error. + */ +gboolean + plover_transaction_helper_install_group(PloverTransactionHelper *helper, + const char *group,GError **error) +{ + gboolean retval; + struct plover_vector *selected_packages; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + selected_packages=plover_transaction_helper_group_get_default_packages( + helper,group,error); + if (!selected_packages) + return FALSE; + if (!selected_packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); + plover_vector_free(selected_packages); + return FALSE; + } + retval=plover_transaction_helper_install_packages(helper,selected_packages, + error); + plover_vector_free(selected_packages); + return retval; +} + +/* + * Returns TRUE if there is work to be done or FALSE if the group is + * not installed or on error. + */ +gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper, + const char *group,GError **error) +{ + gboolean retval; + struct plover_vector *selected_packages; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + selected_packages=plover_transaction_helper_group_get_default_packages( + helper,group,error); + if (!selected_packages) + return FALSE; + if (!selected_packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); + plover_vector_free(selected_packages); + return FALSE; + } + transaction=plover_transaction_new(); + plover_transaction_set_installed(transaction,helper->installed); + if (!plover_transaction_remove(transaction,selected_packages->strings, + error)) + { + plover_vector_free(selected_packages); + g_object_unref(transaction); + return FALSE; + } + retval=plover_transaction_helper_add_transaction(helper,transaction, + NULL,RAZOR_INSTALL_ACTION_REMOVE,error); + g_object_unref(transaction); + plover_vector_free(selected_packages); + return retval; +} + +/* + * Returns TRUE if there is work to be done or FALSE if all updates have + * already been applied or on error. + */ +gboolean plover_transaction_helper_update(PloverTransactionHelper *helper, + GError **error) +{ + gboolean retval; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + transaction=plover_transaction_helper_new_transaction(helper,error); + if (!transaction) + return FALSE; + if (!plover_transaction_update(transaction,NULL,error)) + { + g_object_unref(transaction); + return FALSE; + } + retval=plover_transaction_helper_add_transaction(helper,transaction, + NULL,RAZOR_INSTALL_ACTION_ADD,error); + g_object_unref(transaction); + return retval; +} + +gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + if (helper->error_dialog) + return TRUE; + else if (!helper->assistant) + return FALSE; + else + return gtk_widget_get_visible(GTK_WIDGET(helper->assistant)); +} + +void plover_transaction_helper_present(PloverTransactionHelper *helper) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + if (helper->error_dialog) + gtk_window_present(GTK_WINDOW(helper->error_dialog)); + else if (helper->assistant) + gtk_window_present(GTK_WINDOW(helper->assistant)); +} + +static void + plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog, + int response_id,PloverTransactionHelper *helper) +{ + g_signal_handlers_disconnect_by_data(error_dialog,helper); + if ((GtkWidget *)error_dialog==helper->error_dialog) + { + gtk_widget_destroy(helper->error_dialog); + helper->error_dialog=NULL; + if (helper->assistant) + { + gtk_widget_hide(GTK_WIDGET(helper->assistant)); + gtk_assistant_set_current_page(helper->assistant,0); + } + g_signal_emit(helper,signals[CLOSE],0); + } +} + +const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper, + const GError **error) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + if (!helper->error_dialog) + return NULL; + if (error) + *error=helper->error; + return helper->error_primary_text; +} + +void plover_transaction_helper_set_error(PloverTransactionHelper *helper, + const GError *error,const char *primary_text) +{ + GtkMessageType type; + GtkWindow *window; + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(error != NULL); + g_return_if_fail(primary_text != NULL); + if (helper->pulse_handler) + { + g_source_remove(helper->pulse_handler); + helper->pulse_handler=0; + } + if (helper->error_dialog) + { + gtk_widget_destroy(helper->error_dialog); + helper->error_dialog=NULL; + } + g_free(helper->error_primary_text); + helper->error_primary_text=g_strdup(primary_text); + g_clear_error(&helper->error); + helper->error=g_error_copy(error); + if (g_error_matches(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK)) + type=GTK_MESSAGE_INFO; + else + type=GTK_MESSAGE_ERROR; + if (helper->assistant) + window=GTK_WINDOW(helper->assistant); + else + window=NULL; + helper->error_dialog=gtk_message_dialog_new(window, + GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text); + gtk_message_dialog_format_secondary_text( + GTK_MESSAGE_DIALOG(helper->error_dialog),error->message); + gtk_widget_show(helper->error_dialog); + g_signal_connect(helper->error_dialog,"response", + G_CALLBACK(plover_transaction_helper_error_dialog_response),helper); +}