Blob Blame History Raw
/*
    Copyright (C) 2011  ABRT Team
    Copyright (C) 2011  RedHat inc.

    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 <gtk/gtk.h>
#include "internal_libreport_gtk.h"

static GtkWindow *g_event_list_window;
static GList *g_option_list = NULL;

static bool has_password_option;

enum
{
    COLUMN_EVENT_UINAME,
    COLUMN_EVENT_NAME,
    NUM_COLUMNS
};

static GtkWidget *gtk_label_new_justify_left(const gchar *label_str)
{
    GtkWidget *label = gtk_label_new(label_str);
    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);

    gtk_widget_set_halign (label, GTK_ALIGN_START);
    /* Make some space between label and input field to the right of it: */
    gtk_widget_set_margin_start(label, 5);
    gtk_widget_set_margin_end(label, 5);

    return label;
}

GList *add_option_widget(GList *options, GtkWidget *widget, event_option_t *option)
{
    option_widget_t *ow = (option_widget_t *)xmalloc(sizeof(*ow));
    ow->widget = widget;
    ow->option = option;
    options = g_list_prepend(options, ow);

    return options;
}

static void on_show_pass_cb(GtkToggleButton *tb, gpointer user_data)
{
    GtkEntry *entry = (GtkEntry *)user_data;
    gtk_entry_set_visibility(entry, gtk_toggle_button_get_active(tb));
}

static void on_show_pass_store_cb(GtkToggleButton *tb, gpointer user_data)
{
    set_user_setting("store_passwords", gtk_toggle_button_get_active(tb) ? "no" : "yes");
}

static unsigned add_one_row_to_grid(GtkGrid *table)
{
    gulong rows = (gulong)g_object_get_data(G_OBJECT(table), "n-rows");
    gtk_grid_insert_row(table, rows);
    g_object_set_data(G_OBJECT(table), "n-rows", (gpointer)(rows + 1));
    return rows;
}

void save_data_from_event_config_dialog(GList *widgets, event_config_t *ec)
{
    dehydrate_config_dialog(widgets);
    const char *const store_passwords_s = get_user_setting("store_passwords");
    save_event_config_data_to_user_storage(ec_get_name(ec),
                                           ec,
                                           !(store_passwords_s && !strcmp(store_passwords_s, "no")));
}

static void save_data_from_event_dialog_name(GList *widgets, const char *name)
{
    event_config_t *ec = get_event_config(name);

    save_data_from_event_config_dialog(widgets, ec);
}


static void add_option_to_table(gpointer data, gpointer user_data)
{
    event_option_t *option = data;
    GtkGrid *option_table = user_data;
    if (option->is_advanced)
        option_table = GTK_GRID(g_object_get_data(G_OBJECT(option_table), "advanced-options"));

    GtkWidget *label;
    GtkWidget *option_input;
    unsigned last_row;

    char *option_label;
    if (option->eo_label != NULL)
        option_label = xstrdup(option->eo_label);
    else
    {
        option_label = xstrdup(option->eo_name ? option->eo_name : "");
        /* Replace '_' with ' ' */
        char *p = option_label - 1;
        while (*++p)
            if (*p == '_')
                *p = ' ';
    }

    switch (option->eo_type)
    {
        case OPTION_TYPE_TEXT:
        case OPTION_TYPE_NUMBER:
        case OPTION_TYPE_PASSWORD:
            last_row = add_one_row_to_grid(option_table);
            label = gtk_label_new_justify_left(option_label);
            gtk_grid_attach(option_table, label,
                             /*left,top:*/ 0, last_row,
                             /*width,height:*/ 1, 1);
            option_input = gtk_entry_new();
            gtk_entry_set_activates_default(GTK_ENTRY(option_input), TRUE);
            gtk_widget_set_hexpand(option_input, TRUE);
            if (option->eo_value != NULL)
                gtk_entry_set_text(GTK_ENTRY(option_input), option->eo_value);
            gtk_grid_attach(option_table, option_input,
                             /*left,top:*/ 1, last_row,
                             /*width,height:*/ 1, 1);
            g_option_list = add_option_widget(g_option_list, option_input, option);
            if (option->eo_type == OPTION_TYPE_PASSWORD)
            {
                gtk_entry_set_visibility(GTK_ENTRY(option_input), 0);
                last_row = add_one_row_to_grid(option_table);
                GtkWidget *pass_cb = gtk_check_button_new_with_label(_("Show password"));
                gtk_grid_attach(option_table, pass_cb,
                             /*left,top:*/ 1, last_row,
                             /*width,height:*/ 1, 1);
                g_signal_connect(pass_cb, "toggled", G_CALLBACK(on_show_pass_cb), option_input);
                has_password_option = true;
            }
            break;

        case OPTION_TYPE_HINT_HTML:
            label = gtk_label_new(option_label);
            gtk_label_set_use_markup(GTK_LABEL(label), TRUE);

            gtk_widget_set_halign(label, GTK_ALIGN_START);
            gtk_widget_set_valign(label, GTK_ALIGN_START);

            make_label_autowrap_on_resize(GTK_LABEL(label));

            last_row = add_one_row_to_grid(option_table);
            gtk_grid_attach(option_table, label,
                             /*left,top:*/ 0, last_row,
                             /*width,height:*/ 2, 1);
            break;

        case OPTION_TYPE_BOOL:
            last_row = add_one_row_to_grid(option_table);
            option_input = gtk_check_button_new_with_label(option_label);
            gtk_grid_attach(option_table, option_input,
                             /*left,top:*/ 0, last_row,
                             /*width,height:*/ 2, 1);
            if (option->eo_value != NULL)
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(option_input),
                                    string_to_bool(option->eo_value));
            g_option_list = add_option_widget(g_option_list, option_input, option);
            break;

        default:
            //option_input = gtk_label_new_justify_left("WTF?");
            log_warning("unsupported option type");
            free(option_label);
            return;
    }

    if (option->eo_note_html)
    {
        label = gtk_label_new(option->eo_note_html);
        gtk_label_set_use_markup(GTK_LABEL(label), TRUE);

        gtk_widget_set_halign(label, GTK_ALIGN_START);
        gtk_widget_set_valign(label, GTK_ALIGN_START);

        make_label_autowrap_on_resize(GTK_LABEL(label));

        last_row = add_one_row_to_grid(option_table);
        gtk_grid_attach(option_table, label,
                             /*left,top:*/ 1, last_row,
                             /*top,heigh:*/ 1, 1);
    }

    free(option_label);
}

static GtkWidget *create_event_config_grid()
{
    GtkWidget *option_table = gtk_grid_new();

    gtk_widget_set_margin_start(option_table, 5);
    gtk_widget_set_margin_end(option_table, 5);

    gtk_widget_set_margin_top(option_table, 5);
    gtk_widget_set_margin_bottom(option_table, 5);

    gtk_grid_set_row_homogeneous(GTK_GRID(option_table), FALSE);
    gtk_grid_set_column_homogeneous(GTK_GRID(option_table), FALSE);
    gtk_grid_set_row_spacing(GTK_GRID(option_table), 10);
    g_object_set_data(G_OBJECT(option_table), "n-rows", (gpointer)-1);

    gtk_widget_set_hexpand(option_table, TRUE);
    gtk_widget_set_vexpand(option_table, TRUE);
    gtk_widget_set_halign(option_table, GTK_ALIGN_FILL);
    gtk_widget_set_valign(option_table, GTK_ALIGN_FILL);

    return option_table;
}

config_dialog_t *create_event_config_dialog_content(event_config_t *event, GtkWidget *content)
{
    INITIALIZE_LIBREPORT();

    if (content == NULL)
        content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);

    //event_config_t *event = get_event_config(event_name);
    GtkWidget *notebook_layout = gtk_notebook_new();
    gtk_box_pack_start(GTK_BOX(content), notebook_layout, TRUE, TRUE, 0);

    GtkWidget *option_table = create_event_config_grid();

    /* table to hold advanced options
     * hidden in expander which is visible only if there's at least
     * one advanced option
    */
    GtkWidget *adv_option_table = create_event_config_grid();

    g_object_set_data(G_OBJECT(option_table), "advanced-options", adv_option_table);

    has_password_option = false;
    /* it's already stored in config_dialog_t from the previous call
     * we need to set it to null so we create a new list for the actual
     * event_config
     * note: say *NO* to the global variables!
    */
    g_option_list = NULL;
    /* this fills the g_option_list, so we can use it for new_config_dialog */
    g_list_foreach(event->options, &add_option_to_table, option_table);

    /* if there is at least one password option, add checkbox to disable storing passwords */
    /* if the user storage is not available nothing is to be stored, so it is not necessary
     * to bother with an extra checkbox about storing passwords */
    if (is_event_config_user_storage_available()
            && has_password_option)
    {
        unsigned last_row = add_one_row_to_grid(GTK_GRID(option_table));
        GtkWidget *pass_store_cb = gtk_check_button_new_with_label(_("Don't store passwords"));
        gtk_grid_attach(GTK_GRID(option_table), pass_store_cb,
                /*left,top:*/ 0, last_row,
                /*width,height:*/ 1, 1);
        const char *store_passwords = get_user_setting("store_passwords");
        if (store_passwords && !strcmp(store_passwords, "no"))
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pass_store_cb), 1);
        g_signal_connect(pass_store_cb, "toggled", G_CALLBACK(on_show_pass_store_cb), NULL);
    }

    GtkWidget *option_table_lbl = gtk_label_new_with_mnemonic(_("Basic"));
    GtkWidget *option_table_scrl = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_add(GTK_CONTAINER(option_table_scrl), option_table);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook_layout), option_table_scrl, option_table_lbl);

    /* add the adv_option_table to the dialog only if there is some adv option */
    if (g_list_length(gtk_container_get_children(GTK_CONTAINER(adv_option_table))) > 0)
    {
        GtkWidget *adv_option_table_lbl = gtk_label_new_with_mnemonic(_("Advanced"));
        GtkWidget *adv_option_table_scrl = gtk_scrolled_window_new(NULL, NULL);
        gtk_container_add(GTK_CONTAINER(adv_option_table_scrl), adv_option_table);
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook_layout), adv_option_table_scrl, adv_option_table_lbl);
    }
    else
        /* Do not show single tab 'Basic' */
        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook_layout), FALSE);

    /* add warning if secrets service is not available showing the nagging dialog
     * is considered "too heavy UI" be designers
     */
    if (!is_event_config_user_storage_available())
    {
        GtkWidget *keyring_warn_lbl =
        gtk_label_new(
          _("Secret Service is not available, your settings won't be saved!"));
        gtk_widget_set_name(keyring_warn_lbl, "keyring_warn_lbl");

        GtkCssProvider *g_provider = gtk_css_provider_new();
        gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
                                                  GTK_STYLE_PROVIDER(g_provider),
                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
        const gchar *data = "#keyring_warn_lbl {color: rgba(100%, 0%, 0%, 1);}";
        gtk_css_provider_load_from_data(g_provider, data, -1, NULL);
        g_object_unref (g_provider);

        gtk_box_pack_start(GTK_BOX(content), keyring_warn_lbl, false, false, 0);
    }

    gtk_widget_show_all(content); //make it all visible

    //g_option_list is filled on
    config_dialog_t *cdialog = new_config_dialog(NULL,
                                    g_option_list,
                                    (config_save_fun_t)save_data_from_event_dialog_name
                                    );

    return cdialog;
}

config_dialog_t *create_event_config_dialog(const char *event_name, GtkWindow *parent)
{
    INITIALIZE_LIBREPORT();

    event_config_t *event = get_event_config(event_name);

    if(!ec_is_configurable(event))
        return NULL;

    GtkWindow *parent_window = parent ? parent : g_event_list_window;

    char *window_title = xasprintf("%s - Reporting Configuration",
            ec_get_screen_name(event) ? ec_get_screen_name(event) : event_name);

    GtkWidget *dialog = gtk_dialog_new_with_buttons(
                        window_title,
                        parent_window,
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                        _("_Cancel"),
                        GTK_RESPONSE_CANCEL,
                        _("_OK"),
                        GTK_RESPONSE_APPLY,
                        NULL);

    free(window_title);

    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);

    /* Allow resize?
     * W/o resize, e.g. upload configuration hint looks awfully
     * line wrapped.
     */
    gtk_window_set_resizable(GTK_WINDOW(dialog), true);
    gtk_window_set_default_size(GTK_WINDOW(dialog), 450, 310);

    if (parent_window != NULL)
    {
        gtk_window_set_icon_name(GTK_WINDOW(dialog),
                                 gtk_window_get_icon_name(parent_window));
    }

    GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
    config_dialog_t *cdialog = create_event_config_dialog_content(event, content);
    cdialog_set_widget(cdialog, dialog);

    return cdialog;
}

static void add_event_to_liststore(gpointer key, gpointer value, gpointer list_store)
{
    config_item_info_t *info = ec_get_config_info((event_config_t *)value);
    config_dialog_t *cdialog = create_event_config_dialog(key, NULL);

    add_item_to_config_liststore(cdialog, info, list_store);
}

GtkListStore *add_events_to_liststore(GHashTable *events)
{
    GtkListStore *list_store = new_conf_liststore();
    g_hash_table_foreach(events, (GHFunc)add_event_to_liststore, list_store);

    return list_store;
}

int show_event_config_dialog(const char *event_name, GtkWindow *parent)
{
    INITIALIZE_LIBREPORT();

    config_dialog_t *dialog = create_event_config_dialog(event_name, parent);
    const int result = cdialog_run(dialog, event_name);
    free(dialog);

    return result;
}