/*
Copyright (C) 2014 ABRT Team
Copyright (C) 2014 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 "problem_details_widget.h"
#include "internal_libreport_gtk.h"
#include "internal_libreport.h"
#define PROBLEM_DETAILS_WIDGET_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), TYPE_PROBLEM_DETAILS_WIDGET, ProblemDetailsWidgetPrivate))
#define EXPLICIT_ITEMS \
CD_DUMPDIR, \
FILENAME_TIME, \
FILENAME_LAST_OCCURRENCE, \
FILENAME_UID, \
FILENAME_USERNAME, \
FILENAME_TYPE, \
FILENAME_COMMENT, \
FILENAME_ANALYZER
#define ORDERED_ITEMS \
FILENAME_EXPLOITABLE, \
FILENAME_NOT_REPORTABLE, \
FILENAME_REASON, \
FILENAME_BACKTRACE, \
FILENAME_CRASH_FUNCTION, \
FILENAME_CMDLINE, \
FILENAME_EXECUTABLE, \
FILENAME_PACKAGE, \
FILENAME_COMPONENT, \
FILENAME_PID, \
FILENAME_PWD, \
FILENAME_HOSTNAME, \
FILENAME_COUNT
static GtkCssProvider *g_provider = NULL;
static const char *items_orderlist[] = {
ORDERED_ITEMS,
NULL,
};
static const char *items_auto_blacklist[] = {
EXPLICIT_ITEMS,
ORDERED_ITEMS,
FILENAME_PKG_NAME,
FILENAME_PKG_VERSION,
FILENAME_PKG_RELEASE,
FILENAME_PKG_ARCH,
FILENAME_PKG_EPOCH,
NULL,
};
struct ProblemDetailsWidgetPrivate {
gulong rows;
problem_data_t *problem_data;
};
G_DEFINE_TYPE(ProblemDetailsWidget, problem_details_widget, GTK_TYPE_GRID)
static void problem_details_widget_finalize(GObject *object);
static void load_css_style()
{
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 = "#value {font-family: monospace;}";
gtk_css_provider_load_from_data(g_provider, data, -1, NULL);
g_object_unref (g_provider);
}
static void
problem_details_widget_class_init(ProblemDetailsWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = problem_details_widget_finalize;
g_type_class_add_private(klass, sizeof(ProblemDetailsWidgetPrivate));
}
static void
problem_details_widget_finalize(GObject *object)
{
ProblemDetailsWidget *self;
self = PROBLEM_DETAILS_WIDGET(object);
self->priv->problem_data = (void *)0xdeadbeaf;
G_OBJECT_CLASS(problem_details_widget_parent_class)->finalize(object);
}
static gulong
problem_details_widget_append_row(ProblemDetailsWidget *self)
{
gtk_grid_insert_row(GTK_GRID(self), self->priv->rows);
return self->priv->rows++;
}
static void
problem_details_widget_add_single_line(ProblemDetailsWidget *self, const char *name, const char *content)
{
if (g_provider == NULL)
load_css_style();
GtkWidget *label = gtk_label_new(name);
gtk_widget_set_halign(label, GTK_ALIGN_START);
gtk_widget_set_valign(label, GTK_ALIGN_START);
GtkWidget *value = gtk_label_new(content);
gtk_label_set_selectable(GTK_LABEL(value), TRUE);
gtk_label_set_line_wrap(GTK_LABEL(value), TRUE);
gtk_label_set_line_wrap_mode(GTK_LABEL(value), GTK_WRAP_WORD);
gtk_widget_set_halign(value, GTK_ALIGN_START);
gtk_widget_set_hexpand(value, TRUE);
gtk_widget_set_name(GTK_WIDGET(value), "value");
gtk_widget_set_margin_start(label, 20);
gtk_widget_set_margin_end(label, 20);
gtk_widget_set_margin_start(value, 5);
const gulong row = problem_details_widget_append_row(self);
gtk_grid_attach(GTK_GRID(self), label, 0, row, 1, 1);
gtk_grid_attach(GTK_GRID(self), value, 1, row, 1, 1);
}
static void
problem_details_widget_add_multi_line(ProblemDetailsWidget *self, const char *name, const char *content)
{
if (g_provider == NULL)
load_css_style();
#if 0
GtkWidget *value = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(value), FALSE);
if (strcmp(name, FILENAME_COMMENT) == 0
|| strcmp(name, FILENAME_REASON) == 0)
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(value), GTK_WRAP_WORD);
reload_text_to_text_view(GTK_TEXT_VIEW(value), content);
#else
GtkWidget *value = gtk_label_new(content);
gtk_widget_set_halign(value, GTK_ALIGN_START);
if (strcmp(name, FILENAME_COMMENT) == 0
|| strcmp(name, FILENAME_REASON) == 0)
{
gtk_label_set_line_wrap(GTK_LABEL(value), TRUE);
gtk_label_set_line_wrap_mode(GTK_LABEL(value), GTK_WRAP_WORD);
gtk_widget_set_margin_bottom(value, 12);
}
gtk_label_set_selectable(GTK_LABEL(value), TRUE);
#endif
gtk_widget_set_name(GTK_WIDGET(value), "value");
GtkWidget *expander = gtk_expander_new(name);
gtk_widget_set_hexpand(expander, TRUE);
gtk_container_add(GTK_CONTAINER(expander), value);
const gulong row = problem_details_widget_append_row(self);
gtk_grid_attach(GTK_GRID(self), expander, 0, row, 2, 1);
}
static void
problem_details_widget_add_binary(ProblemDetailsWidget *self, const char *label, const char *path)
{
struct stat statbuf;
statbuf.st_size = 0;
if (stat(path, &statbuf) != 0)
{
log_warning("File '%s' does not exist", path);
return;
}
gchar *size = g_format_size_full((long long)statbuf.st_size, G_FORMAT_SIZE_IEC_UNITS);
char *msg = xasprintf(_("$DATA_DIRECTORY/%s (binary file, %s)"), label, size);
problem_details_widget_add_single_line(self, label, msg);
free(msg);
g_free(size);
}
static void
problem_details_widget_add_time_stamp(ProblemDetailsWidget *self, const char *label, const char *stamp)
{
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
const char *ret = strptime(stamp, "%s", &tm);
if (ret == NULL || ret[0] != '\0')
return;
char buf[255];
strftime(buf, sizeof(buf), "%F %T", &tm);
problem_details_widget_add_single_line(self, label, buf);
}
static void
problem_details_widget_add_problem_item(ProblemDetailsWidget *self, const char *name, problem_item *item)
{
if (item->flags & CD_FLAG_TXT)
{
if (strchr(item->content, '\n') == NULL)
problem_details_widget_add_single_line(self, name, item->content);
else
problem_details_widget_add_multi_line(self, name, item->content);
}
else if (item->flags & CD_FLAG_BIN)
problem_details_widget_add_binary(self, name, item->content);
else
log_warning("Unsupported file type");
}
/* Callback for GHashTable */
static void
problem_data_entry_to_grid_row_one_line(const char *item_name, problem_item *item, ProblemDetailsWidget *self)
{
if (((item->flags & CD_FLAG_TXT) && (strchr(item->content, '\n') == NULL))
&& !is_in_string_list(item_name, items_auto_blacklist))
problem_details_widget_add_single_line(self, item_name, item->content);
}
static void
problem_data_entry_to_grid_row_multi_line(const char *item_name, problem_item *item, ProblemDetailsWidget *self)
{
if (((item->flags & CD_FLAG_TXT) && (strchr(item->content, '\n') != NULL))
&& !is_in_string_list(item_name, items_auto_blacklist))
problem_details_widget_add_multi_line(self, item_name, item->content);
}
static void
problem_data_entry_to_grid_row_binary(const char *item_name, problem_item *item, ProblemDetailsWidget *self)
{
if ((item->flags & CD_FLAG_BIN)
&& !is_in_string_list(item_name, items_auto_blacklist))
problem_details_widget_add_binary(self, item_name, item->content);
}
static void
problem_details_widget_populate(ProblemDetailsWidget *self)
{
{ /* Explicit order */
for (const char **iter = items_orderlist; *iter; ++iter)
{
struct problem_item *item = problem_data_get_item_or_NULL(
self->priv->problem_data, *iter);
if (item == NULL)
continue;
problem_details_widget_add_problem_item(self, *iter, item);
}
}
{ /* comment: */
const char *dd = problem_data_get_content_or_NULL(
self->priv->problem_data, FILENAME_COMMENT);
if (dd)
problem_details_widget_add_multi_line(self, FILENAME_COMMENT, dd);
}
{ /* First occurence: 2014-08-26 11:08 */
const char *ts = problem_data_get_content_or_NULL(
self->priv->problem_data, FILENAME_TIME);
if (ts)
problem_details_widget_add_time_stamp(self, "first_occurence", ts);
}
{ /* Last occurence: 2014-08-27 11:08 */
const char *ts = problem_data_get_content_or_NULL(
self->priv->problem_data, FILENAME_LAST_OCCURRENCE);
if (ts)
problem_details_widget_add_time_stamp(self, "last_occurence", ts);
}
{ /* User: login(UID) */
const char *uid = problem_data_get_content_or_NULL(
self->priv->problem_data, FILENAME_UID);
const char *username = problem_data_get_content_or_NULL(
self->priv->problem_data, "username");
char *line = NULL;
if (uid && username)
line = xasprintf("%s (%s)", username, uid);
else if (!uid && !username)
line = xstrdup("unknown user");
else
line = xasprintf("%s", uid ? uid : username);
problem_details_widget_add_single_line(self, "user", line);
free(line);
}
{ /* Type/Analyzer: CCpp */
const char *type = problem_data_get_content_or_NULL(
self->priv->problem_data, FILENAME_TYPE);
const char *analyzer = problem_data_get_content_or_NULL(
self->priv->problem_data, FILENAME_ANALYZER);
char *label = NULL;
char *line = NULL;
if (type != NULL && analyzer != NULL)
{
if (strcmp(type, analyzer) != 0)
{
label = xstrdup("type/analyzer");
line = xasprintf("%s/%s", type, analyzer);
}
else
{
label = xstrdup("type");
line = xstrdup(type);
}
}
else
{
label = xstrdup(type ? "type" : "anlyzer");
line = xstrdup(type ? type : analyzer);
}
problem_details_widget_add_single_line(self, label, line);
free(line);
free(label);
}
g_hash_table_foreach(self->priv->problem_data,
(GHFunc)problem_data_entry_to_grid_row_one_line, self);
{ /* data directory: */
const char *dd = problem_data_get_content_or_NULL(
self->priv->problem_data, CD_DUMPDIR);
if (dd)
problem_details_widget_add_single_line(self, "data_directory", dd);
/* show binaries below the data_directory entry */
g_hash_table_foreach(self->priv->problem_data,
(GHFunc)problem_data_entry_to_grid_row_binary, self);
}
g_hash_table_foreach(self->priv->problem_data,
(GHFunc)problem_data_entry_to_grid_row_multi_line, self);
}
static void
problem_details_widget_init(ProblemDetailsWidget *self)
{
self->priv = PROBLEM_DETAILS_WIDGET_GET_PRIVATE(self);
self->priv->rows = 0;
self->priv->problem_data = NULL;
}
ProblemDetailsWidget *
problem_details_widget_new(problem_data_t *problem)
{
INITIALIZE_LIBREPORT();
GObject *object = g_object_new(TYPE_PROBLEM_DETAILS_WIDGET, NULL);
ProblemDetailsWidget *self = PROBLEM_DETAILS_WIDGET(object);
self->priv->problem_data = problem;
problem_details_widget_populate(self);
gtk_widget_show_all(GTK_WIDGET(self));
return self;
}