Signed-off-by: Jakub Filak jfilak@redhat.com --- src/gui-wizard-gtk/main.c | 24 +- src/gui-wizard-gtk/wizard.c | 1544 +++++++++++++++++---------------------- src/gui-wizard-gtk/wizard.glade | 10 +- src/gui-wizard-gtk/wizard.h | 2 + 4 files changed, 703 insertions(+), 877 deletions(-)
diff --git a/src/gui-wizard-gtk/main.c b/src/gui-wizard-gtk/main.c index 05d01e9..84b210e 100644 --- a/src/gui-wizard-gtk/main.c +++ b/src/gui-wizard-gtk/main.c @@ -22,12 +22,12 @@ #if HAVE_LOCALE_H # include <locale.h> #endif +#include "run_event_list_thread.h"
char *g_glade_file = NULL; char *g_dump_dir_name = NULL; char *g_events = NULL; GList *g_auto_event_list = NULL; -int g_report_only = false; problem_data_t *g_cd;
@@ -77,9 +77,14 @@ void reload_problem_data_from_dump_dir(void)
}
+struct elp_thread_args *g_event_process; + int main(int argc, char **argv) { const char *prgname = "abrt"; + + g_event_process = new_elp_thread_args(); + abrt_init(argv);
/* I18n */ @@ -113,9 +118,8 @@ int main(int argc, char **argv) OPT_v = 1 << 0, OPT_g = 1 << 1, OPT_p = 1 << 2, - OPT_o = 1 << 3, // report only - OPT_d = 1 << 4, - OPT_e = 1 << 5, + OPT_d = 1 << 3, + OPT_e = 1 << 4, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { @@ -123,7 +127,6 @@ int main(int argc, char **argv) OPT_STRING('g', NULL, &g_glade_file, "FILE", _("Alternate GUI file")), OPT_BOOL( 'p', NULL, NULL, _("Add program names to log")), /* for use from 3rd party apps to show just a reporter selector */ - OPT_BOOL( 'o', "report-only", &g_report_only, _("Skip analyze steps, go through report steps only")), OPT_BOOL( 'd', "delete", NULL, _("Remove DIR after reporting")), OPT_LIST( 'e', "event", &g_auto_event_list, "EVENT", _("Run only this event")), OPT_END() @@ -145,15 +148,24 @@ int main(int argc, char **argv)
reload_problem_data_from_dump_dir();
+ gdk_threads_init (); + gdk_threads_enter (); + + if (elp_thread_init(g_event_process)) + perror_msg_and_die("initializatoin of event process thread failed"); + create_assistant();
g_custom_logger = &show_error_as_msgbox;
update_gui_state_from_problem_data(); - /* Enter main loop */ gtk_main();
+ gdk_threads_leave(); + + free_elp_thread_args(g_event_process); + if (opts & OPT_d) delete_dump_dir_possibly_using_abrtd(g_dump_dir_name);
diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c index 30c25ee..8ad8285 100644 --- a/src/gui-wizard-gtk/wizard.c +++ b/src/gui-wizard-gtk/wizard.c @@ -20,6 +20,9 @@ #include <gdk/gdkkeysyms.h> #include "client.h" #include "internal_libreport_gtk.h" +#include "run_event.h" +#include "run_event_list.h" +#include "run_event_list_thread.h" #include "wizard.h"
#define DEFAULT_WIDTH 800 @@ -30,7 +33,6 @@ # define GDK_KEY_Delete GDK_Delete # define GDK_KEY_KP_Delete GDK_KP_Delete #endif - typedef struct event_gui_data_t { char *event_name; @@ -38,12 +40,55 @@ typedef struct event_gui_data_t } event_gui_data_t;
-static char *g_event_selected; -static unsigned g_black_event_count = 0; +static enum elp_signal_ret gtk_next_event_selected(struct event_list_process *process); +static enum elp_signal_ret gtk_review_data(struct event_list_process *process); +static enum elp_signal_ret gtk_run_event(struct event_list_process *process); +static enum elp_signal_ret gtk_event_done(struct event_list_process *process); +static enum elp_signal_ret gtk_finished(struct event_list_process *process); +static enum elp_signal_ret gtk_configuration_issue(struct event_list_process *process); + +static void gtk_alert(const char *msg, void *args); +static char *gtk_ask(const char *msg, void *args); +static int gtk_ask_yes_no(const char *msg, void *args); +static int gtk_ask_password(const char *msg, char **password, void *args); +static void gtk_command_started(pid_t pid, void *args); +static void gtk_command_msg_processed(const char *msg, const char *raw_input, const char *rsp, void *args); +static void gtk_command_finished(int retval, int status, const char *err_msg, void *args); + +static struct elp_signals_impl g_process_impl = { + .next_event_selected=gtk_next_event_selected, + .review_data=gtk_review_data, + .run_event=gtk_run_event, + .event_done=gtk_event_done, + .finished=gtk_finished, + .configuration_issue=gtk_configuration_issue, +}; + +static struct run_event_impl *g_run_impl; + +static struct run_event_impl g_gtk_run_impl = { + .alert=gtk_alert, + .ask=gtk_ask, + .ask_yes_no=gtk_ask_yes_no, + .ask_password=gtk_ask_password, + .args=NULL, +}; + +static struct run_event_observer *g_run_obs; + +static struct run_event_observer g_gtk_run_obs = { + .command_started=gtk_command_started, + .command_msg_processed=gtk_command_msg_processed, + .command_finished=gtk_command_finished, + .args=NULL, +}; + +static pthread_t g_gui_thread_id;
-static pid_t g_event_child_pid = 0; +static char *g_event_selected;
static bool g_expert_mode; +static bool g_reviewing_data;
static GtkNotebook *g_assistant; static GtkWindow *g_wnd_assistant; @@ -60,10 +105,6 @@ static GtkLabel *g_lbl_event_log; static GtkTextView *g_tv_event_log;
/* List of event_gui_data's */ - -/* List of event_gui_data's */ -static GList *g_list_selected_reporters; - static GtkContainer *g_container_details1; static GtkContainer *g_container_details2;
@@ -157,13 +198,13 @@ enum { * instead of strcmp. */ static const gchar PAGE_SUMMARY[] = "page_0"; -static const gchar PAGE_EVENT_SELECTOR[] = "page_2_report"; +static const gchar PAGE_EVENT_SELECTOR[] = "page_2"; static const gchar PAGE_EDIT_COMMENT[] = "page_1"; static const gchar PAGE_EDIT_ELEMENTS[] = "page_3"; -static const gchar PAGE_REVIEW_DATA[] = "page_4_report"; -static const gchar PAGE_EVENT_PROGRESS[] = "page_5_report"; -static const gchar PAGE_EVENT_DONE[] = "page_6_report"; -static const gchar PAGE_NOT_SHOWN[] = "page_7_report"; +static const gchar PAGE_REVIEW_DATA[] = "page_4"; +static const gchar PAGE_EVENT_PROGRESS[] = "page_5"; +static const gchar PAGE_EVENT_DONE[] = "page_6"; +static const gchar PAGE_NOT_SHOWN[] = "page_7";
static const gchar *const page_names[] = { @@ -188,9 +229,31 @@ typedef struct
static page_obj_t pages[NUM_PAGES];
-static struct strbuf *cmd_output = NULL; +//static struct strbuf *cmd_output = NULL; + +static gint select_next_page_no(gint current_page_no); +static void on_comment_changed(GtkTextBuffer *buffer, gpointer user_data);
/* Utility functions */ +static char *format_glist_of_strings(const GList *list, const char *item_format, const char *delimiter) +{ + struct strbuf *buf = strbuf_new(); + + const GList *i = list; + if (i) + { + strbuf_append_strf(buf, item_format, i->data); + i = g_list_next(i); + } + + for(; i; i = g_list_next(i)) + { + strbuf_append_str(buf, delimiter); + strbuf_append_strf(buf, item_format, i->data); + } + + return strbuf_free_nobuf(buf); +}
static void wrap_fixer(GtkWidget *widget, gpointer data_unused) { @@ -254,17 +317,27 @@ static void on_configure_event_cb(GtkWidget *button, gpointer user_data) } }
-static void show_event_opt_error_dialog(const char *event_name) +static void show_event_list_opt_error_dialog(const GList *events) { - event_config_t *ec = get_event_config(event_name); + struct strbuf *buf = strbuf_new(); + for(const GList *e = events; e; e = g_list_next(e)) + { + event_config_t *ec = get_event_config(e->data); + + if (e != events) + strbuf_append_strf(buf,",%s", ec ? ec->screen_name : e->data); + else + strbuf_append_strf(buf,"%s", ec ? ec->screen_name : e->data); + } + + char *names = strbuf_free_nobuf(buf); + char *message = xasprintf(_("Wrong settings detected for %s, " "reporting will probably fail if you continue " - "with the current configuration."), - ec->screen_name); + "with the current configuration."), names); char *markup_message = xasprintf(_("Wrong settings detected for <b>%s</b>, " "reporting will probably fail if you continue " - "with the current configuration."), - ec->screen_name); + "with the current configuration."), names); GtkWidget *wrong_settings = g_top_most_window = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, @@ -276,22 +349,36 @@ static void show_event_opt_error_dialog(const char *event_name) markup_message); free(message); free(markup_message); + free(names);
GtkWidget *act_area = gtk_dialog_get_content_area(GTK_DIALOG(wrong_settings)); - char * conf_btn_lbl = xasprintf(_("Con_figure %s"), ec->screen_name); - GtkWidget *configure_event_btn = gtk_button_new_with_mnemonic(conf_btn_lbl); - g_signal_connect(configure_event_btn, "clicked", G_CALLBACK(on_configure_event_cb), (gpointer)event_name); - free(conf_btn_lbl); - - gtk_box_pack_start(GTK_BOX(act_area), configure_event_btn, false, false, 0); - gtk_widget_show(configure_event_btn);
+ for(const GList *e = events; e; e = g_list_next(e)) + { + event_config_t *ec = get_event_config(e->data); + char * conf_btn_lbl = xasprintf(_("Configure %s"), ec->screen_name); + GtkWidget *configure_event_btn = gtk_button_new_with_mnemonic(conf_btn_lbl); + g_signal_connect(configure_event_btn, "clicked", G_CALLBACK(on_configure_event_cb), (gpointer)e->data); + free(conf_btn_lbl); + gtk_box_pack_start(GTK_BOX(act_area), configure_event_btn, false, false, 0); + gtk_widget_show(configure_event_btn); + }
gtk_dialog_run(GTK_DIALOG(wrong_settings)); if (g_top_most_window) gtk_widget_destroy(wrong_settings); }
+static void show_event_opt_error_dialog(const char *event_name) +{ + GList *e = NULL; + log("%s", event_name); + e = g_list_append(e, (gpointer)event_name); + show_event_list_opt_error_dialog(e); + g_list_free(e); + return; +} + static void update_window_title(void) { /* prgname can be null according to gtk documentation */ @@ -365,6 +452,7 @@ struct dump_dir *steal_if_needed(struct dump_dir *dd) */ char *old_name = g_dump_dir_name; g_dump_dir_name = xstrdup(dd->dd_dirname); + VERB1 log("New dump dir path '%s'", g_dump_dir_name); dd_close(dd);
update_window_title(); @@ -378,8 +466,11 @@ struct dump_dir *steal_if_needed(struct dump_dir *dd) return dd; }
-void show_error_as_msgbox(const char *msg) +void show_error_as_msgbox_mts(const char *msg, bool need_lock) { + if (need_lock) + gdk_threads_enter(); + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, @@ -388,6 +479,14 @@ void show_error_as_msgbox(const char *msg) ); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); + + if (need_lock) + gdk_threads_leave(); +} + +void show_error_as_msgbox(const char *msg) +{ + show_error_as_msgbox_mts(msg, !pthread_equal(g_gui_thread_id, pthread_self())); }
static void load_text_to_text_view(GtkTextView *tv, const char *name) @@ -715,38 +814,6 @@ static void event_rb_was_toggled(GtkButton *button, gpointer user_data) * Set "toggled" callback on each button to given GCallback if it's not NULL. * Return active button (or NULL if none created). */ -/* helper */ -static char *missing_items_in_comma_list(const char *input_item_list) -{ - if (!input_item_list) - return NULL; - - char *item_list = xstrdup(input_item_list); - char *result = item_list; - char *dst = item_list; - - while (item_list[0]) - { - char *end = strchr(item_list, ','); - if (end) *end = '\0'; - if (!get_problem_data_item_or_NULL(g_cd, item_list)) - { - if (dst != result) - *dst++ = ','; - dst = stpcpy(dst, item_list); - } - if (!end) - break; - *end = ','; - item_list = end + 1; - } - if (result == dst) - { - free(result); - result = NULL; - } - return result; -} static event_gui_data_t *add_event_buttons(GtkBox *box, GList **p_event_list, char *event_name, @@ -758,8 +825,6 @@ static event_gui_data_t *add_event_buttons(GtkBox *box, g_list_free(*p_event_list); *p_event_list = NULL;
- g_black_event_count = 0; - if (!event_name || !event_name[0]) { GtkWidget *lbl = gtk_label_new(_("No reporting targets are defined for this problem. Check configuration in /etc/libreport/*")); @@ -795,31 +860,19 @@ static event_gui_data_t *add_event_buttons(GtkBox *box, event_screen_name = cfg->screen_name; event_description = cfg->description;
- char *missing = missing_items_in_comma_list(cfg->ec_requires_items); - if (missing) - { - red_choice = true; - event_description = tmp_description = xasprintf(_("(requires: %s)"), missing); - free(missing); - } - else - if (cfg->ec_creates_items) + GList *issues = NULL; + enum event_usability_status status = check_event_usability(cfg, g_cd, NULL, &issues, EUS_LOW); + + red_choice = status == EUS_UNUSABLE; + green_choice = status == EUS_LOW; + + if (issues) { - if (get_problem_data_item_or_NULL(g_cd, cfg->ec_creates_items)) - { - char *missing = missing_items_in_comma_list(cfg->ec_creates_items); - if (missing) - free(missing); - else - { - green_choice = true; - event_description = tmp_description = xasprintf(_("(not needed, data already exist: %s)"), cfg->ec_creates_items); - } - } + event_description = tmp_description = format_glist_of_strings(issues, "(%s)", ","); + tmp_description[strlen(tmp_description)-1] = '\0'; + g_list_free_full(issues, (GDestroyNotify)free); } } - if (!green_choice && !red_choice) - g_black_event_count++;
//VERB2 log("adding button '%s' to box %p", event_name, box); char *event_label = xasprintf("%s%s%s", @@ -991,9 +1044,9 @@ static void append_item_to_ls_details(gpointer name, gpointer value, gpointer da }
/* Based on selected reporter, update item checkboxes */ -static void update_ls_details_checkboxes(void) +static void update_ls_details_checkboxes(const char *event_name) { - event_config_t *cfg = get_event_config(g_event_selected); + event_config_t *cfg = get_event_config(event_name); //log("%s: event:'%s', cfg:'%p'", __func__, g_event_selected, cfg); GHashTableIter iter; char *name; @@ -1117,32 +1170,6 @@ void update_gui_state_from_problem_data(void) gtk_widget_show_all(GTK_WIDGET(g_wnd_assistant)); }
- -/* start_event_run */ - -struct analyze_event_data -{ - struct run_event_state *run_state; - const char *event_name; - GList *env_list; - GtkWidget *page_widget; - GtkLabel *status_label; - GtkTextView *tv_log; - const char *success_msg; - const char *error_msg; - GIOChannel *channel; - struct strbuf *event_log; - int event_log_state; - int fd; - /*guint event_source_id;*/ -}; -enum { - LOGSTATE_FIRSTLINE = 0, - LOGSTATE_BEGLINE, - LOGSTATE_ERRLINE, - LOGSTATE_MIDLINE, -}; - static void set_excluded_envvar(void) { struct strbuf *item_list = strbuf_new(); @@ -1179,491 +1206,6 @@ static void set_excluded_envvar(void) unsetenv("EXCLUDE_FROM_REPORT"); }
-static int spawn_next_command_in_evd(struct analyze_event_data *evd) -{ - evd->env_list = export_event_config(evd->event_name); - int r = spawn_next_command(evd->run_state, g_dump_dir_name, evd->event_name); - if (r >= 0) - { - g_event_child_pid = evd->run_state->command_pid; - } - else - { - unexport_event_config(evd->env_list); - evd->env_list = NULL; - } - return r; -} - -static void save_to_event_log(struct analyze_event_data *evd, const char *str) -{ - static const char delim[] = { - [LOGSTATE_FIRSTLINE] = '>', - [LOGSTATE_BEGLINE] = ' ', - [LOGSTATE_ERRLINE] = '*', - }; - - while (str[0]) - { - char *end = strchrnul(str, '\n'); - char end_char = *end; - if (end_char == '\n') - end++; - switch (evd->event_log_state) - { - case LOGSTATE_FIRSTLINE: - case LOGSTATE_BEGLINE: - case LOGSTATE_ERRLINE: - /* skip empty lines */ - if (str[0] == '\n') - goto next; - strbuf_append_strf(evd->event_log, "%s%c %.*s", - iso_date_string(NULL), - delim[evd->event_log_state], - (int)(end - str), str - ); - break; - case LOGSTATE_MIDLINE: - strbuf_append_strf(evd->event_log, "%.*s", (int)(end - str), str); - break; - } - evd->event_log_state = LOGSTATE_MIDLINE; - if (end_char != '\n') - break; - evd->event_log_state = LOGSTATE_BEGLINE; - next: - str = end; - } -} - -static void update_event_log_on_disk(const char *str) -{ - /* Load existing log */ - struct dump_dir *dd = dd_opendir(g_dump_dir_name, 0); - if (!dd) - return; - char *event_log = dd_load_text_ext(dd, FILENAME_EVENT_LOG, DD_FAIL_QUIETLY_ENOENT); - - /* Append new log part to existing log */ - unsigned len = strlen(event_log); - if (len != 0 && event_log[len - 1] != '\n') - event_log = append_to_malloced_string(event_log, "\n"); - event_log = append_to_malloced_string(event_log, str); - - /* Trim log according to size watermarks */ - len = strlen(event_log); - char *new_log = event_log; - if (len > EVENT_LOG_HIGH_WATERMARK) - { - new_log += len - EVENT_LOG_LOW_WATERMARK; - new_log = strchrnul(new_log, '\n'); - if (new_log[0]) - new_log++; - } - - /* Save */ - dd_save_text(dd, FILENAME_EVENT_LOG, new_log); - free(event_log); - dd_close(dd); -} - -static void on_btn_cancel_event(GtkButton *button) -{ - if (g_event_child_pid > 0) - kill(- g_event_child_pid, SIGTERM); -} - -static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, gpointer data) -{ - struct analyze_event_data *evd = data; - - /* Read and insert the output into the log pane */ - char buf[257]; /* usually we get one line, no need to have big buf */ - int r; - int alert_prefix_len = strlen(REPORT_PREFIX_ALERT); - int ask_prefix_len = strlen(REPORT_PREFIX_ASK); - int ask_yes_no_prefix_len = strlen(REPORT_PREFIX_ASK_YES_NO); - int ask_password_prefix_len = strlen(REPORT_PREFIX_ASK_PASSWORD); - - if (!cmd_output) - cmd_output = strbuf_new(); - - /* read buffered and split lines */ - while ((r = read(evd->fd, buf, sizeof(buf) - 1)) > 0) - { - char *newline; - char *raw; - buf[r] = '\0'; - raw = buf; - - /* split lines in the current buffer */ - while ((newline = strchr(raw, '\n')) != NULL) - { - *newline = '\0'; - strbuf_append_str(cmd_output, raw); - char *msg = cmd_output->buf; - - /* In the code below: - * response is always malloced, - * log_response is always set to response - * or to constant string. - */ - char *response = NULL; - const char *log_response = response; - unsigned skip_chars = 0; - - char * tagged_msg = NULL; - - /* alert dialog */ - if (strncmp(REPORT_PREFIX_ALERT, msg, alert_prefix_len) == 0) - { - skip_chars = alert_prefix_len; - - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_CLOSE, - "%s", msg + skip_chars); - tagged_msg = tag_url(msg + skip_chars, "\n"); - gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); - - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - } - /* ask dialog with textbox */ - else if (strncmp(REPORT_PREFIX_ASK, msg, ask_prefix_len) == 0) - { - skip_chars = ask_prefix_len; - - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_OK_CANCEL, - "%s", msg + skip_chars); - tagged_msg = tag_url(msg + skip_chars, "\n"); - gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); - - GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkWidget *textbox = gtk_entry_new(); - /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE); - * is not available in gtk3, so please use the highlevel - * g_object_set - */ - g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL); - gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0); - gtk_widget_show(textbox); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) - { - const char *text = gtk_entry_get_text(GTK_ENTRY(textbox)); - response = xstrdup(text); - log_response = response; - } - else - { - response = xstrdup(""); - log_response = ""; - } - gtk_widget_destroy(textbox); - gtk_widget_destroy(dialog); - } - /* ask dialog with passwordbox */ - else if (strncmp(REPORT_PREFIX_ASK_PASSWORD, msg, ask_password_prefix_len) == 0) - { - skip_chars = ask_password_prefix_len; - - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_OK_CANCEL, - "%s", msg + skip_chars); - tagged_msg = tag_url(msg + skip_chars, "\n"); - gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); - - GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkWidget *textbox = gtk_entry_new(); - /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE); - * is not available in gtk3, so please use the highlevel - * g_object_set - */ - g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL); - gtk_entry_set_visibility(GTK_ENTRY(textbox), FALSE); - gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0); - gtk_widget_show(textbox); - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) - { - const char *text = gtk_entry_get_text(GTK_ENTRY(textbox)); - response = xstrdup(text); - log_response = "******"; /* don't log passwords! */ - } - else - { - response = xstrdup(""); - log_response = ""; - } - gtk_widget_destroy(textbox); - gtk_widget_destroy(dialog); - } - /* yes/no dialog */ - else if (strncmp(REPORT_PREFIX_ASK_YES_NO, msg, ask_yes_no_prefix_len) == 0) - { - skip_chars = ask_yes_no_prefix_len; - - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - "%s", msg + skip_chars); - tagged_msg = tag_url(msg + skip_chars, "\n"); - gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) - { - response = xstrdup(_("y")); - log_response = "YES"; - } - else - { - response = xstrdup(""); - log_response = "NO"; - } - gtk_widget_destroy(dialog); - } - /* else: no special prefix, just forward to log */ - - if (response) - { - unsigned len = strlen(response); - response[len++] = '\n'; - if (full_write(evd->run_state->command_in_fd, response, len) != len) - { - VERB1 perror_msg("Can't write %u bytes to child's stdin", len); - free(response); - response = xstrdup("<WRITE ERROR>"); - log_response = response; - } - strbuf_append_char(cmd_output, ' '); - strbuf_append_str(cmd_output, log_response); - free(response); - } - - msg = cmd_output->buf; - msg += skip_chars; - gtk_label_set_text(g_lbl_event_log, msg); - - strbuf_append_char(cmd_output, '\n'); - msg = cmd_output->buf; - msg += skip_chars; - append_to_textview(evd->tv_log, msg); - save_to_event_log(evd, msg); - - strbuf_clear(cmd_output); - - /* jump to next line */ - raw = newline + 1; - free(tagged_msg); - } - - /* beginning of next line. the line continues by next read() */ - strbuf_append_str(cmd_output, raw); - } - - if (r < 0 && errno == EAGAIN) - /* We got all buffered data, but fd is still open. Done for now */ - return TRUE; /* "please don't remove this event (yet)" */ - - /* EOF/error */ - - strbuf_clear(cmd_output); - - unexport_event_config(evd->env_list); - evd->env_list = NULL; - - /* Wait for child to actually exit, collect status */ - int status; - safe_waitpid(evd->run_state->command_pid, &status, 0); - int retval = WEXITSTATUS(status); - if (WIFSIGNALED(status)) - retval = WTERMSIG(status) + 128; - - /* Make sure "Cancel" button won't send anything (process is gone) */ - g_event_child_pid = -1; - evd->run_state->command_pid = -1; /* just for consistency */ - - /* Write a final message to the log */ - if (evd->event_log->len != 0 && evd->event_log->buf[evd->event_log->len - 1] != '\n') - save_to_event_log(evd, "\n"); - /* If program failed, or if it finished successfully without saying anything... */ - if (retval != 0 || evd->event_log_state == LOGSTATE_FIRSTLINE) - { - if (retval != 0) /* If program failed, emit error line */ - evd->event_log_state = LOGSTATE_ERRLINE; - char *msg; - if (WIFSIGNALED(status)) - msg = xasprintf("(killed by signal %u)\n", WTERMSIG(status)); - else - msg = xasprintf("(exited with %u)\n", retval); - append_to_textview(evd->tv_log, msg); - save_to_event_log(evd, msg); - free(msg); - } - - /* Append log to FILENAME_EVENT_LOG */ - update_event_log_on_disk(evd->event_log->buf); - strbuf_clear(evd->event_log); - evd->event_log_state = LOGSTATE_FIRSTLINE; - - if (geteuid() == 0) - { - /* Reset mode/uig/gid to correct values for all files created by event run */ - struct dump_dir *dd = dd_opendir(g_dump_dir_name, 0); - if (dd) - { - dd_sanitize_mode_and_owner(dd); - dd_close(dd); - } - } - - if (retval != 0) - { - /* If we were running -e EV1 -e EV2, stop if EV1 failed: */ - g_auto_event_list = NULL; - } - - /* Stop if exit code is not 0, or no more commands */ - if (retval != 0 - || spawn_next_command_in_evd(evd) < 0 - ) { - VERB1 log("done running event on '%s': %d", g_dump_dir_name, retval); - append_to_textview(evd->tv_log, "\n"); - - if (retval) - gtk_label_set_text(evd->status_label, evd->error_msg); - else - gtk_label_set_text(evd->status_label, evd->success_msg); - - /* Free child output buffer */ - strbuf_free(cmd_output); - cmd_output = NULL; - - /* Hide spinner and stop btn */ - gtk_widget_hide(GTK_WIDGET(g_spinner_event_log)); - gtk_widget_hide(g_btn_stop); - /* Enable (un-gray out) navigation buttons */ - gtk_widget_set_sensitive(g_btn_close, true); - gtk_widget_set_sensitive(g_btn_next, true); - - /*g_source_remove(evd->event_source_id);*/ - close(evd->fd); - free_run_event_state(evd->run_state); - strbuf_free(evd->event_log); - free(evd); - - reload_problem_data_from_dump_dir(); - update_gui_state_from_problem_data(); - - /* Inform abrt-gui that it is a good idea to rescan the directory */ - kill(getppid(), SIGCHLD); - - return FALSE; /* "please remove this event" */ - } - - /* New command was started. Continue waiting for input */ - - /* Transplant cmd's output fd onto old one, so that main loop - * is none the wiser that fd it waits on has changed - */ - xmove_fd(evd->run_state->command_out_fd, evd->fd); - evd->run_state->command_out_fd = evd->fd; /* just to keep it consistent */ - ndelay_on(evd->fd); - - /* Revive "Cancel" button */ - g_event_child_pid = evd->run_state->command_pid; - - return TRUE; /* "please don't remove this event (yet)" */ -} - -static void start_event_run(const char *event_name, - GtkWidget *page, - GtkTextView *tv_log, - GtkLabel *status_label, - const char *start_msg, - const char *error_msg, - const char *success_msg -) { - /* Start event asynchronously on the dump dir - * (synchronous run would freeze GUI until completion) - */ - struct run_event_state *state = new_run_event_state(); - - if (prepare_commands(state, g_dump_dir_name, event_name) == 0) - { - no_cmds: - /* No commands needed?! (This is untypical) */ - free_run_event_state(state); -//TODO: better msg? - char *msg = xasprintf(_("No processing for event '%s' is defined"), event_name); - gtk_label_set_text(status_label, msg); - free(msg); - return; - } - - struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY); - dd = steal_if_needed(dd); - int locked = (dd && dd->locked); - dd_close(dd); - if (!locked) - { - free_run_event_state(state); - return; /* user refused to steal, or write error, etc... */ - } - - set_excluded_envvar(); - GList *env_list = export_event_config(event_name); - - if (spawn_next_command(state, g_dump_dir_name, event_name) < 0) - { - unexport_event_config(env_list); - goto no_cmds; - } - g_event_child_pid = state->command_pid; - - /* At least one command is needed, and we started first one. - * Hook its output fd to the main loop. - */ - struct analyze_event_data *evd = xzalloc(sizeof(*evd)); - evd->run_state = state; - evd->event_name = event_name; - evd->env_list = env_list; - evd->page_widget = page; - evd->status_label = status_label; - evd->tv_log = tv_log; - evd->error_msg = error_msg; - evd->success_msg = success_msg; - evd->event_log = strbuf_new(); - evd->fd = state->command_out_fd; - ndelay_on(evd->fd); - evd->channel = g_io_channel_unix_new(evd->fd); - /*evd->event_source_id = */ g_io_add_watch(evd->channel, - G_IO_IN | G_IO_ERR | G_IO_HUP, /* need HUP to detect EOF w/o any data */ - consume_cmd_output, - evd - ); - - gtk_label_set_text(status_label, start_msg); - - VERB1 log("running event '%s' on '%s'", event_name, g_dump_dir_name); - char *msg = xasprintf("--- Running %s ---\n", event_name); - append_to_textview(evd->tv_log, msg); - free(msg); - - gtk_widget_show(GTK_WIDGET(g_spinner_event_log)); - gtk_widget_show(g_btn_stop); - /* Disable (gray out) navigation buttons */ - gtk_widget_set_sensitive(g_btn_close, false); - gtk_widget_set_sensitive(g_btn_next, false); -} - - /* Backtrace checkbox handling */
static void add_warning(const char *warning) @@ -1693,127 +1235,12 @@ static void clear_warnings(void) gtk_container_foreach(GTK_CONTAINER(g_box_warning_labels), &remove_child_widget, NULL); }
-static void check_bt_rating_and_allow_send(void) -{ - int minimal_rating = 0; - bool send = true; - - /* - * FIXME: this should be bind to a reporter not to a compoment - * but so far only oopses don't have rating, so for now we - * skip the "kernel" manually - */ - const char *analyzer = get_problem_item_content_or_NULL(g_cd, FILENAME_ANALYZER); -//FIXME: say "no" to special casing! - if (analyzer && strcmp(analyzer, "Kerneloops") != 0) - { - const char *rating_str = get_problem_item_content_or_NULL(g_cd, FILENAME_RATING); -//COMPAT, remove after 2.1 release - if (!rating_str) - rating_str = get_problem_item_content_or_NULL(g_cd, "rating"); - - if (rating_str) - { - char *endptr; - errno = 0; - long rating = strtol(rating_str, &endptr, 10); - if (errno != 0 || endptr == rating_str || *endptr != '\0') - { - add_warning(_("Reporting disabled because the rating does not contain a number '%s'.")); - send = false; - } - - GList *li = g_list_selected_reporters; - while (li != NULL) - { - /* need to obey the highest minimal rating of all selected reporters - * FIXME: check this when selecting the reporter and allow select - * only usable ones - */ - event_config_t *cfg = get_event_config((const char *)li->data); - if (cfg->ec_minimal_rating > minimal_rating) - { - minimal_rating = cfg->ec_minimal_rating; - VERB1 log("%s reporter sets the minimal rating to: %i", (const char *)li->data, minimal_rating); - } - - li = g_list_next(li); - }; - - if (rating == minimal_rating) /* bt is usable, but not complete, so show a warning */ - { - add_warning(_("The backtrace is incomplete, please make sure you provide the steps to reproduce.")); - } - - if (rating < minimal_rating) - { - //FIXME: see CreporterAssistant: 394 for ideas - add_warning(_("Reporting disabled because the backtrace is unusable.")); - send = false; - } - } - } - gtk_widget_set_sensitive(g_btn_next, send); -} - static void on_bt_approve_toggle(GtkToggleButton *togglebutton, gpointer user_data) { gtk_widget_set_sensitive(g_btn_next, gtk_toggle_button_get_active(g_tb_approve_bt)); }
-static void toggle_eb_comment(void) -{ - /* The page doesn't exist with report-only option */ - if (pages[PAGENO_EDIT_COMMENT].page_widget == NULL) - return; - - bool good = - gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(g_tv_comment)) >= 10 - || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_cb_no_comment)); - - /* Allow next page only when the comment has at least 10 chars */ - gtk_widget_set_sensitive(g_btn_next, good); - - /* And show the eventbox with label */ - if (good) - gtk_widget_hide(GTK_WIDGET(g_eb_comment)); - else - gtk_widget_show(GTK_WIDGET(g_eb_comment)); -} - -static void on_comment_changed(GtkTextBuffer *buffer, gpointer user_data) -{ - toggle_eb_comment(); -} - -static void on_no_comment_toggled(GtkToggleButton *togglebutton, gpointer user_data) -{ - toggle_eb_comment(); -} - - -static void on_show_event_list_cb(GtkWidget *button, gpointer user_data) -{ - show_events_list_dialog(GTK_WINDOW(g_wnd_assistant)); -} - -#if 0 -static void log_ready_state(void) -{ - char buf[NUM_PAGES+1]; - for (int i = 0; i < NUM_PAGES; i++) - { - char ch = '_'; - if (pages[i].page_widget) - ch = gtk_assistant_get_page_complete(g_assistant, pages[i].page_widget) ? '+' : '-'; - buf[i] = ch; - } - buf[NUM_PAGES] = 0; - log("Completeness:[%s]", buf); -} -#endif - -static gboolean highligh_word_in_tabs(const char *search_word, int flags) +static gboolean highligh_word_in_tabs(const char *search_word, int flags) { gboolean found = false; GtkTextBuffer *buffer; @@ -1926,45 +1353,460 @@ static void highlight_forbidden(void) list_free_with_free(forbidden_words); }
-static gint select_next_page_no(gint current_page_no, gpointer data); +static void process_step_done_callback_fn(struct elp_thread_args *args) +{ + /* + if (PAGENO_EVENT_PROGRESS == gtk_notebook_get_current_page(g_assistant)) + event_list_process_loop_next_step(args); + */ +} + +static void process_start_callback_fn(struct elp_thread_args *args) +{ + GList *need_config = NULL; + GHashTable *errors = NULL; + for(const GList* e = g_auto_event_list; e; e = g_list_next(e)) + { + event_config_t *ec = get_event_config(e->data); + errors = validate_event_config(ec); + if (errors != NULL) + { + g_hash_table_unref(errors); + need_config = g_list_prepend(need_config, e->data); + } + }
-static void on_page_prepare(GtkNotebook *assistant, GtkWidget *page, gpointer user_data) + if (need_config) + { + gdk_threads_enter(); + show_event_list_opt_error_dialog(need_config); + gdk_threads_leave(); + g_list_free(need_config); + } + + return; +} + +static void process_finish_callback_fn(struct elp_thread_args *args) +{ + free_event_list_process(elp_thread_args_get_process(args)); + + gdk_threads_enter(); + + if (!g_expert_mode) + gtk_main_quit(); + else + gtk_notebook_set_current_page(g_assistant, pages[PAGENO_EVENT_SELECTOR].page_no); + + gdk_threads_leave(); +} + +static void on_btn_cancel_event(GtkButton *button) { - //int page_no = gtk_assistant_get_current_page(g_assistant); - //log_ready_state(); + if (g_event_process) + elp_thread_loop_kill_command(g_event_process); +} + +static void on_next_btn_cb(GtkWidget *btn, gpointer user_data) +{ + const gint current_page_no = gtk_notebook_get_current_page(g_assistant); + const gint next_page_no = select_next_page_no(current_page_no); + + if (g_reviewing_data || !elp_thread_is_runnig(g_event_process)) + gtk_notebook_set_current_page(g_assistant, next_page_no); + else + elp_thread_loop_next_step(g_event_process); +} + +static void gtk_alert(const char *msg, void *args) +{ + gdk_threads_enter();
- /* This suppresses [Last] button: assistant thinks that - * we never have this page ready unless we are on it - * -> therefore there is at least one non-ready page - * -> therefore it won't show [Last] + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CLOSE, + "%s", msg); + char *tagged_msg = tag_url(msg, "\n"); + gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); + free(tagged_msg); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + gdk_threads_leave(); +} + +static char *gtk_ask(const char *msg, void *args) +{ + gdk_threads_enter(); + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_OK_CANCEL, + "%s", msg); + char *tagged_msg = tag_url(msg, "\n"); + gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); + free(tagged_msg); + + GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget *textbox = gtk_entry_new(); + /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE); + * is not available in gtk3, so please use the highlevel + * g_object_set */ - // Doesn't work: if Completeness:[++++++-+++], - // then [Last] btn will still be shown. - //gtk_assistant_set_page_complete(g_assistant, - // pages[PAGENO_REVIEW_DATA].page_widget, - // pages[PAGENO_REVIEW_DATA].page_widget == page - //); + g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL); + gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0); + gtk_widget_show(textbox);
- if (pages[PAGENO_SUMMARY].page_widget == page) + char *response = NULL; + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { - if (!g_expert_mode) - { - /* Skip intro screen */ - int n = select_next_page_no(pages[PAGENO_SUMMARY].page_no, NULL); - VERB2 log("switching to page_no:%d", n); - gtk_notebook_set_current_page(assistant, n); - return; - } + const char *text = gtk_entry_get_text(GTK_ENTRY(textbox)); + response = xstrdup(text); } + else + response = xstrdup(""); + + gtk_widget_destroy(textbox); + gtk_widget_destroy(dialog);
- if (pages[PAGENO_EDIT_ELEMENTS].page_widget == page) + gdk_threads_leave(); + + return response; +} + +static int gtk_ask_yes_no(const char *msg, void *args) +{ + gdk_threads_enter(); + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "%s", msg); + char *tagged_msg = tag_url(msg, "\n"); + gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); + free(tagged_msg); + + const int ret = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES; + + gtk_widget_destroy(dialog); + + gdk_threads_leave(); + + return ret; +} + +static int gtk_ask_password(const char *msg, char **password, void *args) +{ + gdk_threads_enter(); + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_OK_CANCEL, + "%s", msg); + char *tagged_msg = tag_url(msg, "\n"); + gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg); + free(tagged_msg); + + GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget *textbox = gtk_entry_new(); + /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE); + * is not available in gtk3, so please use the highlevel + * g_object_set + */ + g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL); + gtk_entry_set_visibility(GTK_ENTRY(textbox), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0); + gtk_widget_show(textbox); + + char *response = NULL; + int ret = 0; + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { - clear_warnings(); - check_bt_rating_and_allow_send(); - highlight_forbidden(); - show_warnings(); + ret = 1; + const char *text = gtk_entry_get_text(GTK_ENTRY(textbox)); + response = xstrdup(text); } + else + response = xstrdup(""); + + gtk_widget_destroy(textbox); + gtk_widget_destroy(dialog); + + if (password) + *password = response; + else + free(response); + + gdk_threads_leave(); + + return ret; +} + +static void gtk_command_started(pid_t pid, void *args) +{ + gdk_threads_enter(); + + VERB3 log("Running event command %d", pid); + + gdk_threads_leave(); +} + +static void gtk_command_msg_processed(const char *msg, const char *raw_input, const char *rsp, void *args) +{ + gdk_threads_enter(); + + gtk_label_set_text(g_lbl_event_log, msg); + append_to_textview(g_tv_event_log, msg); + append_to_textview(g_tv_event_log, "\n"); + + gdk_threads_leave(); +} + +static void gtk_command_finished(int retval, int status, const char *err_msg, void *args) +{ + gdk_threads_enter(); + + if (err_msg) + { + char *msg; + msg = xasprintf("(%s)\n", err_msg); + append_to_textview(g_tv_event_log, msg); + free(msg); + } + + gdk_threads_leave(); +} + +static enum elp_signal_ret gtk_next_event_selected(struct event_list_process *process) +{ + gdk_threads_enter(); + + if (!g_expert_mode) + { /* In the expert mode an executed list was build from g_event_list */ + /* therefore we have to care about a selected event only in not expert mode */ + free(g_event_selected); + g_event_selected = xstrdup(elp_get_current_event(process)); + } + + gdk_threads_leave(); + + return ELPSR_CONTINUE; +} + +static enum elp_signal_ret gtk_review_data(struct event_list_process *process) +{ + gdk_threads_enter(); + + g_reviewing_data = true; + + if (get_problem_item_content_or_NULL(g_cd, FILENAME_COMMENT)) + gtk_notebook_set_current_page(g_assistant, pages[PAGENO_EDIT_ELEMENTS].page_no); + else + gtk_notebook_set_current_page(g_assistant, pages[PAGENO_EDIT_COMMENT].page_no); + + gdk_threads_leave(); + + return ELPSR_CONTINUE; +} + +static enum elp_signal_ret gtk_run_event(struct event_list_process *process) +{ + gdk_threads_enter(); + + set_excluded_envvar(); + const char *event_name = elp_get_current_event(process); + log("running event '%s' on '%s'", event_name, g_dump_dir_name); + export_event_config(event_name); + + gtk_label_set_text(g_lbl_event_log, _("Processing...")); + + char *msg = xasprintf("--- Running %s ---\n", event_name); + append_to_textview(g_tv_event_log, msg); + free(msg); + + gtk_widget_show(GTK_WIDGET(g_spinner_event_log)); + gtk_widget_show(g_btn_stop); + /* Disable (gray out) navigation buttons */ + gtk_widget_set_sensitive(g_btn_close, false); + gtk_widget_set_sensitive(g_btn_next, false); + + gtk_notebook_set_current_page(g_assistant, PAGENO_EVENT_PROGRESS); + + gdk_threads_leave(); + + return ELPSR_CONTINUE; +} + +static void event_finished(const char *event_name, const char *message) +{ + //unexport_event_config(event_name);
+ VERB1 log("done running event on '%s'", g_dump_dir_name); + append_to_textview(g_tv_event_log, "\n"); + gtk_label_set_text(g_lbl_event_log, message); + + /* Hide spinner and stop btn */ + gtk_widget_hide(GTK_WIDGET(g_spinner_event_log)); + gtk_widget_hide(g_btn_stop); + /* Enable (un-gray out) navigation buttons */ + gtk_widget_set_sensitive(g_btn_close, true); + gtk_widget_set_sensitive(g_btn_next, true); + + reload_problem_data_from_dump_dir(); + update_gui_state_from_problem_data(); + + /* Inform abrt-gui that it is a good idea to rescan the directory */ + kill(getppid(), SIGCHLD); +} + +static enum elp_signal_ret gtk_event_done(struct event_list_process *process) +{ + gdk_threads_enter(); + + const char *const event_name = elp_get_current_event(process); + const enum elp_event_run_status status = elp_get_last_event_status(process); + switch(status) + { + case ELP_ERS_SUCCESSFUL: + event_finished(event_name, + _("Processing finished, please proceed to the next step.")); + break; + + case ELP_ERS_FAILED: + event_finished(event_name, + _("Processing failed. You can try another operation if available.")); + break; + + case ELP_ERS_NOT_FOUND: + event_finished(event_name, + _("We are so sorry, it is not possible to find any processing for this problem.")); + break; + + case ELP_ERS_NONE: + /* fall throug */ + default: + error_msg_and_die("unexpected event return status '%d'", status); + break; + } + + gdk_threads_leave(); + return ELPSR_CONTINUE; +} + +static enum elp_signal_ret gtk_finished(struct event_list_process *process) +{ + gdk_threads_enter(); + + gtk_notebook_set_current_page(g_assistant, PAGENO_EVENT_PROGRESS); + gtk_label_set_text(g_lbl_event_log, _("Thank you!")); + + gdk_threads_leave(); + + return ELPSR_CONTINUE; +} + +static enum elp_signal_ret gtk_configuration_issue(struct event_list_process *process) +{ + + enum elp_signal_ret ret = ELPSR_CONTINUE; + + if (!g_expert_mode) + { + const struct elp_configuration_issue *last = elp_get_last_configuration_issue(process); + + char *detail = format_glist_of_strings(last->issues, "* %s", "\n"); + event_config_t *ec = get_event_config(last->event_name); + + char *warning = xasprintf("Processing of '%s' has following problems:\n%s", + ec ? ec->screen_name : last->event_name, detail); + + gdk_threads_enter(); + if (last->usability == EUS_LOW) + add_warning(warning); + else if (last->usability == EUS_UNUSABLE) + show_error_as_msgbox_mts(warning, false); + gdk_threads_leave(); + + free(warning); + free(detail); + ret = last->usability == EUS_UNUSABLE ? ELPSR_FINISH : ELPSR_CONTINUE; + } + + return ret; +} + +static void toggle_eb_comment(void) +{ + /* The page doesn't exist with report-only option */ + if (pages[PAGENO_EDIT_COMMENT].page_widget == NULL) + return; + + bool good = + gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(g_tv_comment)) >= 10 + || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_cb_no_comment)); + + /* Allow next page only when the comment has at least 10 chars */ + gtk_widget_set_sensitive(g_btn_next, good); + + /* And show the eventbox with label */ + if (good) + gtk_widget_hide(GTK_WIDGET(g_eb_comment)); + else + gtk_widget_show(GTK_WIDGET(g_eb_comment)); +} + +static void on_comment_changed(GtkTextBuffer *buffer, gpointer user_data) +{ + toggle_eb_comment(); +} + +static void on_no_comment_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + toggle_eb_comment(); +} + + +static void on_show_event_list_cb(GtkWidget *button, gpointer user_data) +{ + show_events_list_dialog(GTK_WINDOW(g_wnd_assistant)); +} + +#if 0 +static void log_ready_state(void) +{ + char buf[NUM_PAGES+1]; + for (int i = 0; i < NUM_PAGES; i++) + { + char ch = '_'; + if (pages[i].page_widget) + ch = gtk_assistant_get_page_complete(g_assistant, pages[i].page_widget) ? '+' : '-'; + buf[i] = ch; + } + buf[NUM_PAGES] = 0; + log("Completeness:[%s]", buf); +} +#endif + +static void start_event_list_process(const GList *event_list) +{ + if (g_run_obs) + free_command_log_run_event_observer(g_run_obs); + + g_run_obs = new_command_log_run_event_observer(g_dump_dir_name, &g_gtk_run_obs); + + struct event_list_process *proc = + new_event_list_process(event_list, &g_process_impl, g_run_impl, g_run_obs, g_dump_dir_name, NULL); + + elp_thread_run(g_event_process, proc); +} + +static void on_page_prepare(GtkNotebook *assistant, GtkWidget *page, gpointer user_data) +{ /* Save text fields if changed */ save_items_from_notepad(); save_text_from_text_view(g_tv_comment, FILENAME_COMMENT); @@ -1972,6 +1814,8 @@ static void on_page_prepare(GtkNotebook *assistant, GtkWidget *page, gpointer us if (pages[PAGENO_SUMMARY].page_widget == page || pages[PAGENO_REVIEW_DATA].page_widget == page ) { + elp_thread_loop_next_step(g_event_process); + GtkWidget *w = GTK_WIDGET(g_tv_details); GtkContainer *c = GTK_CONTAINER(gtk_widget_get_parent(w)); if (c) @@ -1988,139 +1832,67 @@ static void on_page_prepare(GtkNotebook *assistant, GtkWidget *page, gpointer us if (pages[PAGENO_REVIEW_DATA].page_widget == page) { gtk_widget_set_sensitive(g_btn_next, gtk_toggle_button_get_active(g_tb_approve_bt)); - update_ls_details_checkboxes(); + update_ls_details_checkboxes(g_event_selected); } }
- if (pages[PAGENO_EDIT_COMMENT].page_widget == page) + if (pages[PAGENO_EDIT_COMMENT].page_widget == page ) { gtk_widget_set_sensitive(g_btn_next, false); on_comment_changed(gtk_text_view_get_buffer(g_tv_comment), NULL); } - //log_ready_state();
- if (pages[PAGENO_EVENT_PROGRESS].page_widget == page) + if (pages[PAGENO_EDIT_ELEMENTS].page_widget == page ) { - VERB2 log("g_event_selected:'%s'", g_event_selected); - if (g_event_selected - && g_event_selected[0] - ) { - start_event_run(g_event_selected, - pages[PAGENO_EVENT_PROGRESS].page_widget, - g_tv_event_log, - g_lbl_event_log, - _("Processing..."), - _("Processing failed. You can try another operation if available."), - _("Processing finished, please proceed to the next step.") - ); - - if (g_auto_event_list) - { - g_auto_event_list = g_auto_event_list->next; - VERB1 log("next -e EVENT:%s", g_auto_event_list ? (char*)g_auto_event_list->data : "NULL"); - } - } + clear_warnings(); + highlight_forbidden(); + show_warnings(); } }
-static gint select_next_page_no(gint current_page_no, gpointer data) +static gint select_next_page_no(gint current_page_no) { GtkWidget *page;
- if (g_report_only) - { - /* In only-report mode, we only need to wrap back at the end */ - page = gtk_notebook_get_nth_page(g_assistant, current_page_no); - if (page == pages[PAGENO_EVENT_DONE].page_widget) - current_page_no = 0; - else - current_page_no++; - VERB1 log("%s: selected page #%d", __func__, current_page_no); - return current_page_no; - } - - again: VERB1 log("%s: current_page_no:%d", __func__, current_page_no); - current_page_no++; page = gtk_notebook_get_nth_page(g_assistant, current_page_no);
if (pages[PAGENO_EVENT_SELECTOR].page_widget == page) { - if (!g_expert_mode) - { - VERB1 log("selected -e EVENT:%s on page: %d", (char*)g_auto_event_list->data, current_page_no); - free(g_event_selected); - g_event_selected = xstrdup((char*)g_auto_event_list->data); - /* - * We don't remove the list element, because GTK calls select_next_page_no() - * spuriously (for example, it calls it twice for first page). - */ - - if (check_event_config(g_event_selected) != 0) - { - goto again; - } + if(!g_expert_mode) + error_msg_and_die("Event selector is enabled only in export mode.");
- current_page_no = pages[PAGENO_EVENT_SELECTOR].page_no + 1; - goto event_was_selected; + struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY); + dd = steal_if_needed(dd); + const int locked = dd && dd->locked; + dd_close(dd);
- } - } + if (!locked) + return current_page_no;
- if (pages[PAGENO_EVENT_SELECTOR + 1].page_widget == page) - { - event_was_selected: - if (!g_event_selected) - { - /* Go back to selectors */ - current_page_no = pages[PAGENO_EVENT_SELECTOR].page_no - 1; - goto again; - } + g_list_free(g_auto_event_list); + g_auto_event_list = NULL; + g_auto_event_list = g_list_prepend(g_auto_event_list, g_event_selected);
- event_config_t *cfg = get_event_config(g_event_selected); - if (cfg && cfg->ec_skip_review) - { - current_page_no = pages[PAGENO_EVENT_PROGRESS].page_no - 1; - goto again; - } - if (!get_problem_item_content_or_NULL(g_cd, FILENAME_BACKTRACE)) - goto again; /* no backtrace, skip this page */ + start_event_list_process(g_auto_event_list); }
-#if 0 - if (pages[PAGENO_EDIT_COMMENT].page_widget == page) - { - if (get_problem_item_content_or_NULL(g_cd, FILENAME_COMMENT)) - goto again; /* no comment, skip this page */ - } -#endif + ++current_page_no; + + if (pages[PAGENO_REVIEW_DATA].page_widget == page) + g_reviewing_data = false;
if (pages[PAGENO_EVENT_DONE].page_widget == page) - { - if (g_auto_event_list) - { - /* Go back to selectors */ - current_page_no = pages[PAGENO_SUMMARY].page_no; - } - goto again; - } + current_page_no = pages[PAGENO_EVENT_SELECTOR].page_no;
if (pages[PAGENO_NOT_SHOWN].page_widget == page) - { - if (!g_expert_mode) - exit(0); - /* No! this would SEGV (infinitely recurse into select_next_page_no) */ - /*gtk_assistant_commit(g_assistant);*/ - current_page_no = pages[PAGENO_EVENT_SELECTOR].page_no - 1; - goto again; - } + current_page_no = pages[PAGENO_EVENT_SELECTOR].page_no;
VERB1 log("%s: selected page #%d", __func__, current_page_no); return current_page_no; }
- static void highlight_widget(GtkWidget *widget, gpointer *user_data) { gtk_drag_highlight(widget); @@ -2296,7 +2068,7 @@ static void on_btn_add_file(GtkButton *button) reload_problem_data_from_dump_dir(); update_gui_state_from_problem_data(); /* Set flags for the new item */ - update_ls_details_checkboxes(); + update_ls_details_checkboxes(g_event_selected); } } free(new_name); @@ -2381,18 +2153,6 @@ static gint on_key_press_event_in_item_list(GtkTreeView *treeview, GdkEventKey * /* wizard.glade file as a string WIZARD_GLADE_CONTENTS: */ #include "wizard_glade.c"
-static void on_next_btn_cb(GtkWidget *btn, gpointer user_data) -{ - gint current_page_no = gtk_notebook_get_current_page(g_assistant); - gint next_page_no = select_next_page_no(current_page_no, NULL); - - /* if pageno is not change 'switch-page' signal is not emitted */ - if (current_page_no == next_page_no) - on_page_prepare(g_assistant, gtk_notebook_get_nth_page(g_assistant, next_page_no), NULL); - else - gtk_notebook_set_current_page(g_assistant, next_page_no); -} - static void add_pages(void) { GError *error = NULL; @@ -2424,23 +2184,11 @@ static void add_pages(void) dd_close(dd);
int i; - int page_no = 0; for (i = 0; page_names[i] != NULL; i++) { - char *delim = strrchr(page_names[i], '_'); - if (!not_reportable && delim) - { - if (g_report_only && (strncmp(delim + 1, "report", strlen("report"))) != 0) - { - pages[i].page_widget = NULL; - pages[i].page_no = -1; - continue; - } - } - GtkWidget *page = GTK_WIDGET(gtk_builder_get_object(g_builder, page_names[i])); pages[i].page_widget = page; - pages[i].page_no = page_no++; + pages[i].page_no = i; gtk_notebook_append_page(g_assistant, page, gtk_label_new(pages[i].title)); VERB1 log("added page: %s", page_names[i]); } @@ -2568,10 +2316,53 @@ static void init_pages(void) init_page(&pages[7], PAGE_NOT_SHOWN , "" ); }
+static gboolean gtk_g_idle_prepare(GSource *source_data, gint *timeout) +{ + return TRUE; +} + +static gboolean gtk_g_idle_check(GSource *source) +{ + return TRUE; +} + +static gboolean gtk_g_idle_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) +{ + /* Start processing of list of events */ + gdk_threads_enter(); + + struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY); + dd = steal_if_needed(dd); + const int locked = dd && dd->locked; + dd_close(dd); + + gdk_threads_leave(); + + if (!locked) + { + gtk_main_quit(); + return FALSE; + } + + start_event_list_process(g_auto_event_list); + elp_thread_loop_next_step(g_event_process); + return FALSE; +} + +struct gtk_assistant_source +{ + GSource source; +}; + void create_assistant(void) { + g_expert_mode = !g_auto_event_list;
+ /* Run event callback for gtk are wrapped with logging implementation */ + /* TODO : missing clean up */ + g_run_impl = &g_gtk_run_impl; + g_monospace_font = pango_font_description_from_string("monospace"); g_builder = gtk_builder_new();
@@ -2599,6 +2390,7 @@ void create_assistant(void) gtk_widget_hide(g_btn_stop);
g_wnd_assistant = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + gtk_container_add(GTK_CONTAINER(g_wnd_assistant), GTK_WIDGET(g_box_assistant));
gtk_window_set_default_size(g_wnd_assistant, DEFAULT_WIDTH, DEFAULT_HEIGHT); @@ -2627,6 +2419,26 @@ void create_assistant(void)
g_signal_connect(g_search_entry_bt, "changed", G_CALLBACK(search_timeout), NULL);
- /* switch to right starting page */ - on_page_prepare(g_assistant, gtk_notebook_get_nth_page(g_assistant, 0), NULL); + elp_thread_args_set_on_step_done(g_event_process, process_step_done_callback_fn); + elp_thread_args_set_on_start(g_event_process, process_start_callback_fn); + elp_thread_args_set_on_finish(g_event_process, process_finish_callback_fn); + + g_gui_thread_id = pthread_self(); + + if (!g_expert_mode) + { + static GSourceFuncs idle_funcs = { + .prepare=gtk_g_idle_prepare, + .check=gtk_g_idle_check, + .dispatch=gtk_g_idle_dispatch, + .finalize=NULL, + .closure_callback=NULL, + .closure_marshal=NULL + }; + + struct gtk_assistant_source *const gtk_assistant_source = + (struct gtk_assistant_source*)g_source_new(&idle_funcs, sizeof(*gtk_assistant_source)); + + g_source_attach((GSource*)gtk_assistant_source, g_main_context_get_thread_default()); + } } diff --git a/src/gui-wizard-gtk/wizard.glade b/src/gui-wizard-gtk/wizard.glade index 41924eb..7029677 100644 --- a/src/gui-wizard-gtk/wizard.glade +++ b/src/gui-wizard-gtk/wizard.glade @@ -178,7 +178,7 @@ <object class="GtkWindow" id="window2"> <property name="can_focus">False</property> <child> - <object class="GtkVBox" id="page_2_report"> + <object class="GtkVBox" id="page_2"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="border_width">10</property> @@ -485,7 +485,7 @@ <object class="GtkWindow" id="window4"> <property name="can_focus">False</property> <child> - <object class="GtkVBox" id="page_4_report"> + <object class="GtkVBox" id="page_4"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="border_width">10</property> @@ -598,7 +598,7 @@ <object class="GtkWindow" id="window5"> <property name="can_focus">False</property> <child> - <object class="GtkVBox" id="page_5_report"> + <object class="GtkVBox" id="page_5"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="border_width">10</property> @@ -683,7 +683,7 @@ <object class="GtkWindow" id="window6"> <property name="can_focus">False</property> <child> - <object class="GtkVBox" id="page_6_report"> + <object class="GtkVBox" id="page_6"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="border_width">10</property> @@ -727,7 +727,7 @@ <object class="GtkWindow" id="window7"> <property name="can_focus">False</property> <child> - <object class="GtkVBox" id="page_7_report"> + <object class="GtkVBox" id="page_7"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="border_width">10</property> diff --git a/src/gui-wizard-gtk/wizard.h b/src/gui-wizard-gtk/wizard.h index a818828..64e6a4b 100644 --- a/src/gui-wizard-gtk/wizard.h +++ b/src/gui-wizard-gtk/wizard.h @@ -25,6 +25,8 @@ void create_assistant(void); void update_gui_state_from_problem_data(void); void show_error_as_msgbox(const char *msg);
+struct elp_thread_args; +extern struct elp_thread_args *g_event_process;
extern char *g_glade_file; extern char *g_dump_dir_name;