diff options
Diffstat (limited to 'src/gui-gtk/main.c')
-rw-r--r-- | src/gui-gtk/main.c | 514 |
1 files changed, 512 insertions, 2 deletions
diff --git a/src/gui-gtk/main.c b/src/gui-gtk/main.c index f76f62f3..2888d6d5 100644 --- a/src/gui-gtk/main.c +++ b/src/gui-gtk/main.c @@ -16,15 +16,525 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + #include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> #include <sys/inotify.h> +#include <libreport-gtk.h> #include "abrtlib.h" #include "parse_options.h" -#include "abrt-gtk.h" #if HAVE_LOCALE_H # include <locale.h> #endif +static void scan_dirs_and_add_to_dirlist(void); + + +static const char help_uri[] = "http://docs.fedoraproject.org/en-US/" + "Fedora/14/html/Deployment_Guide/ch-abrt.html"; + +static GtkListStore *s_dumps_list_store; +static GtkWidget *s_treeview; +static GtkWidget *g_main_window; +static GtkWidget *s_report_window; + +enum +{ + COLUMN_REPORTED, + COLUMN_REASON, + COLUMN_DIRNAME, + COLUMN_LATEST_CRASH_STR, + COLUMN_LATEST_CRASH, + COLUMN_DUMP_DIR, + NUM_COLUMNS +}; + +static void add_directory_to_dirlist(const char *dirname) +{ + /* Silently ignore *any* errors, not only EACCES. + * We saw "lock file is locked by process PID" error + * when we raced with wizard. + */ + int sv_logmode = logmode; + logmode = 0; + struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY | DD_FAIL_QUIETLY_EACCES); + logmode = sv_logmode; + if (!dd) + return; + + time_t time = atoi(dd_load_text(dd, FILENAME_TIME)); + struct tm *ptm = localtime(&time); + char time_buf[60]; + size_t time_len = strftime(time_buf, sizeof(time_buf)-1, "%c", ptm); + time_buf[time_len] = '\0'; + + char *msg = dd_load_text_ext(dd, FILENAME_REPORTED_TO, 0 + | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE + | DD_FAIL_QUIETLY_ENOENT + | DD_FAIL_QUIETLY_EACCES + ); + const char *reported = (msg ? GTK_STOCK_YES : GTK_STOCK_NO); + free(msg); + char *reason = dd_load_text(dd, FILENAME_REASON); + + GtkTreeIter iter; + gtk_list_store_append(s_dumps_list_store, &iter); + gtk_list_store_set(s_dumps_list_store, &iter, + COLUMN_REPORTED, reported, + COLUMN_REASON, reason, + COLUMN_DIRNAME, dd->dd_dirname, + //OPTION: time format + COLUMN_LATEST_CRASH_STR, time_buf, + COLUMN_LATEST_CRASH, (int)time, + COLUMN_DUMP_DIR, dirname, + -1); + free(reason); + + dd_close(dd); + VERB1 log("added: %s", dirname); +} + +static void rescan_dirs_and_add_to_dirlist(void) +{ + gtk_list_store_clear(s_dumps_list_store); + scan_dirs_and_add_to_dirlist(); +} + + +static GtkTreePath *get_cursor(void) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(s_treeview); + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + if (selection) + { + GtkTreeIter iter; + GtkTreeModel *store = gtk_tree_view_get_model(treeview); + if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE) + { + return gtk_tree_model_get_path(store, &iter); + } + } + return NULL; +} + +static void sanitize_cursor(GtkTreePath *preferred_path) +{ + GtkTreePath *path; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(s_treeview), &path, /* GtkTreeViewColumn** */ NULL); + if (path) + { + /* Cursor exists already */ + goto ret; + } + + if (preferred_path) + { + /* Try to position cursor on preferred_path */ + gtk_tree_view_set_cursor(GTK_TREE_VIEW(s_treeview), preferred_path, + /* GtkTreeViewColumn *focus_column */ NULL, /* start_editing */ false); + + /* Did it work? */ + gtk_tree_view_get_cursor(GTK_TREE_VIEW(s_treeview), &path, /* GtkTreeViewColumn** */ NULL); + if (path) /* yes */ + goto ret; + } + + /* Try to position cursor on 1st element */ + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(s_dumps_list_store), &iter)) + { + /* We have at least one element, put cursor on it */ + + /* Get path from iter pointing to 1st element */ + path = gtk_tree_model_get_path(GTK_TREE_MODEL(s_dumps_list_store), &iter); + + /* Use it to set cursor */ + gtk_tree_view_set_cursor(GTK_TREE_VIEW(s_treeview), path, + /* GtkTreeViewColumn *focus_column */ NULL, /* start_editing */ false); + } + /* else we have no elements */ + + ret: + gtk_tree_path_free(path); + + /* Without this, the *header* of the list gets the focus. Ugly. */ + gtk_widget_grab_focus(s_treeview); +} + + +/* create_main_window and helpers */ + +static void on_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + if (selection) + { + GtkTreeIter iter; + GtkTreeModel *store = gtk_tree_view_get_model(treeview); + if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE) + { + GValue d_dir = { 0 }; + gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir); + + const char *dirname = g_value_get_string(&d_dir); + report_problem_in_dir(dirname, LIBREPORT_ANALYZE | LIBREPORT_NOWAIT); + } + } +} + +static void on_btn_report_cb(GtkButton *button, gpointer user_data) +{ + on_row_activated_cb(GTK_TREE_VIEW(s_treeview), NULL, NULL, NULL); +} + +static void delete_report(GtkTreeView *treeview) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + if (selection) + { + GtkTreeIter iter; + GtkTreeModel *store = gtk_tree_view_get_model(treeview); + if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE) + { + GtkTreePath *old_path = gtk_tree_model_get_path(store, &iter); + + GValue d_dir = { 0 }; + gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir); + const char *dump_dir_name = g_value_get_string(&d_dir); + + VERB1 log("Deleting '%s'", dump_dir_name); + if (delete_dump_dir_possibly_using_abrtd(dump_dir_name) == 0) + { + gtk_list_store_remove(s_dumps_list_store, &iter); + } + else + { + /* Strange. Deletion did not succeed. Someone else deleted it? + * Rescan the whole list */ + rescan_dirs_and_add_to_dirlist(); + } + + /* Try to retain the same cursor position */ + sanitize_cursor(old_path); + gtk_tree_path_free(old_path); + } + } +} + +static gint on_key_press_event_cb(GtkTreeView *treeview, GdkEventKey *key, gpointer unused) +{ + int k = key->keyval; + + if (k == GDK_Delete || k == GDK_KP_Delete) + { + delete_report(treeview); + return TRUE; + } + return FALSE; +} + +static void on_btn_delete_cb(GtkButton *button, gpointer unused) +{ + delete_report(GTK_TREE_VIEW(s_treeview)); +} + +static void on_btn_online_help_cb(GtkButton *button, gpointer unused) +{ + gtk_show_uri(NULL, help_uri, GDK_CURRENT_TIME, NULL); +} + +static void on_menu_help_cb(GtkMenuItem *menuitem, gpointer unused) +{ + gtk_show_uri(NULL, help_uri, GDK_CURRENT_TIME, NULL); +} + +static void on_button_send_cb(GtkWidget *button, gpointer data) +{ + GtkTextView *tev = GTK_TEXT_VIEW(data); + GtkTextBuffer *buf = gtk_text_view_get_buffer(tev); + GtkTextIter it_start; + GtkTextIter it_end; + gtk_text_buffer_get_start_iter(buf, &it_start); + gtk_text_buffer_get_end_iter(buf, &it_end); + gchar *text = gtk_text_buffer_get_text(buf, + &it_start, + &it_end, + false); + + problem_data_t *pd = new_problem_data(); + + if (text[0]) + { + add_to_problem_data(pd, "description", text); + } + + /* why it doesn't want to hide before report ends? */ + gtk_widget_destroy(s_report_window); + +//TODO: LIBREPORT_WAIT is used here only because we don't want to leave temp dir. +//Change to LIBREPORT_NOWAIT when libreport is taught to +//properly delete temp dir even with LIBREPORT_NOWAIT. + int status = report_problem_in_memory(pd, LIBREPORT_WAIT); + VERB1 log("Reporting finished with status %i", status); + free_problem_data(pd); +} + +static void on_menu_report_cb(GtkMenuItem *menuitem, gpointer unused) +{ + s_report_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(s_report_window), _("Problem description")); + gtk_window_set_default_size(GTK_WINDOW(s_report_window), 400, 400); + GtkWidget *vbox = gtk_vbox_new(false, 0); + GtkWidget *button_send = gtk_button_new_with_label(_("Send")); + GtkWidget *tev = gtk_text_view_new(); + g_signal_connect(button_send, "clicked", G_CALLBACK(on_button_send_cb), tev); + + gtk_box_pack_start(GTK_BOX(vbox), tev, true, true, 0); + gtk_box_pack_start(GTK_BOX(vbox), button_send, false, false, 5); + + gtk_container_add(GTK_CONTAINER(s_report_window), vbox); + + gtk_widget_show_all(s_report_window); +} + +static void on_menu_about_cb(GtkMenuItem *menuitem, gpointer unused) +{ + static const char copyright_str[] = "Copyright © 2009, 2010, 2011 Red Hat, Inc"; + + static const char license_str[] = "This program is free software; you can redistribut" + "e 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 Li" + "cense, or (at your option) any later version.\n\nThis program is distrib" + "uted in the hope that it will be useful, but WITHOUT ANY WARRANTY; witho" + "ut even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICU" + "LAR PURPOSE. See the GNU General Public License for more details.\n\nYo" + "u should have received a copy of the GNU General Public License along wi" + "th this program. If not, see <http://www.gnu.org/licenses/>."; + + static const char website_url[] = "https://fedorahosted.org/abrt/"; + + static const char *authors[] = { + "Anton Arapov <aarapov@redhat.com>", + "Karel Klic <kklic@redhat.com>", + "Jiri Moskovcak <jmoskovc@redhat.com>", + "Nikola Pajkovsky <npajkovs@redhat.com>", + "Denys Vlasenko <dvlasenk@redhat.com>", + "Michal Toman <mtoman@redhat.com>", + "Zdenek Prikryl", + NULL + }; + + static const char *artists[] = { + "Patrick Connelly <pcon@fedoraproject.org>", + "Máirín Duffy <duffy@fedoraproject.org>", + "Lapo Calamandrei", + "Jakub Steinar <jsteiner@redhat.com>", + NULL + }; + + GtkWidget *about_d = gtk_about_dialog_new(); + + gtk_window_set_icon_name(GTK_WINDOW(about_d), "abrt"); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about_d), VERSION); + gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(about_d), "abrt"); + gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about_d), "ABRT"); + gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about_d), copyright_str); + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about_d), license_str); + gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(about_d),true); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about_d), website_url); + gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about_d), authors); + gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about_d), artists); + gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about_d), _("translator-credits")); + + gtk_window_set_transient_for(GTK_WINDOW(about_d), GTK_WINDOW(g_main_window)); + + gtk_dialog_run(GTK_DIALOG(about_d)); + gtk_widget_hide(GTK_WIDGET(about_d)); +} + +static void show_events_list_dialog_cb(GtkMenuItem *menuitem, gpointer user_data) +{ + show_events_list_dialog(GTK_WINDOW(g_main_window)); +} + +static void add_columns(GtkTreeView *treeview) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes(_("Reported"), + renderer, + "stock_id", + COLUMN_REPORTED, + NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, COLUMN_REPORTED); + gtk_tree_view_append_column(treeview, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Problem"), + renderer, + "text", + COLUMN_REASON, + NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, COLUMN_REASON); + gtk_tree_view_append_column(treeview, column); + + /* + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Stored in"), + renderer, + "text", + COLUMN_DIRNAME, + NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, COLUMN_DIRNAME); + gtk_tree_view_append_column(treeview, column); + */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Last occurrence"), + renderer, + "text", + COLUMN_LATEST_CRASH_STR, + NULL); + gtk_tree_view_column_set_sort_column_id(column, COLUMN_LATEST_CRASH); + gtk_tree_view_append_column(treeview, column); +} + +static GtkWidget *create_menu(void) +{ + /* main bar */ + GtkWidget *menu = gtk_menu_bar_new(); + GtkWidget *file_item = gtk_menu_item_new_with_mnemonic(_("_File")); + GtkWidget *edit_item = gtk_menu_item_new_with_mnemonic(_("_Edit")); + GtkWidget *help_item = gtk_menu_item_new_with_mnemonic(_("_Help")); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), file_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), edit_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), help_item); + + /* file submenu */ + GtkWidget *file_submenu = gtk_menu_new(); + GtkWidget *quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(file_submenu), quit_item); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_submenu); + + g_signal_connect(quit_item, "activate", >k_main_quit, NULL); + + /* edit submenu */ + GtkWidget *edit_submenu = gtk_menu_new(); + GtkWidget *plugins_item = gtk_menu_item_new_with_mnemonic(_("_Event configuration")); + //GtkWidget *preferences_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(edit_submenu), plugins_item); + //gtk_menu_shell_append(GTK_MENU_SHELL(edit_submenu), preferences_item); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_item), edit_submenu); + + g_signal_connect(plugins_item, "activate", G_CALLBACK(show_events_list_dialog_cb), NULL); + + /* help submenu */ + GtkWidget *help_submenu = gtk_menu_new(); + GtkWidget *online_help_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL); + GtkWidget *report_problem_item = gtk_menu_item_new_with_label(_("Report problem with ABRT")); + GtkWidget *about_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), online_help_item); + gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), report_problem_item); + gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), about_item); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_submenu); + + g_signal_connect(online_help_item, "activate", G_CALLBACK(on_menu_help_cb), NULL); + g_signal_connect(report_problem_item, "activate", G_CALLBACK(on_menu_report_cb), NULL); + g_signal_connect(about_item, "activate", G_CALLBACK(on_menu_about_cb), NULL); + + return menu; +} + +static GtkWidget *create_main_window(void) +{ + /* Main window */ + g_main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(g_main_window), 700, 700); + gtk_window_set_title(GTK_WINDOW(g_main_window), _("Automatic Bug Reporting Tool")); + gtk_window_set_default_icon_name("abrt"); + + GtkWidget *main_vbox = gtk_vbox_new(false, 0); + + /* Scrolled region inside main window */ + GtkWidget *scroll_win = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_win), + GTK_SHADOW_ETCHED_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + gtk_box_pack_start(GTK_BOX(main_vbox), create_menu(), false, false, 0); + gtk_box_pack_start(GTK_BOX(main_vbox), scroll_win, true, true, 0); + gtk_container_add(GTK_CONTAINER(g_main_window), main_vbox); + + /* Tree view inside scrolled region */ + s_treeview = gtk_tree_view_new(); + g_object_set(s_treeview, "rules-hint", 1, NULL); /* use alternating colors */ + add_columns(GTK_TREE_VIEW(s_treeview)); + gtk_container_add(GTK_CONTAINER(scroll_win), s_treeview); + + /* Create data store for the list and attach it */ + s_dumps_list_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, /* reported */ + G_TYPE_STRING, /* executable */ + G_TYPE_STRING, /* hostname */ + G_TYPE_STRING, /* time */ + G_TYPE_INT, /* unix time - used for sort */ + G_TYPE_STRING, /* dump dir path */ + G_TYPE_STRING);/* row background */ + + //FIXME: configurable!! + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_dumps_list_store), + COLUMN_LATEST_CRASH, + GTK_SORT_DESCENDING); + + gtk_tree_view_set_model(GTK_TREE_VIEW(s_treeview), GTK_TREE_MODEL(s_dumps_list_store)); + + /* buttons are homogenous so set size only for one button and it will + * work for the rest buttons in same gtk_hbox_new() */ + GtkWidget *btn_report = gtk_button_new_from_stock(GTK_STOCK_OPEN); + gtk_widget_set_size_request(btn_report, 200, 30); + + GtkWidget *btn_delete = gtk_button_new_from_stock(GTK_STOCK_DELETE); + + GtkWidget *hbox_report_delete = gtk_hbox_new(true, 4); + gtk_box_pack_start(GTK_BOX(hbox_report_delete), btn_delete, true, true, 0); + gtk_box_pack_start(GTK_BOX(hbox_report_delete), btn_report, true, true, 0); + + GtkWidget *halign = gtk_alignment_new(1, 0, 0, 0); + gtk_container_add(GTK_CONTAINER(halign), hbox_report_delete); + + GtkWidget *hbox_help_close = gtk_hbutton_box_new(); + GtkWidget *btn_online_help = gtk_button_new_with_mnemonic(_("Online _Help")); + GtkWidget *btn_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_end(GTK_BOX(hbox_help_close), btn_online_help, false, false, 0); + gtk_box_pack_end(GTK_BOX(hbox_help_close), btn_close, false, false, 0); + + gtk_box_pack_start(GTK_BOX(main_vbox), halign, false, false, 10); + gtk_box_pack_start(GTK_BOX(main_vbox), hbox_help_close, false, false, 10); + + /* Double click/Enter handler */ + g_signal_connect(s_treeview, "row-activated", G_CALLBACK(on_row_activated_cb), NULL); + g_signal_connect(btn_report, "clicked", G_CALLBACK(on_btn_report_cb), NULL); + /* Delete handler */ + g_signal_connect(s_treeview, "key-press-event", G_CALLBACK(on_key_press_event_cb), NULL); + g_signal_connect(btn_delete, "clicked", G_CALLBACK(on_btn_delete_cb), NULL); + /* Quit when user closes the main window */ + g_signal_connect(g_main_window, "destroy", gtk_main_quit, NULL); + /* Quit when user click on Cancel button */ + g_signal_connect(btn_close, "clicked", gtk_main_quit, NULL); + /* Show online help */ + g_signal_connect(btn_online_help, "clicked", G_CALLBACK(on_btn_online_help_cb), NULL); + + return g_main_window; +} + + static int inotify_fd = -1; static GIOChannel *channel_inotify; static int channel_inotify_event_id = -1; @@ -182,7 +692,7 @@ static void scan_directory_and_add_to_dirlist(const char *path) } } -void scan_dirs_and_add_to_dirlist(void) +static void scan_dirs_and_add_to_dirlist(void) { char **argv = s_dirs; while (*argv) |