diff options
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/callbacks.c | 325 | ||||
| -rw-r--r-- | shell/iconcache.c | 100 | ||||
| -rw-r--r-- | shell/loadgraph-uber.c | 131 | ||||
| -rw-r--r-- | shell/loadgraph.c | 331 | ||||
| -rw-r--r-- | shell/menu.c | 178 | ||||
| -rw-r--r-- | shell/report.c | 1284 | ||||
| -rw-r--r-- | shell/shell.c | 2398 | ||||
| -rw-r--r-- | shell/stock.c | 93 | ||||
| -rw-r--r-- | shell/syncmanager.c | 734 | 
9 files changed, 5574 insertions, 0 deletions
| diff --git a/shell/callbacks.c b/shell/callbacks.c new file mode 100644 index 00000000..b9c6252b --- /dev/null +++ b/shell/callbacks.c @@ -0,0 +1,325 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br> + * + *    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, version 2 or later. + * + *    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 St, Fifth Floor, Boston, MA  02110-1301 USA + */ + +#include <stdlib.h> +#include <gtk/gtk.h> + +#include "hardinfo.h" +#include "callbacks.h" +#include "iconcache.h" + +#include "shell.h" +#include "report.h" +#include "syncmanager.h" +#include "uri_handler.h" + +#include "config.h" + +void cb_sync_manager() +{ +    Shell *shell = shell_get_main_shell(); + +    sync_manager_show(shell->window); +} +#if GLIB_CHECK_VERSION(2,40,0) +#else +//For compatibility with older glib +gboolean g2_key_file_save_to_file (GKeyFile *key_file, +              const gchar  *filename, GError **error) +{ +  gchar *contents; +  gboolean success; +  gsize length; + +  g_return_val_if_fail (key_file != NULL, FALSE); +  g_return_val_if_fail (filename != NULL, FALSE); +  g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + +  contents = g_key_file_to_data (key_file, &length, NULL); +  g_assert (contents != NULL); + +  success = g_file_set_contents (filename, contents, length, error); +  g_free (contents); + +  return success; +} +#endif + +void cb_sync_on_startup() +{ +    gboolean setting = shell_action_get_active("SyncOnStartupAction"); +    GKeyFile *key_file = g_key_file_new(); + +    g_mkdir(g_get_user_config_dir(),0755); +    g_mkdir(g_build_filename(g_get_user_config_dir(), "hardinfo", NULL),0755); + +    gchar *conf_path = g_build_filename(g_get_user_config_dir(), "hardinfo2", +                                        "settings.ini", NULL); + +    g_key_file_load_from_file( +        key_file, conf_path, +        G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); +    g_key_file_set_boolean(key_file, "Sync", "OnStartup", setting); +#if GLIB_CHECK_VERSION(2,40,0) +    g_key_file_save_to_file(key_file, conf_path, NULL); +#else +    g2_key_file_save_to_file(key_file, conf_path, NULL); +#endif +    g_free(conf_path); +    g_key_file_free(key_file); +} + +void cb_open_web_page() +{ +    uri_open("https://www.hardinfo2.org"); +} + +void cb_report_bug() +{ +    uri_open("https://github.com/hardinfo2/hardinfo2/issues"); +} + +void cb_refresh() +{ +    shell_do_reload(); +} + +void cb_copy_to_clipboard() +{ +    ShellModuleEntry *entry = shell_get_main_shell()->selected; + +    if (entry) { +	gchar *data = module_entry_function(entry); +	GtkClipboard *clip = +	    gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)); +	ReportContext *ctx = report_context_text_new(NULL); + +	ctx->entry = entry; + +	report_header(ctx); +	report_table(ctx, data); +	report_footer(ctx); + +	gtk_clipboard_set_text(clip, ctx->output, -1); + +	g_free(data); +	report_context_free(ctx); +    } +} + +void cb_side_pane() +{ +    gboolean visible; + +    visible = shell_action_get_active("SidePaneAction"); +    shell_set_side_pane_visible(visible); +} + +void cb_toolbar() +{ +    gboolean visible; + +    visible = shell_action_get_active("ToolbarAction"); +    shell_ui_manager_set_visible("/MainMenuBarAction", visible); +} + +void cb_about_module(GtkAction * action) +{ +    Shell *shell = shell_get_main_shell(); +    GSList *modules = shell->tree->modules; +    const ModuleAbout *ma; +    gchar *name; + +    g_object_get(G_OBJECT(action), "tooltip", &name, NULL); + +    for (; modules; modules = modules->next) { +	ShellModule *sm = (ShellModule *) modules->data; + +	if (!g_str_equal(sm->name, name)) +	    continue; + +	if ((ma = module_get_about(sm))) { +	    GtkWidget *about; +	    gchar *text; + +	    about = gtk_about_dialog_new(); + +	    gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(shell->window)); + +	    text = g_strdup(sm->name); +#if GTK_CHECK_VERSION(2, 12, 0) +	    gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), text); +#else +	    gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), text); +#endif +	    g_free(text); + +	    gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), +					 ma->version); + +	    text = g_strdup_printf(_("Written by %s\nLicensed under %s"), +				   ma->author, ma->license); +	    gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), text); +	    g_free(text); + +	    if (ma->description) +		gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), +					      _(ma->description)); + +	    gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), sm->icon); +	    gtk_dialog_run(GTK_DIALOG(about)); +	    gtk_widget_destroy(about); +	} else { +	    g_warning +		(_("No about information is associated with the %s module."), +		 name); +	} + +	break; +    } + +    g_free(name); +} + +void cb_about() +{ +    Shell *shell = shell_get_main_shell(); +    GtkWidget *about; +    gchar *copyright = NULL; +    const gchar *authors[] = { +        "L. A. F. Pereira (2003-2023)", +	"hwspeedy(2024-)", +        "Agney Lopes Roth Ferraz", +        "Andrey Esin", +        "Burt P.", +        "Ondrej Čerman", +        "Stewart Adam", +        "Pascal F. Martin", +        "TotalCaesar659", +        "Julian Ospald", +	"Julien Lavergne", +        "Fernando López", +        "PICCORO Lenz McKAY", +        "Alexander Münch", +        "Simon Quigley", +        "AsciiWolf", +        "George Schneeloch", +        "Mattia Rizzolo", +        "Yo", +        "jamesbond", +        "Ondrej Čerman", +        "Mike Hewitt", +        "Boris Afonot", +	"", +	"Based on work by:", +	"uber-graph by Christian Hergert and others.", +	"BinReloc by Hongli Lai", +	"decode-dimms by Philip Edelbrock", +	"decode-dimms by Christian Zuckschwerdt", +	"decode-dimms by Burkart Lingner", +        "x86cpucaps by Osamu Kayasono", +        "MD5 implementation by Colin Plumb", +        "SHA1 implementation by Steve Reid", +        "Blowfish implementation by Paul Kocher", +        "Raytracing benchmark by John Walker", +        "FFT benchmark by Scott Robert Ladd", +        "Vendor list based on GtkSysInfo by Pissens Sebastien", +        "DMI support based on code by Stewart Adam", +        "SCSI support based on code by Pascal F. Martin", +	"", +	"Translated by:", +	"Alexander Münch", +	"micrococo", +	"yolanteng0", +	"Yunji Lee", +	"Hugo Carvalho", +	"Paulo Giovanni pereira", +	"Sergey Rodin", +	"Sabri Ünal", +	"yetist", +        "", +	"Artwork by:", +        "Jakub Szypulka", +        "Tango Project", +        "The GNOME Project", +        "epicbard", +        "Roundicons", +        NULL +    }; + +    about = gtk_about_dialog_new(); +    gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(shell->window)); + +#if GTK_CHECK_VERSION(2, 12, 0) +    gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), "Hardinfo2"); +#else +    gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "Hardinfo2"); +#endif + +    copyright = g_strdup_printf("Copyright \302\251 2003-2023 L. A. F. Pereira\nCopyright \302\251 2024-%d Hardinfo2 Project\n\n\n\n", HARDINFO2_COPYRIGHT_LATEST_YEAR); + +    gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION); +    gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), copyright); +    gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), +				  _("System Information and Benchmark")); +    gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), +			      icon_cache_get_pixbuf("hardinfo2.png")); + +    gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), +				 _("HardInfo2 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, version 2 or later.\n\n" +				 "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.\n\n" +				 "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 St, Fifth Floor, Boston, MA  02110-1301 USA")); +    gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(about), TRUE); + +    gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors); +    //gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about), artists); +    //gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), _("translator-credits")); + +    gtk_dialog_run(GTK_DIALOG(about)); +    gtk_widget_destroy(about); + +    g_free(copyright); +} + +void cb_generate_report() +{ +    Shell *shell = shell_get_main_shell(); +    gboolean btn_refresh = shell_action_get_enabled("RefreshAction"); +    gboolean btn_copy = shell_action_get_enabled("CopyAction"); + +    report_dialog_show(shell->tree->model, shell->window); + +    shell_action_set_enabled("RefreshAction", btn_refresh); +    shell_action_set_enabled("CopyAction", btn_copy); +} + +void cb_quit(void) +{ +    do { +	gtk_main_quit(); +    } while (gtk_main_level() > 1); + +    exit(0); +} diff --git a/shell/iconcache.c b/shell/iconcache.c new file mode 100644 index 00000000..8a3e6e13 --- /dev/null +++ b/shell/iconcache.c @@ -0,0 +1,100 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + *    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, version 2 or later. + * + *    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 St, Fifth Floor, Boston, MA  02110-1301 USA + */ +#include <iconcache.h> +#include <config.h> +#include <hardinfo.h> + +static GHashTable *cache = NULL; + +void icon_cache_init(void) +{ +    if (!cache) { +        DEBUG("initializing icon cache"); +	cache = g_hash_table_new(g_str_hash, g_str_equal); +    } +} + +GdkPixbuf *icon_cache_get_pixbuf(const gchar * file) +{ +    GdkPixbuf *icon; + +    if (!cache) +	icon_cache_init(); + +    icon = g_hash_table_lookup(cache, file); + +    if (!icon) { +	gchar *path; + +	path = g_build_filename(params.path_data, "pixmaps", file, NULL); +	icon = gdk_pixbuf_new_from_file(path, NULL); +	g_hash_table_insert(cache, g_strdup(file), icon); + +	g_free(path); +    } + +    if (icon) { +      g_object_ref(icon); +    } + +    return icon; +} + +GtkWidget *icon_cache_get_image(const gchar * file) +{ +    GdkPixbuf *icon; + +    icon = icon_cache_get_pixbuf(file); +    return gtk_image_new_from_pixbuf(icon); +} + +GdkPixbuf *icon_cache_get_pixbuf_at_size(const gchar * file, gint wid, +					 gint hei) +{ +    GdkPixbuf *icon; + +    if (!cache) +	icon_cache_init(); + +    icon = g_hash_table_lookup(cache, file); + +    if (!icon) { +	gchar *path; + +	path = g_build_filename(params.path_data, "pixmaps", file, NULL); +	icon = gdk_pixbuf_new_from_file_at_size(path, wid, hei, NULL); +	g_hash_table_insert(cache, g_strdup(file), icon); + +	g_free(path); +    } + +    if (icon) { +      g_object_ref(icon); +    } + +    return icon; +} + +GtkWidget *icon_cache_get_image_at_size(const gchar * file, gint wid, +					gint hei) +{ +    GdkPixbuf *icon; + +    icon = icon_cache_get_pixbuf_at_size(file, wid, hei); +    return gtk_image_new_from_pixbuf(icon); +} diff --git a/shell/loadgraph-uber.c b/shell/loadgraph-uber.c new file mode 100644 index 00000000..08f1e2b5 --- /dev/null +++ b/shell/loadgraph-uber.c @@ -0,0 +1,131 @@ +/* + * Christian Hergert's uber-graph (GPL3) + * wrapped in an interface compatible with + * L. A. F. Pereira's loadgraph (GPL2.1). + */ + +#include <string.h> +#include "loadgraph.h" +#include "uber.h" + +#define LG_MAX_LINES 9 + +static const gchar *default_colors[] = { "#73d216", +                                         "#f57900", +     /*colors from simple.c sample */    "#3465a4", +                                         "#ef2929", +                                         "#75507b", +                                         "#ce5c00", +                                         "#c17d11", +                                         "#ce5c00", +                                         "#729fcf", +                                         NULL }; + +struct _LoadGraph { +    GtkWidget *uber_widget; +    gdouble cur_value[LG_MAX_LINES]; +    gint height; +}; + +gdouble +_sample_func (UberLineGraph *graph, +                 guint      line, +                 gpointer   user_data) +{ +    LoadGraph *lg = (LoadGraph *)user_data; +    return lg->cur_value[line-1]; +} + +LoadGraph *load_graph_new(gint size) +{ +    LoadGraph *lg; +    GdkRGBA color; +    int i = 0; + +    lg = g_new0(LoadGraph, 1); +    lg->uber_widget = uber_line_graph_new(); +    lg->height = (size+1) * 2; /* idk */ +    for (i = 0; i < LG_MAX_LINES; i++) { +        lg->cur_value[i] = UBER_LINE_GRAPH_NO_VALUE; +        //GtkWidget *label = uber_label_new(); +        //uber_label_set_text(UBER_LABEL(label), "BLAH!"); +        gdk_rgba_parse(&color, default_colors[i]); +        uber_line_graph_add_line(UBER_LINE_GRAPH(lg->uber_widget), &color, NULL); /* UBER_LABEL(label) */ +    } +    uber_line_graph_set_autoscale(UBER_LINE_GRAPH(lg->uber_widget), TRUE); +    uber_line_graph_set_data_func(UBER_LINE_GRAPH(lg->uber_widget), +                (UberLineGraphFunc)_sample_func, (gpointer *)lg, NULL); +    return lg; +} + +void load_graph_set_data_suffix(LoadGraph * lg, gchar * suffix) +{ + +} + +gchar *load_graph_get_data_suffix(LoadGraph * lg) +{ +    return strdup(""); +} + +GtkWidget *load_graph_get_framed(LoadGraph * lg) +{ +    if (lg != NULL) +        return lg->uber_widget; +    return NULL; +} + +void load_graph_set_title(LoadGraph * lg, const gchar *title) +{ +} + +void load_graph_clear(LoadGraph * lg) +{ +    int i; +    if (lg != NULL) { +        for (i = 0; i < LG_MAX_LINES; i++) { +            lg->cur_value[i] = UBER_LINE_GRAPH_NO_VALUE; +        } +        uber_graph_scale_changed(UBER_GRAPH(lg->uber_widget)); +    } +} + +void load_graph_set_color(LoadGraph * lg, LoadGraphColor color) +{ + +} + +void load_graph_destroy(LoadGraph * lg) +{ +    if (lg != NULL) { +        g_object_unref(lg->uber_widget); +        g_free(lg); +    } +} + +static gboolean _expose(GtkWidget * widget, GdkEventExpose * event, gpointer user_data) +{ +    return TRUE; +} + +void load_graph_configure_expose(LoadGraph * lg) +{ + +} + +void load_graph_update_ex(LoadGraph *lg, guint line, gdouble value) +{ +    if (lg != NULL && line < LG_MAX_LINES) +        lg->cur_value[line] = value; +} + +void load_graph_update(LoadGraph * lg, gdouble value) +{ +    load_graph_update_ex(lg, 0, value); +} + +gint load_graph_get_height(LoadGraph *lg) { +    if (lg != NULL) +        return lg->height; +    return 0; +} diff --git a/shell/loadgraph.c b/shell/loadgraph.c new file mode 100644 index 00000000..0290d1f1 --- /dev/null +++ b/shell/loadgraph.c @@ -0,0 +1,331 @@ +/* + * Simple Load Graph + * Version 0.1 - Wed, Jan 11 2006 + *   - initial release + * Version 0.1.1 - Fri, Jan 13 2006 + *   - fixes autoscaling + *   - add color + * + * Copyright (C) 2006 L. A. F. Pereira <l@tia.mat.br> + * + * The Simple Load Graph is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * The Simple Load Graph 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the Simple Load Graph; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + */ + +#include "loadgraph.h" + +struct _LoadGraph { +    GdkPixmap     *buf; +    GdkGC         *grid; +    GdkGC         *trace; +    GdkGC         *fill; +    GtkWidget     *area; + +    gint          *data; +    gfloat         scale; + +    gint       size; +    gint       width, height; +    LoadGraphColor color; + +    gint       max_value, remax_count; + +    PangoLayout   *layout; +    gchar     *suffix; +    gchar *title; +}; + +static void _draw(LoadGraph * lg); + +LoadGraph *load_graph_new(gint size) +{ +    LoadGraph *lg; + +    lg = g_new0(LoadGraph, 1); + +    size++; + +    lg->suffix = g_strdup(""); +    lg->title = g_strdup(""); +    lg->area = gtk_drawing_area_new(); +    lg->size = (size * 3) / 2; +    lg->data = g_new0(gint, lg->size); + +    lg->scale = 1.0; + +    lg->width = size * 6; +    lg->height = size * 2; + +    lg->max_value = 1; +    lg->remax_count = 0; + +    lg->layout = pango_layout_new(gtk_widget_get_pango_context(lg->area)); + +    gtk_widget_set_size_request(lg->area, lg->width, lg->height); +    gtk_widget_show(lg->area); + +    return lg; +} + +void load_graph_set_data_suffix(LoadGraph * lg, gchar * suffix) +{ +    g_free(lg->suffix); +    lg->suffix = g_strdup(suffix); +} + +gchar *load_graph_get_data_suffix(LoadGraph * lg) +{ +    return lg->suffix; +} + +void load_graph_set_title(LoadGraph * lg, const gchar * title) +{ +    g_free(lg->title); +    lg->title = g_strdup(title); +} + +const gchar *load_graph_get_title(LoadGraph *lg) +{ +    if (lg != NULL) return lg->title; +    return NULL; +} + +GtkWidget *load_graph_get_framed(LoadGraph * lg) +{ +    GtkWidget *align, *frame; + +    align = gtk_alignment_new(0.5, 0.5, 0, 0); +    gtk_widget_show(align); + +    frame = gtk_frame_new(NULL); +    gtk_widget_show(frame); +    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); + +    gtk_container_add(GTK_CONTAINER(align), frame); +    gtk_container_add(GTK_CONTAINER(frame), lg->area); + +    return align; +} + +void load_graph_clear(LoadGraph * lg) +{ +    gint i; + +    for (i = 0; i < lg->size; i++) +        lg->data[i] = 0; + +    lg->scale = 1.0; +    lg->max_value = 1; +    lg->remax_count = 0; + +    load_graph_set_title(lg, ""); + +    _draw(lg); +} + +void load_graph_set_color(LoadGraph * lg, LoadGraphColor color) +{ +    lg->color = color; +    gdk_rgb_gc_set_foreground(lg->trace, lg->color); +    gdk_rgb_gc_set_foreground(lg->fill, lg->color - 0x303030); +    gdk_rgb_gc_set_foreground(lg->grid, lg->color - 0xcdcdcd); +} + +void load_graph_destroy(LoadGraph * lg) +{ +    g_free(lg->data); +    gtk_widget_destroy(lg->area); +    gdk_pixmap_unref(lg->buf); +    g_object_unref(lg->trace); +    g_object_unref(lg->grid); +    g_object_unref(lg->fill); +    g_object_unref(lg->layout); +    g_free(lg); +} + +static gboolean _expose(GtkWidget * widget, GdkEventExpose * event, +            gpointer user_data) +{ +    LoadGraph *lg = (LoadGraph *) user_data; +    GdkDrawable *draw = GDK_DRAWABLE(lg->buf); + +    gdk_draw_drawable(lg->area->window, +              lg->area->style->black_gc, +              draw, 0, 0, 0, 0, lg->width, lg->height); +    return FALSE; +} + +void load_graph_configure_expose(LoadGraph * lg) +{ +    /* creates the backing store pixmap */ +    gtk_widget_realize(lg->area); +    lg->buf = gdk_pixmap_new(lg->area->window, lg->width, lg->height, -1); + +    /* create the graphic contexts */ +    lg->grid = gdk_gc_new(GDK_DRAWABLE(lg->buf)); +    lg->trace = gdk_gc_new(GDK_DRAWABLE(lg->buf)); +    lg->fill = gdk_gc_new(GDK_DRAWABLE(lg->buf)); + +    /* the default color is green */ +    load_graph_set_color(lg, LG_COLOR_GREEN); + +    /* init graphic contexts */ +    gdk_gc_set_line_attributes(lg->grid, +                   1, GDK_LINE_ON_OFF_DASH, +                   GDK_CAP_NOT_LAST, GDK_JOIN_ROUND); +    gdk_gc_set_dashes(lg->grid, 0, (gint8*)"\2\2", 2); + +    gdk_gc_set_line_attributes(lg->trace, +                   1, GDK_LINE_SOLID, +                   GDK_CAP_PROJECTING, GDK_JOIN_ROUND); + +    /* configures the expose event */ +    g_signal_connect(G_OBJECT(lg->area), "expose-event", +             (GCallback) _expose, lg); +} + +static void _draw_title(LoadGraph * lg, const char* title) { +    gchar *tmp = g_strdup_printf("<span size=\"x-small\">%s</span>", title); +    pango_layout_set_markup(lg->layout, tmp, -1); +    int width = 0; +    int height = 0; +    pango_layout_get_pixel_size(lg->layout, &width, &height); +    gint position = (lg->width / 2) - (width / 2); +    gdk_draw_layout(GDK_DRAWABLE(lg->buf), lg->trace, position, 2, +                    lg->layout); +    g_free(tmp); +} + +static void _draw_label_and_line(LoadGraph * lg, gint position, gint value) +{ +    gchar *tmp; + +    /* draw lines */ +    if (position > 0) +        gdk_draw_line(GDK_DRAWABLE(lg->buf), lg->grid, 0, position, +                  lg->width, position); +    else +        position = -1 * position; + +    /* draw label */ +    tmp = +    g_strdup_printf("<span size=\"x-small\">%d%s</span>", value, +            lg->suffix); + +    pango_layout_set_markup(lg->layout, tmp, -1); +    pango_layout_set_width(lg->layout, +               lg->area->allocation.width * PANGO_SCALE); +    gdk_draw_layout(GDK_DRAWABLE(lg->buf), lg->trace, 2, position, +            lg->layout); + +    g_free(tmp); +} + +static void _draw(LoadGraph * lg) +{ +    GdkDrawable *draw = GDK_DRAWABLE(lg->buf); +    gint i, d; + +    /* clears the drawing area */ +    gdk_draw_rectangle(draw, lg->area->style->black_gc, +               TRUE, 0, 0, lg->width, lg->height); + + +    /* the graph */ +    GdkPoint *points = g_new0(GdkPoint, lg->size + 1); + +    for (i = 0; i < lg->size; i++) { +        points[i].x = i * 4; +        points[i].y = lg->height - lg->data[i] * lg->scale; +    } + +    points[0].x = points[1].x = 0; +    points[0].y = points[i].y = lg->height; +    points[i].x = points[i - 1].x = lg->width; + +    gdk_draw_polygon(draw, lg->fill, TRUE, points, lg->size + 1); +    gdk_draw_polygon(draw, lg->trace, FALSE, points, lg->size + 1); + +    g_free(points); + +    /* vertical bars */ +    for (i = lg->width, d = 0; i > 1; i--, d++) +        if ((d % 45) == 0 && d) +            gdk_draw_line(draw, lg->grid, i, 0, i, lg->height); + +    /* horizontal bars and labels; 25%, 50% and 75% */ +    _draw_label_and_line(lg, -1, lg->max_value); +    _draw_label_and_line(lg, lg->height / 4, 3 * (lg->max_value / 4)); +    _draw_label_and_line(lg, lg->height / 2, lg->max_value / 2); +    _draw_label_and_line(lg, 3 * (lg->height / 4), lg->max_value / 4); + +    /* graph title */ +    _draw_title(lg, lg->title); + +    gtk_widget_queue_draw(lg->area); +} + +void load_graph_update_ex(LoadGraph *lg, guint line, gdouble value) +{ +    /* not implemented */ +    if (line == 0) +        load_graph_update(lg, value); +} + +void load_graph_update(LoadGraph * lg, gdouble v) +{ +    gint i; +    gint value = (gint)v; + +    if (value < 0) +        return; + +    /* shift-right our data */ +    for (i = 0; i < lg->size - 1; i++) { +        lg->data[i] = lg->data[i + 1]; +    } + +    /* insert the updated value */ +    lg->data[i] = value; + +    /* calculates the maximum value */ +    if (lg->remax_count++ > 20) { +        /* only finds the maximum amongst the data every 20 times */ +        lg->remax_count = 0; + +        gint max = lg->data[0]; +        for (i = 1; i < lg->size; i++) { +            if (lg->data[i] > max) +            max = lg->data[i]; +        } + +        lg->max_value = max; +    } else { +        /* otherwise, select the maximum between the current maximum +           and the supplied value */ +        lg->max_value = MAX(value, lg->max_value); +    } + +    /* recalculates the scale; always use 90% of it */ +    lg->scale = 0.90 * ((gfloat) lg->height / (gfloat) lg->max_value); + +    /* redraw */ +    _draw(lg); +} + +gint load_graph_get_height(LoadGraph *lg) { +    if (lg != NULL) +        return lg->height; +    return 0; +} diff --git a/shell/menu.c b/shell/menu.c new file mode 100644 index 00000000..83bd8144 --- /dev/null +++ b/shell/menu.c @@ -0,0 +1,178 @@ +/* + * HardInfo + * Copyright(C) 2003-2007 L. A. F. Pereira. + * + * menu.c is based on UI Manager tutorial by Ryan McDougall + * Copyright(C) 2005 Ryan McDougall. + * + * 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, version 2 or later. + * + * 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <menu.h> +#include <config.h> + +#include <stock.h> + +#include <callbacks.h> +#include <hardinfo.h> + + +#include "uidefs.h" + +#ifndef GTK_STOCK_COPY +#define GTK_STOCK_COPY "_Copy" +#endif + +#ifndef GTK_STOCK_REFRESH +#define GTK_STOCK_REFRESH "_Refresh" +#endif + +static GtkActionEntry entries[] = { +    {"InformationMenuAction", NULL, N_("_Information")},	/* name, stock id, label */ +    //    {"RemoteMenuAction", NULL, N_("_Remote")}, +    {"ViewMenuAction", NULL, N_("_View")}, +    {"HelpMenuAction", NULL, N_("_Help")}, +    //    {"HelpMenuModulesAction", HI_STOCK_ABOUT_MODULES, N_("About _Modules")}, +    {"MainMenuBarAction", NULL, ""}, + +    {"ReportAction", HI_STOCK_REPORT,	/* name, stock id */ +     N_("Generate _Report"), "<control>R",	/* label, accelerator */ +     N_("Generates a report with detailed system information"),			/* tooltip */ +     G_CALLBACK(cb_generate_report)}, + +    {"SyncManagerAction", HI_STOCK_SYNC_MENU, +     N_("Synchronize"), NULL, +     N_("Send benchmark results and receive updated data from the network"), +     G_CALLBACK(cb_sync_manager)}, + +    {"OpenAction", "_Open", +     N_("_Open..."), NULL, +     NULL, +     G_CALLBACK(cb_sync_manager)}, + +    {"CopyAction", HI_STOCK_CLIPBOARD, +     N_("_Copy to Clipboard"), "<control>C", +     N_("Copy to clipboard"), +     G_CALLBACK(cb_copy_to_clipboard)}, + +    {"RefreshAction", HI_STOCK_REFRESH, +     N_("_Refresh"), "F5", +     NULL, +     G_CALLBACK(cb_refresh)}, + +    {"HomePageAction", HI_STOCK_INTERNET, +     N_("_Open HardInfo2 Web Site"), NULL, +     NULL, +     G_CALLBACK(cb_open_web_page)}, + +    {"ReportBugAction", HI_STOCK_INTERNET, +     N_("_Report bug"), NULL, +     NULL, +     G_CALLBACK(cb_report_bug)}, + +    {"AboutAction", "_About", +     N_("_About HardInfo2"), NULL, +     N_("Displays program version information"), +     G_CALLBACK(cb_about)}, + +    {"QuitAction", "_Quit", +     N_("_Quit"), "<control>Q", +     NULL, +     G_CALLBACK(cb_quit)} +}; + +static GtkToggleActionEntry toggle_entries[] = { +    {"SidePaneAction", NULL, +     N_("_Side Pane"), NULL, +     N_("Toggles side pane visibility"), +     G_CALLBACK(cb_side_pane)}, +    {"ToolbarAction", NULL, +     N_("_Toolbar"), NULL, +     NULL, +     G_CALLBACK(cb_toolbar)}, +    {"SyncOnStartupAction", NULL, +     N_("Synchronize on startup"), NULL, +     NULL, +     G_CALLBACK(cb_sync_on_startup)}, +}; + +/* Implement a handler for GtkUIManager's "add_widget" signal. The UI manager + * will emit this signal whenever it needs you to place a new widget it has. */ +static void +menu_add_widget(GtkUIManager * ui, GtkWidget * widget, +		GtkContainer * container) +{ +    gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0); +    gtk_widget_show(widget); +} + +void menu_init(Shell * shell) +{ +    GtkWidget *menu_box;	/* Packing box for the menu and toolbars */ +    GtkActionGroup *action_group;	/* Packing group for our Actions */ +    GtkUIManager *menu_manager;	/* The magic widget! */ +    GError *error;		/* For reporting exceptions or errors */ +    GtkAccelGroup *accel_group; + +    /* Create our objects */ +    menu_box = shell->vbox; +    action_group = gtk_action_group_new("HardInfo2"); +    menu_manager = gtk_ui_manager_new(); + +    shell->action_group = action_group; +    shell->ui_manager = menu_manager; + +    /* Pack up our objects: +     * menu_box -> window +     * actions -> action_group +     * action_group -> menu_manager */ +    gtk_action_group_set_translation_domain( action_group, "hardinfo2" );//gettext +    gtk_action_group_add_actions(action_group, entries, +				 G_N_ELEMENTS(entries), NULL); +    gtk_action_group_add_toggle_actions(action_group, toggle_entries, +					G_N_ELEMENTS(toggle_entries), +					NULL); +    gtk_ui_manager_insert_action_group(menu_manager, action_group, 0); + + +    /* Read in the UI from our XML file */ +    error = NULL; +    gtk_ui_manager_add_ui_from_string(menu_manager, uidefs_str, -1, +				      &error); + +    if (error) { +	g_error("Building menus failed: %s", error->message); +	g_error_free(error); +	return; +    } + +    /* Enable menu accelerators */ +    accel_group = gtk_ui_manager_get_accel_group(menu_manager); +    gtk_window_add_accel_group(GTK_WINDOW(shell->window), accel_group); + +    /* Connect up important signals */ +    /* This signal is necessary in order to place widgets from the UI manager +     * into the menu_box */ +    g_signal_connect(menu_manager, "add_widget", +		     G_CALLBACK(menu_add_widget), menu_box); + +    /* Show the window and run the main loop, we're done! */ +    gtk_widget_show(menu_box); + +    gtk_toolbar_set_style(GTK_TOOLBAR +			  (gtk_ui_manager_get_widget +			   (shell->ui_manager, "/MainMenuBarAction")), +			  GTK_TOOLBAR_BOTH_HORIZ); +} diff --git a/shell/report.c b/shell/report.c new file mode 100644 index 00000000..25b73beb --- /dev/null +++ b/shell/report.c @@ -0,0 +1,1284 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2008 L. A. F. Pereira <l@tia.mat.br> + * + *    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, version 2 or later. + * + *    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 St, Fifth Floor, Boston, MA  02110-1301 USA + */ + +#include <report.h> +#include <stdio.h> +#include <string.h> +#include <shell.h> +#include <iconcache.h> +#include <hardinfo.h> +#include <config.h> +#include "uri_handler.h" + +static ReportDialog *report_dialog_new(GtkTreeModel * model, +				       GtkWidget * parent); +static void set_all_active(ReportDialog * rd, gboolean setting); + +static FileTypes file_types[] = { +    {"HTML (*.html)", "text/html", ".html", report_context_html_new}, +    {"Plain Text (*.txt)", "text/plain", ".txt", report_context_text_new}, +    {"Shell Dump (*.txt)", "text/plain", ".txt", report_context_shell_new}, +    {NULL, NULL, NULL, NULL} +}; + +/* virtual functions */ +void report_header(ReportContext * ctx) +{ ctx->header(ctx); } + +void report_footer(ReportContext * ctx) +{ ctx->footer(ctx); } + +void report_title(ReportContext * ctx, gchar * text) +{ ctx->title(ctx, text); } + +void report_subtitle(ReportContext * ctx, gchar * text) +{ ctx->subtitle(ctx, text); } + +void report_subsubtitle(ReportContext * ctx, gchar * text) +{ ctx->subsubtitle(ctx, text); } + +void report_key_value(ReportContext * ctx, gchar *key, gchar *value, gsize longest_key) +{ ctx->keyvalue(ctx, key, value, longest_key); } + +void report_details_start(ReportContext *ctx, gchar *key, gchar *value, gsize longest_key) +{ ctx->details_start(ctx, key, value, longest_key); } + +void report_details_section(ReportContext *ctx, gchar *label) +{ ctx->details_section(ctx, label); } + +void report_details_keyvalue(ReportContext *ctx, gchar *key, gchar *value, gsize longest_key) +{ ctx->details_keyvalue(ctx, key, value, longest_key); } + +void report_details_end(ReportContext *ctx) +{ ctx->details_end(ctx); } +/* end virtual functions */ + +gint report_get_visible_columns(ReportContext *ctx) +{ +    gint columns; + +    /* Column count starts at two, since we always have at least +       two columns visible. */ +    columns = 2; + +    /* Either the Progress column or the Value column is available at +       the same time. So we don't count them. */ + +    if (ctx->columns & REPORT_COL_EXTRA1) +      columns++; + +    if (ctx->columns & REPORT_COL_EXTRA2) +      columns++; + +    return columns; +} + +gchar *icon_name_css_id(const gchar *file) { +    gchar *safe = g_strdup_printf("icon-%s", file); +    gchar *p = safe; +    while(*p) { +        if (!isalnum(*p)) +            *p = '-'; +        p++; +    } +    return safe; +} + +gchar *make_icon_css(const gchar *file) { +    if (!file || *file == 0) +        return g_strdup(""); +    gchar *ret = NULL; +    gchar *path = g_build_filename(params.path_data, "pixmaps", file, NULL); +    gchar *contents = NULL; +    gsize length = 0; +    if ( g_file_get_contents(path, &contents, &length, NULL) ) { +        gchar *css_class = icon_name_css_id(file); +        const char *ctype = "image/png"; +        if (g_str_has_suffix(file, ".svg") ) +            ctype = "image/svg+xml"; +        gchar *b64data = g_base64_encode(contents, length); +        ret = g_strdup_printf( +            ".%s\n" +            "{ background: url(data:%s;base64,%s) no-repeat;\n" +            "  background-size: cover; }\n", +            css_class ? css_class : "", +            ctype, b64data ); +        g_free(b64data); +        g_free(css_class); +    } +    g_free(contents); +    g_free(path); +    return ret ? ret : g_strdup(""); +} + +void cache_icon(ReportContext *ctx, const gchar *file) { +    if (!ctx->icon_data) return; +    if (!g_hash_table_lookup(ctx->icon_data, file) ) +        g_hash_table_insert(ctx->icon_data, g_strdup(file), make_icon_css(file)); +} + +void report_context_configure(ReportContext * ctx, GKeyFile * keyfile) +{ +    gchar **keys; +    const gchar *group = "$ShellParam$"; + +    if (ctx->icon_refs) { +        g_hash_table_remove_all(ctx->icon_refs); +        ctx->icon_refs = NULL; +    } +    ctx->icon_refs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + +    keys = g_key_file_get_keys(keyfile, group, NULL, NULL); +    if (keys) { +      gint i = 0; + +      for (; keys[i]; i++) { +        gchar *key = keys[i]; + +        if (g_str_equal(key, "ShowColumnHeaders")) { +          ctx->show_column_headers = g_key_file_get_boolean(keyfile, group, key, NULL); +        } else if (g_str_has_prefix(key, "ColumnTitle")) { +          gchar *value, *title = strchr(key, '$'); + +          if (!title) { +                  DEBUG("couldn't find column title"); +                  break; +          } +          title++; +          if (!*title) { +                  DEBUG("title is empty"); +                  break; +          } + +          value = g_key_file_get_value(keyfile, group, key, NULL); +          if (g_str_equal(title, "Extra1")) { +                  ctx->columns |= REPORT_COL_EXTRA1; +          } else if (g_str_equal(title, "Extra2")) { +                  ctx->columns |= REPORT_COL_EXTRA2; +          } else if (g_str_equal(title, "Value")) { +                  ctx->columns |= REPORT_COL_VALUE; +          } else if (g_str_equal(title, "TextValue")) { +                  ctx->columns |= REPORT_COL_TEXTVALUE; +          } else if (g_str_equal(title, "Progress")) { +                  ctx->columns |= REPORT_COL_PROGRESS; +          } + +          g_hash_table_replace(ctx->column_titles, +                               g_strdup(title), g_strdup(value)); +        } else if (g_str_equal(key, "ViewType")) { +          if (g_key_file_get_integer(keyfile, group, "ViewType", NULL) == SHELL_VIEW_PROGRESS) { +            ctx->columns &= ~REPORT_COL_VALUE; +            ctx->columns |= REPORT_COL_PROGRESS; +          } +        } else if (g_str_has_prefix(key, "Icon$")) { +            gchar *ikey = g_utf8_strchr(key, -1, '$'); +            gchar *tag = key_mi_tag(ikey); +            gchar *icon = g_key_file_get_value(keyfile, group, key, NULL); +            cache_icon(ctx, icon); +            g_hash_table_insert(ctx->icon_refs, tag, icon); +        } +      } + +      g_strfreev(keys); +    } + +} + +static void report_html_details_start(ReportContext *ctx, gchar *key, gchar *value, gsize longest_key) { +    guint cols = report_get_visible_columns(ctx); +    report_key_value(ctx, key, value, longest_key); +    ctx->parent_columns = ctx->columns; +    ctx->columns = REPORT_COL_VALUE; +    ctx->output = h_strdup_cprintf("<tr><td colspan=\"%d\"><table class=\"details\">\n", ctx->output, cols); +} + +static void report_html_details_end(ReportContext *ctx) { +    ctx->output = h_strdup_cprintf("</table></td></tr>\n", ctx->output); +    ctx->columns = ctx->parent_columns; +    ctx->parent_columns = 0; +} + +void report_details(ReportContext *ctx, gchar *key, gchar *value, gchar *details, gsize longest_key) +{ +    GKeyFile *key_file = g_key_file_new(); +    gchar **groups; +    gint i; + +    report_details_start(ctx, key, value, longest_key); +    ctx->in_details = TRUE; + +    g_key_file_load_from_data(key_file, details, strlen(details), 0, NULL); +    groups = g_key_file_get_groups(key_file, NULL); + +    for (i = 0; groups[i]; i++) { +        gchar *group, *tmpgroup; +        gchar **keys; +        gint j; + +        if (groups[i][0] == '$') { +            continue; +        } + +        group = groups[i]; + +        tmpgroup = g_strdup(group); +        strend(group, '#'); + +        report_subsubtitle(ctx, group); + +        keys = g_key_file_get_keys(key_file, tmpgroup, NULL, NULL); + +        gsize longest_key = 0; +        for (j = 0; keys[j]; j++) { +            gchar *lbl; +            key_get_components(keys[j], NULL, NULL, NULL, &lbl, NULL, TRUE); +            longest_key = MAX(longest_key, strlen(lbl)); +            g_free(lbl); +        } + +        for (j = 0; keys[j]; j++) { +            gchar *key = keys[j]; +            gchar *raw_value, *value; + +            raw_value = g_key_file_get_value(key_file, tmpgroup, key, NULL); +            value = g_strcompress(raw_value); /* un-escape \n, \t, etc */ + +            if (g_utf8_validate(key, -1, NULL) && g_utf8_validate(value, -1, NULL)) { +                strend(key, '#'); + +                if (g_str_equal(value, "...")) { +                    g_free(value); +                    if (!(value = ctx->entry->fieldfunc(key))) { +                        value = g_strdup("..."); +                    } +                } + +                report_key_value(ctx, key, value, longest_key); + +            } + +            g_free(value); +            g_free(raw_value); +        } + +        g_strfreev(keys); +        g_free(tmpgroup); +    } + +    g_strfreev(groups); +    g_key_file_free(key_file); + +    ctx->in_details = FALSE; +    report_details_end(ctx); +} + +static void report_table_shell_dump(ReportContext *ctx, gchar *key_file_str, int level) +{ +    gchar *text, *p, *next_nl, *eq, *indent; +    gchar *key, *value; + +    indent = g_strnfill(level * 4, ' '); + +    if (key_file_str) { +        p = text = g_strdup(key_file_str); +        while(next_nl = strchr(p, '\n')) { +            *next_nl = 0; +            eq = strchr(p, '='); +            if (*p != '[' && eq) { +                *eq = 0; +                key = p; value = eq + 1; + +                ctx->output = h_strdup_cprintf("%s%s=%s\n", ctx->output, indent, key, value); +                if (key_wants_details(key) || params.force_all_details) { +                    gchar *mi_tag = key_mi_tag(key); +                    gchar *mi_data = ctx->entry->morefunc(mi_tag); /*const*/ + +                    if (mi_data) +                        report_table_shell_dump(ctx, mi_data, level + 1); + +                    g_free(mi_tag); +                } + +            } else +                ctx->output = h_strdup_cprintf("%s%s\n", ctx->output, indent, p); +            p = next_nl + 1; +        } +    } +    g_free(text); +    g_free(indent); +    return; +} + +void report_table(ReportContext * ctx, gchar * text) +{ +    GKeyFile *key_file = NULL; +    gchar **groups; +    gint i; + +    if (ctx->format == REPORT_FORMAT_SHELL) { +        report_table_shell_dump(ctx, text, 0); +        return; +    } + +    key_file = g_key_file_new(); + +    /* make only "Value" column visible ("Key" column is always visible) */ +    ctx->columns = REPORT_COL_VALUE; +    ctx->show_column_headers = FALSE; + +    /**/ +    g_key_file_load_from_data(key_file, text, strlen(text), 0, NULL); +    groups = g_key_file_get_groups(key_file, NULL); + +    for (i = 0; groups[i]; i++) { +        if (groups[i][0] == '$') { +            report_context_configure(ctx, key_file); +            break; +        } +    } + +    for (i = 0; groups[i]; i++) { +        gchar *group, *tmpgroup; +        gchar **keys; +        gint j; + +        if (groups[i][0] == '$') { +            continue; +        } + +        group = groups[i]; + +        tmpgroup = g_strdup(group); +        strend(group, '#'); + +        report_subsubtitle(ctx, group); + +        keys = g_key_file_get_keys(key_file, tmpgroup, NULL, NULL); + +        gsize longest_key = 0; +        for (j = 0; keys[j]; j++) { +            gchar *lbl; +            key_get_components(keys[j], NULL, NULL, NULL, &lbl, NULL, TRUE); +            longest_key = MAX(longest_key, strlen(lbl)); +            g_free(lbl); +        } + +        for (j = 0; keys[j]; j++) { +            gchar *key = keys[j]; +            gchar *raw_value, *value; + +            raw_value = g_key_file_get_value(key_file, tmpgroup, key, NULL); +            value = g_strcompress(raw_value); /* un-escape \n, \t, etc */ + +            if (g_utf8_validate(key, -1, NULL) && g_utf8_validate(value, -1, NULL)) { +                strend(key, '#'); + +                if (g_str_equal(value, "...")) { +                    g_free(value); +                    if (!(value = ctx->entry->fieldfunc(key))) { +                        value = g_strdup("..."); +                    } +                } + +                if ( key_is_flagged(key) ) { +                    gchar *mi_tag = key_mi_tag(key); +                    gchar *mi_data = NULL; /*const*/ + +                    if (key_wants_details(key) || params.force_all_details) +                        mi_data = ctx->entry->morefunc(mi_tag); + +                    if (mi_data) +                        report_details(ctx, key, value, mi_data, longest_key); +                    else +                        report_key_value(ctx, key, value, longest_key); + +                    g_free(mi_tag); +                } else { +                    report_key_value(ctx, key, value, longest_key); +                } + +            } + +            g_free(value); +            g_free(raw_value); +        } + +        g_strfreev(keys); + +        g_free(tmpgroup); +    } + +    g_strfreev(groups); +    g_key_file_free(key_file); +} + +static void report_html_header(ReportContext * ctx) +{ +    g_free(ctx->output); + +    ctx->output = +	g_strdup_printf +	("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Final//EN\">\n" +	 "<html><head>\n" "<title>HardInfo (%s) System Report</title>\n" +	 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n" +	 "<style>\n" "    body    { background: #fff }\n" +	 "    .title  { font: bold 130%% serif; color: #0066FF; padding: 30px 0 10px 0 }\n" +	 "    .stitle { font: bold 100%% sans-serif; color: #0044DD; padding: 0 0 0 0; }\n" +	 "    .sstitle{ font: bold 80%% serif; color: #000000; background: #efefef }\n" +	 "    .field  { font: 80%% sans-serif; color: #000000; padding: 2px; }\n" +	 "    .value  { font: 80%% sans-serif; color: #505050 }\n" +	 "    .hilight  { font: bold 110%% sans-serif; color: #000000; background: #ffff66 }\n" +	 "    table.details { margin-left: 50px; }\n" +	 "    td.icon { width: 1.2em; padding-left: 1.2em; }\n" +	 "    td.icon img  { width: 1.2em; }\n" +	 "    td.icon div { display: block; box-sizing: border-box; -moz-box-sizing: border-box;\n" +	 "        width: 1.2em; height: 1.2em; background-position: right; }\n" +	 "    td.icon_subtitle div { display: block; box-sizing: border-box; -moz-box-sizing: border-box;\n" +	 "        width: 1.8em; height: 1.8em; background-position: right; }\n" +	 "</style>\n" "</head><body>\n", +	 VERSION); +} + +static void report_html_footer(ReportContext * ctx) +{ +    ctx->output = h_strconcat(ctx->output, "</table>", NULL); +    ctx->output = h_strconcat(ctx->output, "<style>\n", NULL); +    GList *l = NULL, *keys = g_hash_table_get_keys(ctx->icon_data); +    for(l = keys; l; l = l->next) { +        gchar *data = g_hash_table_lookup(ctx->icon_data, (gchar*)l->data); +        if (data) +            ctx->output = h_strconcat(ctx->output, data, NULL); +    } +    g_list_free(keys); +    ctx->output = h_strconcat(ctx->output, "</style>\n", NULL); +    ctx->output = h_strconcat(ctx->output, "</html>", NULL); +} + +static void report_html_title(ReportContext * ctx, gchar * text) +{ +    if (!ctx->first_table) { +      ctx->output = h_strdup_cprintf("</table>", ctx->output); +    } + +    ctx->output = h_strdup_cprintf("<h1 class=\"title\">%s</h1>", ctx->output, text); +} + +static void report_html_subtitle(ReportContext * ctx, gchar * text) +{ +    gint columns = report_get_visible_columns(ctx); + +    if (!ctx->first_table) { +      ctx->output = h_strdup_cprintf("</table>", ctx->output); +    } else { +      ctx->first_table = FALSE; +    } + +    gchar *icon = NULL; +    if (ctx->entry->icon_file) { +        gchar *icon_class = icon_name_css_id(ctx->entry->icon_file); +        icon = g_strdup_printf("<div class=\"%s\"></div>", icon_class); +        g_free(icon_class); +    } else { +        icon = g_strdup(""); +    } + +    ctx->output = h_strdup_cprintf("<table><tr><td class=\"icon_subtitle\">%s</td><td colspan=\"%d\" class=\"stit" +				  "le\">%s</td></tr>\n", +				  ctx->output, +                  icon, +				  columns, +				  text); +    g_free(icon); +} + +static void report_html_subsubtitle(ReportContext * ctx, gchar * text) +{ +    gint columns = report_get_visible_columns(ctx); + +    ctx->output = h_strdup_cprintf("<tr><td colspan=\"%d\" class=\"ssti" +				  "tle\">%s</td></tr>\n", +				  ctx->output, +				  columns+1, +				  text); +} + +static void +report_html_key_value(ReportContext * ctx, gchar *key, gchar *value, gsize longest_key) +{ +    gint columns = report_get_visible_columns(ctx); +    gchar **values; +    gint i, mc; + +    gboolean highlight = key_is_highlighted(key); +    gchar *tag = key_mi_tag(key); +    gchar *icon = tag ? (gchar*)g_hash_table_lookup(ctx->icon_refs, tag) : NULL; +    g_free(tag); +    /* icon from the table is const, so can be re-used without free */ +    if (icon) { +        gchar *icon_class = icon_name_css_id(icon); +        icon = g_strdup_printf("<div class=\"%s\"></div>", icon_class); +        g_free(icon_class); +    } else +        icon = g_strdup(""); + +    gchar *name = (gchar*)key_get_name(key); + +    if (columns == 2) { +      ctx->output = h_strdup_cprintf("<tr%s><td class=\"icon\">%s</td><td class=\"field\">%s</td>" +                                    "<td class=\"value\">%s</td></tr>\n", +                                    ctx->output, +                                    highlight ? " class=\"hilight\"" : "", +                                    icon, name, value); +    } else { +      values = g_strsplit(value, "|", columns); +      mc = g_strv_length(values) - 1; + +      ctx->output = h_strdup_cprintf("\n<tr%s>\n<td class=\"icon\">%s</td><td class=\"field\">%s</td>", ctx->output, highlight ? " class=\"hilight\"" : "", icon, name); + +      for (i = mc; i >= 0; i--) { +        ctx->output = h_strdup_cprintf("<td class=\"value\">%s</td>", +                                       ctx->output, +                                       values[i]); +      } + +      ctx->output = h_strdup_cprintf("</tr>\n", ctx->output); + +      g_strfreev(values); +    } +    g_free(icon); +} + +static void report_text_header(ReportContext * ctx) +{ +    g_free(ctx->output); + +    ctx->output = g_strdup(""); +} + +static void report_text_footer(ReportContext * ctx) +{ +} + +static void report_text_title(ReportContext * ctx, gchar * text) +{ +    gchar *str = (gchar *) ctx->output; +    int i = strlen(text); + +    str = h_strdup_cprintf("\n%s\n", str, text); +    for (; i; i--) +	str = h_strconcat(str, "*", NULL); + +    str = h_strconcat(str, "\n\n", NULL); +    ctx->output = str; +} + +static void report_text_subtitle(ReportContext * ctx, gchar * text) +{ +    gchar *str = ctx->output; +    int i = strlen(text); + +    str = h_strdup_cprintf("\n%s\n", str, text); +    for (; i; i--) +	str = h_strconcat(str, "-", NULL); + +    str = h_strconcat(str, "\n\n", NULL); +    ctx->output = str; +} + +static void report_text_subsubtitle(ReportContext * ctx, gchar * text) +{ +    gchar indent[10] = "   "; +    if (!ctx->in_details) +        indent[0] = 0; +    ctx->output = h_strdup_cprintf("%s-%s-\n", ctx->output, indent, text); +} + +static void +report_text_key_value(ReportContext * ctx, gchar *key, gchar *value, gsize longest_key) +{ +    gint columns = report_get_visible_columns(ctx); +    gchar **values; +    gint i, mc, field_width = MAX(10, longest_key); +    gchar indent[10] = "      "; +    if (!ctx->in_details) +        indent[0] = 0; +    gchar field_spacer[51]; +    for(i = 0; i < 49; i++) +        field_spacer[i] = ' '; +    field_width = MIN(50, field_width); +    field_spacer[field_width] = 0; + +    gboolean highlight = key_is_highlighted(key); +    gboolean multiline = (value && strlen(value) && strchr(value, '\n')); +    gchar *name = (gchar*)key_get_name(key); +    gchar *pf = g_strdup_printf("%s%s", indent, highlight ? "* " : "  "); +    gchar *rjname = g_strdup(field_spacer); +    if (strlen(name) > strlen(rjname)) +        name[strlen(rjname)] = 0; +    strcpy(rjname + strlen(rjname) - strlen(name), name); + +    if (columns == 2 || ctx->in_details) { +      if (strlen(value)) { +          if (multiline) { +              gchar **lines = g_strsplit(value, "\n", 0); +              for(i=0; lines[i]; i++) { +                  if (i == 0) +                      ctx->output = h_strdup_cprintf("%s%s : %s\n", ctx->output, pf, rjname, lines[i]); +                  else +                      ctx->output = h_strdup_cprintf("%s%s   %s\n", ctx->output, pf, field_spacer, lines[i]); +              } +              g_strfreev(lines); +          } else { +              ctx->output = h_strdup_cprintf("%s%s : %s\n", ctx->output, pf, rjname, value); +          } +      } else +          ctx->output = h_strdup_cprintf("%s%s\n", ctx->output, pf, rjname); +    } else { +      values = g_strsplit(value, "|", columns); +      mc = g_strv_length(values) - 1; + +      ctx->output = h_strdup_cprintf("%s%s", ctx->output, pf, rjname); + +      for (i = mc; i >= 0; i--) { +        ctx->output = h_strdup_cprintf("\t%s", +                                       ctx->output, +                                       values[i]); +      } + +      ctx->output = h_strdup_cprintf("\n", ctx->output); + +      g_strfreev(values); +    } +    g_free(pf); +} + +static GSList *report_create_module_list_from_dialog(ReportDialog * rd) +{ +    ShellModule *module; +    GSList *modules = NULL; +    GtkTreeModel *model = rd->model; +    GtkTreeIter iter; + +    gtk_tree_model_get_iter_first(model, &iter); +    do { +	gboolean selected; +	gchar *name; + +	gtk_tree_model_get(model, &iter, TREE_COL_SEL, &selected, -1); +	if (!selected) +	    continue; + +	module = g_new0(ShellModule, 1); + +	gtk_tree_model_get(model, &iter, TREE_COL_NAME, &name, -1); +	module->name = name; +	module->entries = NULL; + +	if (gtk_tree_model_iter_has_child(model, &iter)) { +	    ShellModuleEntry *entry; + +	    gint children = gtk_tree_model_iter_n_children(model, &iter); +	    gint i; + +	    for (i = 0; i < children; i++) { +		GtkTreeIter child; + +		gtk_tree_model_iter_nth_child(model, &child, &iter, i); + +		gtk_tree_model_get(model, &child, TREE_COL_SEL, &selected, +				   -1); +		if (!selected) +		    continue; + +		gtk_tree_model_get(model, &child, TREE_COL_MODULE_ENTRY, &entry, +				   -1); +		module->entries = g_slist_append(module->entries, entry); +	    } +	} + +	modules = g_slist_append(modules, module); +    } while (gtk_tree_model_iter_next(rd->model, &iter)); + +    return modules; +} + +static void +report_create_inner_from_module_list(ReportContext * ctx, GSList * modules) +{ +    for (; modules; modules = modules->next) { +	ShellModule *module = (ShellModule *) modules->data; +	GSList *entries; + +	if (!params.gui_running && !params.quiet) +	    fprintf(stderr, "\033[40;32m%s\033[0m\n", module->name); + +	report_title(ctx, module->name); + +	for (entries = module->entries; entries; entries = entries->next) { +	    ShellModuleEntry *entry = (ShellModuleEntry *) entries->data; +        if (entry->flags & MODULE_FLAG_HIDE) continue; + +	    if (!params.gui_running && !params.quiet) +		fprintf(stderr, "\033[2K\033[40;32;1m %s\033[0m\n", +			entry->name); + +        if (entry->icon_file) +            cache_icon(ctx, entry->icon_file); + +	    ctx->entry = entry; +	    report_subtitle(ctx, entry->name); +	    module_entry_scan(entry); +	    report_table(ctx, module_entry_function(entry)); +	} +    } +} + +void report_module_list_free(GSList * modules) +{ +    GSList *m; + +    for (m = modules; m; m = m->next) { +	ShellModule *module = (ShellModule *) m->data; + +	g_slist_free(module->entries); +    } + +    g_slist_free(modules); +} + +static gchar *report_get_filename(void) +{ +    GtkWidget *dialog; +    gchar *filename = NULL; + +#if GTK_CHECK_VERSION(3, 0, 0) +    dialog = gtk_file_chooser_dialog_new(_("Save File"), +					 NULL, +					 GTK_FILE_CHOOSER_ACTION_SAVE, +					 _("_Cancel"), +					 GTK_RESPONSE_CANCEL, +					 _("_Save"), +					 GTK_RESPONSE_ACCEPT, NULL); +#else +    dialog = gtk_file_chooser_dialog_new(_("Save File"), +					 NULL, +					 GTK_FILE_CHOOSER_ACTION_SAVE, +					 GTK_STOCK_CANCEL, +					 GTK_RESPONSE_CANCEL, +					 GTK_STOCK_SAVE, +					 GTK_RESPONSE_ACCEPT, NULL); +#endif + +    gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), +				      "hardinfo_report"); + +    file_chooser_add_filters(dialog, file_types); +    file_chooser_open_expander(dialog); + +    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { +	gchar *ext = file_chooser_get_extension(dialog, file_types); +	filename = file_chooser_build_filename(dialog, ext); +    } +    gtk_widget_destroy(dialog); +    return filename; +} + +ReportContext *report_context_html_new() +{ +    ReportContext *ctx; + +    ctx = g_new0(ReportContext, 1); +    ctx->header = report_html_header; +    ctx->footer = report_html_footer; +    ctx->title = report_html_title; +    ctx->subtitle = report_html_subtitle; +    ctx->subsubtitle = report_html_subsubtitle; +    ctx->keyvalue = report_html_key_value; + +    ctx->details_start = report_html_details_start; +    ctx->details_section = report_html_subsubtitle; +    ctx->details_keyvalue = report_html_key_value; +    ctx->details_end = report_html_details_end; + +    ctx->output = g_strdup(""); +    ctx->format = REPORT_FORMAT_HTML; + +    ctx->column_titles = g_hash_table_new_full(g_str_hash, g_str_equal, +                                               g_free, g_free); +    ctx->first_table = TRUE; + +    ctx->icon_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + +    return ctx; +} + +ReportContext *report_context_text_new() +{ +    ReportContext *ctx; + +    ctx = g_new0(ReportContext, 1); +    ctx->header = report_text_header; +    ctx->footer = report_text_footer; +    ctx->title = report_text_title; +    ctx->subtitle = report_text_subtitle; +    ctx->subsubtitle = report_text_subsubtitle; +    ctx->keyvalue = report_text_key_value; + +    ctx->details_start = report_text_key_value; +    ctx->details_section = report_text_subsubtitle; +    ctx->details_keyvalue = report_text_key_value; +    ctx->details_end = report_text_footer; /* nothing */ + +    ctx->output = g_strdup(""); +    ctx->format = REPORT_FORMAT_TEXT; + +    ctx->column_titles = g_hash_table_new_full(g_str_hash, g_str_equal, +                                               g_free, g_free); +    ctx->first_table = TRUE; + +    return ctx; +} + +ReportContext *report_context_shell_new() +{ +    ReportContext *ctx; + +    ctx = g_new0(ReportContext, 1); +    ctx->header = report_text_header; +    ctx->footer = report_text_footer; + +    ctx->title = report_text_title; +    ctx->subtitle = report_text_subtitle; +    /* special format handled in report_table(), +     * doesn't need the others. */ + +    ctx->output = g_strdup(""); +    ctx->format = REPORT_FORMAT_SHELL; + +    ctx->column_titles = g_hash_table_new_full(g_str_hash, g_str_equal, +                                               g_free, g_free); +    ctx->first_table = TRUE; + +    return ctx; +} + +void report_context_free(ReportContext * ctx) +{ +    g_hash_table_destroy(ctx->column_titles); +    if(ctx->icon_refs) +        g_hash_table_destroy(ctx->icon_refs); +    if(ctx->icon_data) +        g_hash_table_destroy(ctx->icon_data); +    g_free(ctx->output); +    g_free(ctx); +} + +void report_create_from_module_list(ReportContext * ctx, GSList * modules) +{ +    if (ctx->format == REPORT_FORMAT_HTML) +        params.fmt_opts = FMT_OPT_HTML; + +    report_header(ctx); + +    report_create_inner_from_module_list(ctx, modules); +    report_module_list_free(modules); + +    report_footer(ctx); +} + +gchar *report_create_from_module_list_format(GSList * modules, +					     ReportFormat format) +{ +    ReportContext *(*create_context) (); +    ReportContext *ctx; +    gchar *retval; + +    if (format >= N_REPORT_FORMAT) +	return NULL; + +    create_context = file_types[format].data; +    if (!create_context) +	return NULL; + +    ctx = create_context(); + +    report_create_from_module_list(ctx, modules); +    retval = g_strdup(ctx->output); + +    report_context_free(ctx); + +    return retval; +} + +static gboolean report_generate(ReportDialog * rd) +{ +    GSList *modules; +    ReportContext *ctx; +    ReportContext *(*create_context) (); +    gchar *file; +    FILE *stream; + +    int old_fmt_opts = params.fmt_opts; +    params.fmt_opts = FMT_OPT_NONE; + +    if (!(file = report_get_filename())) +	return FALSE; + +    if (!(stream = fopen(file, "w+"))) { +	g_free(file); +	return FALSE; +    } + +    create_context = file_types_get_data_by_name(file_types, file); + +    if (!create_context) { +	g_warning(_("Cannot create ReportContext. Programming bug?")); +	g_free(file); +	fclose(stream); +    params.fmt_opts = old_fmt_opts; +	return FALSE; +    } + +    ctx = create_context(); +    modules = report_create_module_list_from_dialog(rd); + +    report_create_from_module_list(ctx, modules); +    fputs(ctx->output, stream); +    fclose(stream); + +    if (ctx->format == REPORT_FORMAT_HTML) { +	GtkWidget *dialog; +	dialog = gtk_message_dialog_new(NULL, +					GTK_DIALOG_DESTROY_WITH_PARENT, +					GTK_MESSAGE_QUESTION, +					GTK_BUTTONS_NONE, +					_("Open the report with your web browser?")); +#if GTK_CHECK_VERSION(3, 0, 0) +    gtk_dialog_add_buttons(GTK_DIALOG(dialog), +			       _("_No"), GTK_RESPONSE_REJECT, +			       _("_Open"), GTK_RESPONSE_ACCEPT, NULL); +#else +	gtk_dialog_add_buttons(GTK_DIALOG(dialog), +			       GTK_STOCK_NO, GTK_RESPONSE_REJECT, +			       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); +#endif +	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { +	    gchar *temp; + +	    temp = g_strdup_printf("file://%s", file); +	    uri_open(temp); + +	    g_free(temp); +        } + +	gtk_widget_destroy(dialog); +    } + +    report_context_free(ctx); +    g_free(file); + +    params.fmt_opts = old_fmt_opts; +    return TRUE; +} + +void report_dialog_show(GtkTreeModel * model, GtkWidget * parent) +{ +    gboolean success; +    ReportDialog *rd = report_dialog_new(model, parent); + +    if (gtk_dialog_run(GTK_DIALOG(rd->dialog)) == GTK_RESPONSE_ACCEPT) { +	shell_status_update(_("Generating report...")); +	gtk_widget_hide(rd->dialog); +	shell_view_set_enabled(FALSE); +	shell_status_set_enabled(TRUE); + +	success = report_generate(rd); + +	shell_status_set_enabled(FALSE); + +	if (success) +	    shell_status_update(_("Report saved.")); +	else +	    shell_status_update(_("Error while creating the report.")); +    } + +    set_all_active(rd, FALSE); +    gtk_widget_destroy(rd->dialog); +    g_free(rd); +} + +static void +set_children_active(GtkTreeModel * model, GtkTreeIter * iter, +		    gboolean setting) +{ +    if (gtk_tree_model_iter_has_child(model, iter)) { +	gint children = gtk_tree_model_iter_n_children(model, iter); + +	gtk_tree_store_set(GTK_TREE_STORE(model), iter, TREE_COL_SEL, +			   setting, -1); + +	for (children--; children >= 0; children--) { +	    GtkTreeIter child; + +	    gtk_tree_model_iter_nth_child(model, &child, iter, children); +	    gtk_tree_store_set(GTK_TREE_STORE(model), &child, TREE_COL_SEL, +			       setting, -1); +	} +    } +} + +static void set_all_active(ReportDialog * rd, gboolean setting) +{ +    GtkTreeIter iter; +    GtkTreeModel *model = rd->model; + +    gtk_tree_model_get_iter_first(model, &iter); + +    do { +	set_children_active(model, &iter, setting); +    } while (gtk_tree_model_iter_next(model, &iter)); +} + +static void report_dialog_sel_none(GtkWidget * widget, ReportDialog * rd) +{ +    set_all_active(rd, FALSE); +} + +static void report_dialog_sel_all(GtkWidget * widget, ReportDialog * rd) +{ +    set_all_active(rd, TRUE); +} + +static void +report_dialog_sel_toggle(GtkCellRendererToggle * cellrenderertoggle, +			 gchar * path_str, ReportDialog * rd) +{ +    GtkTreeModel *model = rd->model; +    GtkTreeIter iter; +    GtkTreePath *path = gtk_tree_path_new_from_string(path_str); +    gboolean active; + +    gtk_tree_model_get_iter(model, &iter, path); +    gtk_tree_model_get(model, &iter, TREE_COL_SEL, &active, -1); + +    active = !active; +    gtk_tree_store_set(GTK_TREE_STORE(model), &iter, TREE_COL_SEL, active, +		       -1); +    set_children_active(model, &iter, active); + +    if (active) { +        GtkTreeIter parent; + +        if (gtk_tree_model_iter_parent(model, &parent, &iter)) { +            gtk_tree_store_set(GTK_TREE_STORE(model), &parent, +                               TREE_COL_SEL, active, -1); +        } +    } + +    gtk_tree_path_free(path); +} + +static ReportDialog +    * report_dialog_new(GtkTreeModel * model, GtkWidget * parent) +{ +    ReportDialog *rd; +    GtkWidget *dialog; +    GtkWidget *dialog1_vbox; +    GtkWidget *scrolledwindow2; +    GtkWidget *treeview2; +    GtkWidget *vbuttonbox3; +    GtkWidget *button3; +    GtkWidget *button6; +    GtkWidget *dialog1_action_area; +    GtkWidget *button8; +    GtkWidget *button7; +    GtkWidget *label; +    GtkWidget *hbox; + +    GtkTreeViewColumn *column; +    GtkCellRenderer *cr_text, *cr_pbuf, *cr_toggle; + +    rd = g_new0(ReportDialog, 1); + +    dialog = gtk_dialog_new(); +    gtk_window_set_title(GTK_WINDOW(dialog), _("Generate Report")); +    gtk_container_set_border_width(GTK_CONTAINER(dialog), 5); +    gtk_window_set_default_size(GTK_WINDOW(dialog), 420, 260); +    gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent)); +    gtk_window_set_position(GTK_WINDOW(dialog), +			    GTK_WIN_POS_CENTER_ON_PARENT); +    gtk_window_set_type_hint(GTK_WINDOW(dialog), +			     GDK_WINDOW_TYPE_HINT_DIALOG); + +#if GTK_CHECK_VERSION(2, 14, 0) +    dialog1_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); +#else +    dialog1_vbox = GTK_DIALOG(dialog)->vbox; +#endif +    gtk_box_set_spacing(GTK_BOX(dialog1_vbox), 5); +    gtk_container_set_border_width(GTK_CONTAINER(dialog1_vbox), 4); +    gtk_widget_show(dialog1_vbox); + +#if GTK_CHECK_VERSION(3, 0, 0) +    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +#else +    hbox = gtk_hbox_new(FALSE, 5); +#endif +    gtk_box_pack_start(GTK_BOX(dialog1_vbox), hbox, FALSE, FALSE, 0); + +    label = gtk_label_new(_("<big><b>Generate Report</b></big>\n" +			  "Please choose the information that you wish " +			  "to view in your report:")); +    gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); +    gtk_label_set_use_markup(GTK_LABEL(label), TRUE); +#if GTK_CHECK_VERSION(3, 0, 0) +    gtk_widget_set_valign(label, GTK_ALIGN_CENTER); +#else +    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); +#endif + +    gtk_box_pack_start(GTK_BOX(hbox), +		       icon_cache_get_image("report-large.png"), +		       FALSE, FALSE, 0); +    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); +    gtk_widget_show_all(hbox); + +#if GTK_CHECK_VERSION(3, 0, 0) +    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +#else +    hbox = gtk_hbox_new(FALSE, 5); +#endif +    gtk_box_pack_start(GTK_BOX(dialog1_vbox), hbox, TRUE, TRUE, 0); +    gtk_widget_show(hbox); + +    scrolledwindow2 = gtk_scrolled_window_new(NULL, NULL); +    gtk_widget_show(scrolledwindow2); +    gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow2, TRUE, TRUE, +		       0); +    gtk_widget_set_size_request(scrolledwindow2, -1, 200); +    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow2), +				   GTK_POLICY_AUTOMATIC, +				   GTK_POLICY_AUTOMATIC); +    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW +					(scrolledwindow2), GTK_SHADOW_IN); + +    treeview2 = gtk_tree_view_new_with_model(model); +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview2), FALSE); +    gtk_widget_show(treeview2); +    gtk_container_add(GTK_CONTAINER(scrolledwindow2), treeview2); + +    column = gtk_tree_view_column_new(); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview2), column); + +    cr_toggle = gtk_cell_renderer_toggle_new(); +    gtk_tree_view_column_pack_start(column, cr_toggle, FALSE); +    g_signal_connect(cr_toggle, "toggled", +		     G_CALLBACK(report_dialog_sel_toggle), rd); +    gtk_tree_view_column_add_attribute(column, cr_toggle, "active", +				       TREE_COL_SEL); + +    cr_pbuf = gtk_cell_renderer_pixbuf_new(); +    gtk_tree_view_column_pack_start(column, cr_pbuf, FALSE); +    gtk_tree_view_column_add_attribute(column, cr_pbuf, "pixbuf", +				       TREE_COL_PBUF); + +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_text, TRUE); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", +				       TREE_COL_NAME); + +#if GTK_CHECK_VERSION(3, 0, 0) +    vbuttonbox3 = gtk_button_box_new(GTK_ORIENTATION_VERTICAL); +#else +    vbuttonbox3 = gtk_vbutton_box_new(); +#endif +    gtk_widget_show(vbuttonbox3); +    gtk_box_pack_start(GTK_BOX(hbox), vbuttonbox3, FALSE, TRUE, 0); +    gtk_box_set_spacing(GTK_BOX(vbuttonbox3), 5); +    gtk_button_box_set_layout(GTK_BUTTON_BOX(vbuttonbox3), +			      GTK_BUTTONBOX_START); + +    button3 = gtk_button_new_with_mnemonic(_("Select _None")); +    gtk_widget_show(button3); +    gtk_container_add(GTK_CONTAINER(vbuttonbox3), button3); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button3, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button3, GTK_CAN_DEFAULT); +#endif +    g_signal_connect(button3, "clicked", +		     G_CALLBACK(report_dialog_sel_none), rd); + +    button6 = gtk_button_new_with_mnemonic(_("Select _All")); +    gtk_widget_show(button6); +    gtk_container_add(GTK_CONTAINER(vbuttonbox3), button6); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button6, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button6, GTK_CAN_DEFAULT); +#endif +    g_signal_connect(button6, "clicked", G_CALLBACK(report_dialog_sel_all), +		     rd); + +#if GTK_CHECK_VERSION(2, 14, 0) +/* TODO:GTK3 + * [https://developer.gnome.org/gtk3/stable/GtkDialog.html#gtk-dialog-get-action-area] + * gtk_dialog_get_action_area has been deprecated since version 3.12 and should not be used in newly-written code. + * Direct access to the action area is discouraged; use gtk_dialog_add_button(), etc. + */ +    dialog1_action_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); +#else +    dialog1_action_area = GTK_DIALOG(dialog)->action_area; +#endif +    gtk_widget_show(dialog1_action_area); +    gtk_button_box_set_layout(GTK_BUTTON_BOX(dialog1_action_area), +			      GTK_BUTTONBOX_END); + +    button8 = gtk_button_new_with_mnemonic(_("_Cancel")); +    gtk_widget_show(button8); +    gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button8, +				 GTK_RESPONSE_CANCEL); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button8, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button8, GTK_CAN_DEFAULT); +#endif + +    button7 = gtk_button_new_with_mnemonic(_("_Generate")); +    gtk_widget_show(button7); +    gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button7, +				 GTK_RESPONSE_ACCEPT); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button7, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button7, GTK_CAN_DEFAULT); +#endif + +    rd->dialog = dialog; +    rd->btn_cancel = button8; +    rd->btn_generate = button7; +    rd->btn_sel_all = button6; +    rd->btn_sel_none = button3; +    rd->treeview = treeview2; +    rd->model = model; + +    gtk_tree_view_collapse_all(GTK_TREE_VIEW(treeview2)); +    set_all_active(rd, TRUE); + +    return rd; +} diff --git a/shell/shell.c b/shell/shell.c new file mode 100644 index 00000000..a2773523 --- /dev/null +++ b/shell/shell.c @@ -0,0 +1,2398 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + *    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, version 2 or later. + * + *    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 St, Fifth Floor, Boston, MA  02110-1301 USA + */ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <string.h> + +#include <gtk/gtk.h> +#include <glib/gstdio.h> + +#include "config.h" + +#include "hardinfo.h" + +#include "shell.h" +#include "syncmanager.h" +#include "iconcache.h" +#include "menu.h" +#include "stock.h" +#include "uri_handler.h" + +#include "callbacks.h" + +struct UpdateTableItem { +    union { +        GtkWidget *widget; +        GtkTreeIter *iter; +    }; +    gboolean is_iter; +}; + +/* + * Internal Prototypes ******************************************************** + */ + +static void create_window(void); +static ShellTree *tree_new(void); +static ShellInfoTree *info_tree_new(void); + +static void module_selected(gpointer data); +static void module_selected_show_info(ShellModuleEntry * entry, +				      gboolean reload); +static void info_selected(GtkTreeSelection * ts, gpointer data); +static void info_selected_show_extra(const gchar *tag); +static gboolean reload_section(gpointer data); +static gboolean rescan_section(gpointer data); +static gboolean update_field(gpointer data); + +/* + * Globals ******************************************************************** + */ + +static Shell *shell = NULL; +static GHashTable *update_tbl = NULL; +static GSList *update_sfusrc = NULL; + +gchar *lginterval = NULL; + +/* + * Code :) ******************************************************************** + */ + +Shell *shell_get_main_shell(void) +{ +    return shell; +} + +void shell_ui_manager_set_visible(const gchar * path, gboolean setting) +{ +    GtkWidget *widget; + +    if (!params.gui_running) +	return; + +    widget = gtk_ui_manager_get_widget(shell->ui_manager, path); +    if (!widget) +	return; + +    if (setting) +	gtk_widget_show(widget); +    else +	gtk_widget_hide(widget); +} + +void shell_clear_tree_models(Shell *shell) +{ +    gtk_tree_store_clear(GTK_TREE_STORE(shell->tree->model)); +    gtk_tree_store_clear(GTK_TREE_STORE(shell->info_tree->model)); +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), FALSE); +} + +void shell_clear_timeouts(Shell *shell) +{ +    h_hash_table_remove_all(update_tbl); +} + +void shell_action_set_property(const gchar * action_name, +			       const gchar * property, gboolean setting) +{ +    GtkAction *action; + +    if (!params.gui_running) +	return; + +    action = gtk_action_group_get_action(shell->action_group, action_name); +    if (action) { +	GValue value = { 0 }; + +	g_value_init(&value, G_TYPE_BOOLEAN); +	g_value_set_boolean(&value, setting); + +	g_object_set_property(G_OBJECT(action), property, &value); + +	g_value_unset(&value); +    } +} + +void shell_action_set_label(const gchar * action_name, gchar * label) +{ +#if GTK_CHECK_VERSION(2,16,0) +    if (params.gui_running && shell->action_group) { +	GtkAction *action; + +	action = +	    gtk_action_group_get_action(shell->action_group, action_name); +	if (action) { +	    gtk_action_set_label(action, label); +	} +    } +#endif +} + +void shell_action_set_enabled(const gchar * action_name, gboolean setting) +{ +    if (params.gui_running && shell->action_group) { +	GtkAction *action; + +	action = +	    gtk_action_group_get_action(shell->action_group, action_name); +	if (action) { +	    gtk_action_set_sensitive(action, setting); +	} +    } +} + +gboolean shell_action_get_enabled(const gchar * action_name) +{ +    GtkAction *action; + +    if (!params.gui_running) +	return FALSE; + +    action = gtk_action_group_get_action(shell->action_group, action_name); +    if (action) { +	return gtk_action_get_sensitive(action); +    } + +    return FALSE; +} + +void shell_set_side_pane_visible(gboolean setting) +{ +    if (!params.gui_running) +	return; + +    if (setting) +	gtk_widget_show(shell->tree->scroll); +    else +	gtk_widget_hide(shell->tree->scroll); +} + +gboolean shell_action_get_active(const gchar * action_name) +{ +    GtkAction *action; +    GSList *proxies; + +    /* FIXME: Ugh. Are you sure there isn't any simpler way? O_o */ +    if (!params.gui_running) +	return FALSE; + +    action = gtk_action_group_get_action(shell->action_group, action_name); +    if (action) { +	proxies = gtk_action_get_proxies(action); + +	for (; proxies; proxies = proxies->next) { +	    GtkWidget *widget = (GtkWidget *) proxies->data; + +	    if (GTK_IS_CHECK_MENU_ITEM(widget)) { +		return +		    gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM +						   (widget)); +	    } +	} +    } + +    return FALSE; +} + +void shell_action_set_active(const gchar * action_name, gboolean setting) +{ +    GtkAction *action; +    GSList *proxies; + +    /* FIXME: Ugh. Are you sure there isn't any simpler way? O_o */ +    if (!params.gui_running) +	return; + +    action = gtk_action_group_get_action(shell->action_group, action_name); +    if (action) { +	proxies = gtk_action_get_proxies(action); + +	for (; proxies; proxies = proxies->next) { +	    GtkWidget *widget = (GtkWidget *) proxies->data; + +	    if (GTK_IS_CHECK_MENU_ITEM(widget)) { +		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), +					       setting); +		return; +	    } +	} +    } +} + +void shell_status_pulse(void) +{ +    if (params.gui_running) { +	if (shell->_pulses++ == 5) { +	    /* we're pulsing for some time, disable the interface and change the cursor +	       to a hourglass */ +	    shell_view_set_enabled(FALSE); +	} + +	gtk_progress_bar_pulse(GTK_PROGRESS_BAR(shell->progress)); +	while (gtk_events_pending()) +	    gtk_main_iteration(); +    } else if (!params.quiet) { +	static gint counter = 0; + +	fprintf(stderr, "\033[2K\033[40;37;1m %c\033[0m\r", +		"|/-\\"[counter++ % 4]); +    } +} + +void shell_status_set_percentage(gint percentage) +{ +    if (params.gui_running) { +	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(shell->progress), +				      (float) percentage / 100.0); +	while (gtk_events_pending()) +	    gtk_main_iteration(); +    } else if (!params.quiet) { +	if (percentage < 1 || percentage >= 100) { +	    fprintf(stderr, "\033[2K"); +	} else { +	    gchar pbar[] = "----------"; + +	    memset(pbar, '#', percentage / 10); + +	    fprintf(stderr, "\r\033[40;37;1m%3d%% \033[40;34;1m" +		    "%s\033[0m\r", percentage, pbar); +	} +    } +} + +void shell_view_set_enabled(gboolean setting) +{ +    if (!params.gui_running) +	return; + +    if (setting) { +	shell->_pulses = 0; +	widget_set_cursor(shell->window, GDK_LEFT_PTR); +    } else { +	widget_set_cursor(shell->window, GDK_WATCH); +    } + +    gtk_widget_set_sensitive(shell->hbox, setting); +    shell_action_set_enabled("ViewMenuAction", setting); +    shell_action_set_enabled("ConnectToAction", setting); +    shell_action_set_enabled("RefreshAction", setting); +    shell_action_set_enabled("CopyAction", setting); +    shell_action_set_enabled("ReportAction", setting); +    shell_action_set_enabled("SyncManagerAction", setting && sync_manager_count_entries() > 0); +} + +void shell_status_set_enabled(gboolean setting) +{ +    if (!params.gui_running) +	return; + +    if (setting) +	gtk_widget_show(shell->progress); +    else { +	gtk_widget_hide(shell->progress); +	shell_view_set_enabled(TRUE); + +	shell_status_update(_("Done.")); +    } +} + +void shell_do_reload(void) +{ +    if (!params.gui_running || !shell->selected) +	return; + +    shell_action_set_enabled("RefreshAction", FALSE); +    shell_action_set_enabled("CopyAction", FALSE); +    shell_action_set_enabled("ReportAction", FALSE); + +    shell_status_set_enabled(TRUE); + +    module_entry_reload(shell->selected); +    module_selected(NULL); + +    shell_action_set_enabled("RefreshAction", TRUE); +    shell_action_set_enabled("CopyAction", TRUE); +    shell_action_set_enabled("ReportAction", TRUE); +} + +void shell_status_update(const gchar * message) +{ +    if (params.gui_running) { +	gtk_label_set_markup(GTK_LABEL(shell->status), message); +	gtk_progress_bar_pulse(GTK_PROGRESS_BAR(shell->progress)); +	while (gtk_events_pending()) +	    gtk_main_iteration(); +    } else if (!params.quiet) { +	fprintf(stderr, "\033[2K\033[40;37;1m %s\033[0m\r", message); +    } +} + +static void destroy_me(void) +{ +    cb_quit(); +} + +static void close_note(GtkWidget * widget, gpointer user_data) +{ +    gtk_widget_hide(shell->note->event_box); +} + +static ShellNote *note_new(void) +{ +    ShellNote *note; +    GtkWidget *hbox, *icon, *button; +    GtkWidget *border_box; +    /* colors stolen from gtkinfobar.c */ +    GdkColor info_default_border_color     = { 0, 0xb800, 0xad00, 0x9d00 }; +    GdkColor info_default_fill_color       = { 0, 0xff00, 0xff00, 0xbf00 }; + +    note = g_new0(ShellNote, 1); +    note->label = gtk_label_new(""); +    note->event_box = gtk_event_box_new(); +    button = gtk_button_new(); + +    border_box = gtk_event_box_new(); +    gtk_container_set_border_width(GTK_CONTAINER(border_box), 1); +    gtk_container_add(GTK_CONTAINER(note->event_box), border_box); +    gtk_widget_show(border_box); + +    gtk_widget_modify_bg(border_box, GTK_STATE_NORMAL, &info_default_fill_color); +    gtk_widget_modify_bg(note->event_box, GTK_STATE_NORMAL, &info_default_border_color); + +    icon = icon_cache_get_image("close.png"); +    gtk_widget_show(icon); +    gtk_container_add(GTK_CONTAINER(button), icon); +    gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); +    g_signal_connect(G_OBJECT(button), "clicked", (GCallback) close_note, +		     NULL); + +#if GTK_CHECK_VERSION(3, 0, 0) +    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); +#else +    hbox = gtk_hbox_new(FALSE, 3); +#endif +    icon = icon_cache_get_image("dialog-information.png"); + +    gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); +    gtk_box_pack_start(GTK_BOX(hbox), note->label, FALSE, FALSE, 0); +    gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); + +    gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); +    gtk_container_add(GTK_CONTAINER(border_box), hbox); +    gtk_widget_show_all(hbox); + +    return note; +} + +void shell_set_title(Shell *shell, gchar *subtitle) +{ +    if (subtitle) { +        gchar *tmp; + +        tmp = g_strdup_printf(_("%s - System Information and Benchmark"), subtitle); +        gtk_window_set_title(GTK_WINDOW(shell->window), tmp); + +        g_free(tmp); +    } else { +        gtk_window_set_title(GTK_WINDOW(shell->window), _("System Information and Benchmark")); +    } +} + +static void create_window(void) +{ +    GtkWidget *vbox, *hbox; + +    shell = g_new0(Shell, 1); + +    shell->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); +    gtk_window_set_icon(GTK_WINDOW(shell->window), +			icon_cache_get_pixbuf("hardinfo2.png")); +    shell_set_title(shell, NULL); +    gtk_window_set_default_size(GTK_WINDOW(shell->window), 1024, 800); +    g_signal_connect(G_OBJECT(shell->window), "destroy", destroy_me, NULL); + +#if GTK_CHECK_VERSION(3, 0, 0) +    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); +#else +    vbox = gtk_vbox_new(FALSE, 0); +#endif +    gtk_widget_show(vbox); +    gtk_container_add(GTK_CONTAINER(shell->window), vbox); +    shell->vbox = vbox; + +    menu_init(shell); + +#if GTK_CHECK_VERSION(3, 0, 0) +    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +#else +    hbox = gtk_hbox_new(FALSE, 5); +#endif +    gtk_widget_show(hbox); +    gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); + +    shell->progress = gtk_progress_bar_new(); +    gtk_widget_set_size_request(shell->progress, 80, 10); +    gtk_widget_hide(shell->progress); +    gtk_box_pack_end(GTK_BOX(hbox), shell->progress, FALSE, FALSE, 5); + +    shell->status = gtk_label_new(""); +#if GTK_CHECK_VERSION(3, 0, 0) +    gtk_widget_set_valign(GTK_WIDGET(shell->status), GTK_ALIGN_CENTER); +#else +    gtk_misc_set_alignment(GTK_MISC(shell->status), 0.0, 0.5); +#endif +    gtk_widget_show(shell->status); +    gtk_box_pack_start(GTK_BOX(hbox), shell->status, FALSE, FALSE, 5); + +#if GTK_CHECK_VERSION(3, 0, 0) +    shell->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +#else +    shell->hbox = gtk_hbox_new(FALSE, 5); +#endif +    gtk_widget_show(shell->hbox); +    gtk_box_pack_end(GTK_BOX(vbox), shell->hbox, TRUE, TRUE, 0); + +#if GTK_CHECK_VERSION(3, 0, 0) +    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); +#else +    vbox = gtk_vbox_new(FALSE, 5); +#endif +    gtk_widget_show(vbox); +    gtk_box_pack_end(GTK_BOX(shell->hbox), vbox, TRUE, TRUE, 0); + +    shell->note = note_new(); +    gtk_box_pack_end(GTK_BOX(vbox), shell->note->event_box, FALSE, FALSE, 0); + +#if GTK_CHECK_VERSION(3, 0, 0) +    shell->vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); +#else +    shell->vpaned = gtk_vpaned_new(); +#endif +    gtk_box_pack_start(GTK_BOX(vbox), shell->vpaned, TRUE, TRUE, 0); +    gtk_widget_show(shell->vpaned); + +    shell->notebook = gtk_notebook_new(); +    gtk_paned_add2(GTK_PANED(shell->vpaned), shell->notebook); + +    gtk_widget_show(shell->window); +    while (gtk_events_pending()) +	gtk_main_iteration(); +} + +static void view_menu_select_entry(gpointer data, gpointer data2) +{ +    GtkTreePath *path; +    GtkTreeIter *iter = (GtkTreeIter *) data2; + +    path = gtk_tree_model_get_path(shell->tree->model, iter); + +    gtk_tree_selection_select_path(shell->tree->selection, path); +    gtk_tree_view_set_cursor(GTK_TREE_VIEW(shell->tree->view), path, NULL, +			     FALSE); +    gtk_tree_path_free(path); +} + +static void menu_item_set_icon_always_visible(Shell *shell, +                                              gchar *parent_path, +                                              gchar *item_id) +{ +    GtkWidget *menuitem; +    gchar *path; + +    path = g_strdup_printf("%s/%s", parent_path, item_id); +    menuitem = gtk_ui_manager_get_widget(shell->ui_manager, path); + +    //gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(menuitem), TRUE); +    g_free(path); +} + +static void add_module_to_menu(gchar * name, GdkPixbuf * pixbuf) +{ +    GtkAction *action; +    GtkWidget *menuitem; +    gchar *about_module = g_strdup_printf("AboutModule%s", name); +    gchar *path; +    gint merge_id; + +    GtkActionEntry entries[] = { +	{ +	 name,			/* name */ +	 name,			/* stockid */ +	 name,			/* label */ +	 NULL,			/* accelerator */ +	 NULL,			/* tooltip */ +	 NULL,			/* callback */ +	 }, +	{ +	 about_module, +	 name, +	 name, +	 NULL, +	 name, +	 (GCallback) cb_about_module, +	 }, +    }; + +    stock_icon_register_pixbuf(pixbuf, name); + +    if ((action = gtk_action_group_get_action(shell->action_group, name))) { +        gtk_action_group_remove_action(shell->action_group, action); +    } + +    if ((action = gtk_action_group_get_action(shell->action_group, about_module))) { +        gtk_action_group_remove_action(shell->action_group, action); +    } + +    gtk_action_group_add_actions(shell->action_group, entries, 2, NULL); + +    merge_id = gtk_ui_manager_new_merge_id(shell->ui_manager); +    gtk_ui_manager_add_ui(shell->ui_manager, +                          merge_id, +			  "/menubar/ViewMenu/LastSep", +			  name, name, GTK_UI_MANAGER_MENU, TRUE); +    shell->merge_ids = g_slist_prepend(shell->merge_ids, GINT_TO_POINTER(merge_id)); + +    merge_id = gtk_ui_manager_new_merge_id(shell->ui_manager); +    gtk_ui_manager_add_ui(shell->ui_manager, +                          merge_id, +			  //  "/menubar/HelpMenu/HelpMenuModules/LastSep", +			  "/menubar/HelpMenu/LastSep", +			  about_module, about_module, GTK_UI_MANAGER_AUTO, +			  TRUE); +    shell->merge_ids = g_slist_prepend(shell->merge_ids, GINT_TO_POINTER(merge_id)); + +    menu_item_set_icon_always_visible(shell, "/menubar/ViewMenu", name); +} + +static void +add_module_entry_to_view_menu(gchar * module, gchar * name, +			      GdkPixbuf * pixbuf, GtkTreeIter * iter) +{ +    GtkAction *action; +    GtkWidget *menuitem; +    gint merge_id; +    gchar *path; +    GtkActionEntry entry = { +       name,			/* name */ +       name,			/* stockid */ +       name,			/* label */ +       NULL,			/* accelerator */ +       NULL,			/* tooltip */ +       (GCallback) view_menu_select_entry,	/* callback */ +    }; + +    stock_icon_register_pixbuf(pixbuf, name); + +    if ((action = gtk_action_group_get_action(shell->action_group, name))) { +        gtk_action_group_remove_action(shell->action_group, action); +    } + +    gtk_action_group_add_actions(shell->action_group, &entry, 1, iter); + +    merge_id = gtk_ui_manager_new_merge_id(shell->ui_manager); +    path = g_strdup_printf("/menubar/ViewMenu/%s", module); +    gtk_ui_manager_add_ui(shell->ui_manager, +                          merge_id, +                          path, +			  name, name, GTK_UI_MANAGER_AUTO, FALSE); +    shell->merge_ids = g_slist_prepend(shell->merge_ids, GINT_TO_POINTER(merge_id)); + +    menu_item_set_icon_always_visible(shell, path, name); + +    g_free(path); +} + +void shell_add_modules_to_gui(gpointer _shell_module, gpointer _shell_tree) +{ +    ShellModule *module = (ShellModule *) _shell_module; +    ShellTree *shelltree = (ShellTree *) _shell_tree; +    GtkTreeStore *store = GTK_TREE_STORE(shelltree->model); +    GtkTreeIter parent; + +    if (!module) { +        return; +    } + +    gtk_tree_store_append(store, &parent, NULL); +    gtk_tree_store_set(store, &parent, +                       TREE_COL_NAME, module->name, +		       TREE_COL_MODULE, module, +		       TREE_COL_MODULE_ENTRY, NULL, +		       TREE_COL_SEL, FALSE, +		       -1); + +    if (module->icon) { +	gtk_tree_store_set(store, &parent, TREE_COL_PBUF, module->icon, +			   -1); +    } + +    //add_module_to_menu(module->name, module->icon); + +    if (module->entries) { +	ShellModuleEntry *entry; +	GSList *p; + +	for (p = module->entries; p; p = g_slist_next(p)) { +	    GtkTreeIter child; +	    entry = (ShellModuleEntry *) p->data; +        if (entry->flags & MODULE_FLAG_HIDE) continue; + +	    gtk_tree_store_append(store, &child, &parent); +	    gtk_tree_store_set(store, &child, TREE_COL_NAME, entry->name, +			       TREE_COL_MODULE_ENTRY, entry, +			       TREE_COL_SEL, FALSE, -1); + +	    if (entry->icon) { +		gtk_tree_store_set(store, &child, TREE_COL_PBUF, +				   entry->icon, -1); +	    } + +	    add_module_entry_to_view_menu(module->name, entry->name, +					  entry->icon, +					  gtk_tree_iter_copy(&child)); + +	    shell_status_pulse(); +	} + +    } +} + +static void destroy_update_tbl_value(gpointer data) +{ +    struct UpdateTableItem *item = data; + +    if (item->is_iter) { +        gtk_tree_iter_free(item->iter); +    } else { +        g_object_unref(item->widget); +    } + +    g_free(item); +} + +DetailView *detail_view_new(void) +{ +    DetailView *detail_view; + +    detail_view = g_new0(DetailView, 1); +    detail_view->scroll = gtk_scrolled_window_new(NULL, NULL); +#if GTK_CHECK_VERSION(3, 0, 0) +    detail_view->view = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); +#else +    detail_view->view = gtk_vbox_new(FALSE, 0); +#endif + +    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(detail_view->scroll), +                                        GTK_SHADOW_IN); +    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(detail_view->scroll), +                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); +#if GTK_CHECK_VERSION(3, 0, 0) +    gtk_container_add(GTK_CONTAINER(detail_view->scroll), detail_view->view); +#else +    gtk_scrolled_window_add_with_viewport( +        GTK_SCROLLED_WINDOW(detail_view->scroll), detail_view->view); +#endif +    gtk_widget_show_all(detail_view->scroll); + +    return detail_view; +} + +static gboolean +select_first_tree_item(gpointer data) +{ +    GtkTreeIter first; + +    if (gtk_tree_model_get_iter_first(shell->tree->model, &first)) +        gtk_tree_selection_select_iter(shell->tree->selection, &first); + +    return FALSE; +} + +static void +check_for_updates(void) +{ +    GKeyFile *key_file = g_key_file_new(); + +    gchar *conf_path = g_build_filename(g_get_user_config_dir(), "hardinfo2", +                                        "settings.ini", NULL); + +    g_key_file_load_from_file( +        key_file, conf_path, +        G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); + +    gboolean setting = g_key_file_get_boolean(key_file, "Sync", "OnStartup", NULL); +    shell_action_set_active("SyncOnStartupAction", setting); + +    g_free(conf_path); +    g_key_file_free(key_file); + +    if (setting) { +        sync_manager_update_on_startup(); +    } +} + +gboolean hardinfo_link(const gchar *uri) { +    /* Clicked link events pass through here on their +     * way to the default handler (xdg-open). +     * +     * TODO: In the future, links could be used to +     * jump to different pages in hardinfo. +     * +     * if (g_str_has_prefix(uri, "hardinfo:")) { +     *       hardinfo_navigate(g_utf8_strchr(uri, strlen("hardinfo2"), ':') + 1); +     *       return TRUE; +     * } +     */ + +    return FALSE; /* didn't handle it */ +} + +void shell_set_transient_dialog(GtkWindow *dialog) +{ +    shell->transient_dialog = dialog ? dialog : GTK_WINDOW(shell->window); +} + +void shell_init(GSList * modules) +{ +    if (shell) { +	g_error("Shell already created"); +	return; +    } + +    DEBUG("initializing shell"); + +    uri_set_function(hardinfo_link); +    params.fmt_opts = FMT_OPT_PANGO; + +    create_window(); + +    shell_action_set_property("ConnectToAction", "is-important", TRUE); +    shell_action_set_property("CopyAction", "is-important", TRUE); +    shell_action_set_property("RefreshAction", "is-important", TRUE); +    shell_action_set_property("ReportAction", "is-important", TRUE); +    shell_action_set_property("ReportBugAction", "is-important", TRUE); +    shell_action_set_property("SyncManagerAction", "is-important", TRUE); + +    shell->tree = tree_new(); +    shell->info_tree = info_tree_new(); +    shell->loadgraph = load_graph_new(75); +    shell->detail_view = detail_view_new(); +    shell->transient_dialog = GTK_WINDOW(shell->window); + +    update_tbl = g_hash_table_new_full(g_str_hash, g_str_equal, +                                       g_free, destroy_update_tbl_value); + +    gtk_box_pack_start(GTK_BOX(shell->hbox), shell->tree->scroll, +                       FALSE, FALSE, 0); +    gtk_paned_pack1(GTK_PANED(shell->vpaned), shell->info_tree->scroll, +		    SHELL_PACK_RESIZE, SHELL_PACK_SHRINK); + +    gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), +			     load_graph_get_framed(shell->loadgraph), +			     NULL); +    gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), +                             shell->detail_view->scroll, NULL); + +    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(shell->notebook), FALSE); +    gtk_notebook_set_show_border(GTK_NOTEBOOK(shell->notebook), FALSE); + +    shell_status_set_enabled(TRUE); +    shell_status_update(_("Loading modules...")); + +    shell->tree->modules = modules ? modules : modules_load_all(); + +    check_for_updates(); + +    g_slist_foreach(shell->tree->modules, shell_add_modules_to_gui, shell->tree); +    gtk_tree_view_expand_all(GTK_TREE_VIEW(shell->tree->view)); + +    gtk_widget_show_all(shell->hbox); + +    load_graph_configure_expose(shell->loadgraph); +    gtk_widget_hide(shell->notebook); +    gtk_widget_hide(shell->note->event_box); + +    shell_status_update(_("Done.")); +    shell_status_set_enabled(FALSE); + +    shell_action_set_enabled("RefreshAction", FALSE); +    shell_action_set_enabled("CopyAction", FALSE); +    shell_action_set_active("SidePaneAction", TRUE); +    shell_action_set_active("ToolbarAction", TRUE); + +    shell_action_set_enabled("SyncManagerAction", sync_manager_count_entries() > 0); + +    /* Should select Computer Summary (note: not Computer/Summary) */ +    g_idle_add(select_first_tree_item, NULL); +} + +static gboolean update_field(gpointer data) +{ +    ShellFieldUpdate *fu; +    struct UpdateTableItem *item; + +    fu = (ShellFieldUpdate *)data; +    g_return_val_if_fail(fu != NULL, FALSE); + +    DEBUG("update_field [%s]", fu->field_name); + +    item = g_hash_table_lookup(update_tbl, fu->field_name); +    if (!item) { +        return FALSE; +    } + +    /* if the entry is still selected, update it */ +    if (fu->entry->selected && fu->entry->fieldfunc) { +        gchar *value = fu->entry->fieldfunc(fu->field_name); + +        if (item->is_iter) { +            /* +             * this function is also used to feed the load graph when ViewType +             * is SHELL_VIEW_LOAD_GRAPH +             */ +            if (shell->view_type == SHELL_VIEW_LOAD_GRAPH && +                gtk_tree_selection_iter_is_selected(shell->info_tree->selection, +                                                    item->iter)) { + +                load_graph_set_title(shell->loadgraph, fu->field_name); +                load_graph_update(shell->loadgraph, atof(value)); +            } + +            GtkTreeStore *store = GTK_TREE_STORE(shell->info_tree->model); +            gtk_tree_store_set(store, item->iter, INFO_TREE_COL_VALUE, value, -1); +        } else { +            GList *children = gtk_container_get_children(GTK_CONTAINER(item->widget)); +            gtk_label_set_markup(GTK_LABEL(children->next->data), value); +            g_list_free(children); +        } + +        g_free(value); +        return TRUE; +    } + +    if (update_sfusrc) { +        GSList *sfu; + +        for (sfu = update_sfusrc; sfu; sfu = sfu->next) { +            g_free(sfu->data); +        } + +        g_slist_free(update_sfusrc); +        update_sfusrc = NULL; +    } + +    /* otherwise, cleanup and destroy the timeout */ +    g_free(fu->field_name); +    g_free(fu); + +    return FALSE; +} + +#if GTK_CHECK_VERSION(3, 0, 0) +#define RANGE_SET_VALUE(...) +#define RANGE_GET_VALUE(...) 0 +#else +#define RANGE_SET_VALUE(tree, scrollbar, value)                                \ +    do {                                                                       \ +        GtkRange CONCAT(*range, __LINE__) =                                    \ +            GTK_RANGE(GTK_SCROLLED_WINDOW(shell->tree->scroll)->scrollbar);    \ +        gtk_range_set_value(CONCAT(range, __LINE__), value);                   \ +        gtk_adjustment_value_changed(GTK_ADJUSTMENT(                           \ +            gtk_range_get_adjustment(CONCAT(range, __LINE__))));               \ +    } while (0) +#define RANGE_GET_VALUE(tree, scrollbar)                                       \ +    gtk_range_get_value(                                                       \ +        GTK_RANGE(GTK_SCROLLED_WINDOW(shell->tree->scroll)->scrollbar)) +#endif + +static void +destroy_widget(GtkWidget *widget, gpointer user_data) +{ +    gtk_widget_destroy(widget); +} + +static void detail_view_clear(DetailView *detail_view) +{ +    gtk_container_forall(GTK_CONTAINER(shell->detail_view->view), +                         destroy_widget, NULL); +    RANGE_SET_VALUE(detail_view, vscrollbar, 0.0); +    RANGE_SET_VALUE(detail_view, hscrollbar, 0.0); +} + +static gboolean reload_section(gpointer data) +{ +    ShellModuleEntry *entry = (ShellModuleEntry *)data; +#if GTK_CHECK_VERSION(2, 14, 0) +    GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(shell->window)); +#endif + +    /* if the entry is still selected, update it */ +    if (entry->selected) { +        GtkTreePath *path = NULL; +        GtkTreeIter iter; +        double pos_info_scroll; +        double pos_detail_scroll; + +        /* save current position */ +#if GTK_CHECK_VERSION(3, 0, 0) +        /* TODO:GTK3 */ +#else +        pos_info_scroll = RANGE_GET_VALUE(info_tree, vscrollbar); +        pos_detail_scroll = RANGE_GET_VALUE(detail_view, vscrollbar); +#endif + +        /* avoid drawing the window while we reload */ +#if GTK_CHECK_VERSION(2, 14, 0) +        gdk_window_freeze_updates(gdk_window); +#else +        gdk_window_freeze_updates(shell->window->window); +#endif + +        /* gets the current selected path */ +        if (gtk_tree_selection_get_selected(shell->info_tree->selection, +                                            &shell->info_tree->model, &iter)) { +            path = gtk_tree_model_get_path(shell->info_tree->model, &iter); +        } + +        /* update the information, clear the treeview and populate it again */ +        module_entry_reload(entry); +        detail_view_clear(shell->detail_view); +        module_selected_show_info(entry, TRUE); + +        /* if there was a selection, reselect it */ +        if (path) { +            gtk_tree_selection_select_path(shell->info_tree->selection, path); +            gtk_tree_view_set_cursor(GTK_TREE_VIEW(shell->info_tree->view), +                                     path, NULL, FALSE); +            gtk_tree_path_free(path); +        } else { +            /* restore position */ +#if GTK_CHECK_VERSION(3, 0, 0) +            /* TODO:GTK3 */ +#else +            RANGE_SET_VALUE(info_tree, vscrollbar, pos_info_scroll); +#endif +        } + +#if !GTK_CHECK_VERSION(3, 0, 0) +        RANGE_SET_VALUE(detail_view, vscrollbar, pos_detail_scroll); +#endif + +        /* make the window drawable again */ +#if GTK_CHECK_VERSION(2, 14, 0) +        gdk_window_thaw_updates(gdk_window); +#else +        gdk_window_thaw_updates(shell->window->window); +#endif +    } + +    /* destroy the timeout: it'll be set up again */ +    return FALSE; +} + +static gboolean rescan_section(gpointer data) +{ +    ShellModuleEntry *entry = (ShellModuleEntry *) data; + +    module_entry_reload(entry); + +    return entry->selected; +} + +static gint +compare_float(float a, float b) +{ +    return (a > b) - (a < b); +} + +static gint +info_tree_compare_val_func(GtkTreeModel * model, +			   GtkTreeIter * a, +			   GtkTreeIter * b, gpointer userdata) +{ +    gint ret = 0; +    gchar *col1, *col2; + +    gtk_tree_model_get(model, a, INFO_TREE_COL_VALUE, &col1, -1); +    gtk_tree_model_get(model, b, INFO_TREE_COL_VALUE, &col2, -1); + +    if (!col1 && !col2) +        ret = 0; +    else if (!col1) +        ret = -1; +    else if (!col2) +        ret = 1; +    else if (shell->_order_type == SHELL_ORDER_ASCENDING) +        ret = compare_float(atof(col2), atof(col1)); +    else +        ret = compare_float(atof(col1), atof(col2)); + +    g_free(col1); +    g_free(col2); + +    return ret; +} + +static void set_view_type(ShellViewType viewtype, gboolean reload) +{ +#if GTK_CHECK_VERSION(2, 18, 0) +    GtkAllocation* alloc; +#endif +    gboolean type_changed = FALSE; +    if (viewtype != shell->view_type) +        type_changed = TRUE; + +    if (viewtype < SHELL_VIEW_NORMAL || viewtype >= SHELL_VIEW_N_VIEWS) +        viewtype = SHELL_VIEW_NORMAL; + +    shell->normalize_percentage = TRUE; +    shell->view_type = viewtype; +    shell->_order_type = SHELL_ORDER_DESCENDING; + +    /* use an unsorted tree model */ +    GtkTreeSortable *sortable = GTK_TREE_SORTABLE(shell->info_tree->model); + +    gtk_tree_sortable_set_sort_column_id(sortable, +        GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, +        GTK_SORT_ASCENDING); +    gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), +        GTK_TREE_MODEL(sortable)); + +    /* reset to the default view columns */ +    if (!reload) { +      gtk_tree_view_column_set_visible(shell->info_tree->col_extra1, FALSE); +      gtk_tree_view_column_set_visible(shell->info_tree->col_extra2, FALSE); +      gtk_tree_view_column_set_visible(shell->info_tree->col_progress, FALSE); +      gtk_tree_view_column_set_visible(shell->info_tree->col_value, TRUE); +    } + +    /* turn off the rules hint */ +#if GTK_CHECK_VERSION(3, 0, 0) +#else +    gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(shell->info_tree->view), FALSE); +#endif + +    close_note(NULL, NULL); +    detail_view_clear(shell->detail_view); + +    switch (viewtype) { +    default: +    case SHELL_VIEW_NORMAL: +        gtk_widget_show(shell->info_tree->scroll); +        gtk_widget_hide(shell->notebook); + +        if (!reload) { +            gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), FALSE); +        } +        break; +    case SHELL_VIEW_DUAL: +        gtk_widget_show(shell->info_tree->scroll); +        gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 1); +        gtk_widget_show(shell->notebook); + +        if (type_changed) { +#if GTK_CHECK_VERSION(2, 18, 0) +            alloc = g_new(GtkAllocation, 1); +            gtk_widget_get_allocation(shell->hbox, alloc); +            gtk_paned_set_position(GTK_PANED(shell->vpaned), alloc->height / 2); +            g_free(alloc); +#else +            gtk_paned_set_position(GTK_PANED(shell->vpaned), +                            shell->hbox->allocation.height / 2); +#endif +        } +        break; +    case SHELL_VIEW_LOAD_GRAPH: +        gtk_widget_show(shell->info_tree->scroll); +        gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 0); +        gtk_widget_show(shell->notebook); +        load_graph_clear(shell->loadgraph); + +        if (type_changed) { +#if GTK_CHECK_VERSION(2, 18, 0) +            alloc = g_new(GtkAllocation, 1); +            gtk_widget_get_allocation(shell->hbox, alloc); +            gtk_paned_set_position(GTK_PANED(shell->vpaned), +                    alloc->height - load_graph_get_height(shell->loadgraph) - 16); +            g_free(alloc); +#else +            gtk_paned_set_position(GTK_PANED(shell->vpaned), +                           shell->hbox->allocation.height - +                           load_graph_get_height(shell->loadgraph) - 16); +#endif +        } +        break; +    case SHELL_VIEW_PROGRESS_DUAL: +	gtk_widget_show(shell->notebook); + +	gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 1); +	/* fallthrough */ +    case SHELL_VIEW_PROGRESS: +        gtk_widget_show(shell->info_tree->scroll); + +	if (!reload) { +         gtk_tree_view_column_set_visible(shell->info_tree->col_progress, TRUE); +         gtk_tree_view_column_set_visible(shell->info_tree->col_value, FALSE); +        } + +	if (viewtype == SHELL_VIEW_PROGRESS) +		gtk_widget_hide(shell->notebook); +	break; +    case SHELL_VIEW_DETAIL: +        gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 1); + +        gtk_widget_show(shell->notebook); +        gtk_widget_hide(shell->info_tree->scroll); +    } +} + +static void group_handle_special(GKeyFile *key_file, +                                 ShellModuleEntry *entry, +                                 const gchar *group, +                                 gchar **keys) +{ +    if (!g_str_equal(group, "$ShellParam$")) { +        g_warning("Unknown parameter group: ``%s''", group); +        return; +    } + +    gboolean headers_visible = FALSE; +    gint i; + +    for (i = 0; keys[i]; i++) { +        gchar *key = keys[i]; + +        if (g_str_has_prefix(key, "UpdateInterval$")) { +            ShellFieldUpdate *fu = g_new0(ShellFieldUpdate, 1); +            ShellFieldUpdateSource *sfutbl; +            gint ms; + +            ms = g_key_file_get_integer(key_file, group, key, NULL); + +            /* Old style used just the label which has to be checked by translating it, +             * and potentially could by ambiguous. +             * New style can use tag or label. If new style including a tag, +             * send both tag and label and let the hi_get_field() function use +             * key_get_components() to split it. */ +            const gchar *chk = g_utf8_strchr(key, -1, '$'); +            fu->field_name = g_strdup(key_is_flagged(chk) ? chk : chk + 1); +            fu->entry = entry; + +            sfutbl = g_new0(ShellFieldUpdateSource, 1); +            sfutbl->source_id = g_timeout_add(ms, update_field, fu); +            sfutbl->sfu = fu; + +            update_sfusrc = g_slist_prepend(update_sfusrc, sfutbl); +        } else if (g_str_equal(key, "NormalizePercentage")) { +            shell->normalize_percentage = +                g_key_file_get_boolean(key_file, group, key, NULL); +        } else if (g_str_equal(key, "LoadGraphSuffix")) { +            gchar *suffix = g_key_file_get_value(key_file, group, key, NULL); +            load_graph_set_data_suffix(shell->loadgraph, suffix); +            g_free(suffix); +        } else if (g_str_equal(key, "ReloadInterval")) { +            gint ms; + +            ms = g_key_file_get_integer(key_file, group, key, NULL); + +            g_timeout_add(ms, reload_section, entry); +        } else if (g_str_equal(key, "RescanInterval")) { +            gint ms; + +            ms = g_key_file_get_integer(key_file, group, key, NULL); + +            g_timeout_add(ms, rescan_section, entry); +        } else if (g_str_equal(key, "ShowColumnHeaders")) { +            headers_visible = +                g_key_file_get_boolean(key_file, group, key, NULL); +        } else if (g_str_has_prefix(key, "ColumnTitle")) { +            GtkTreeViewColumn *column = NULL; +            gchar *value, *title = g_utf8_strchr(key, -1, '$') + 1; + +            value = g_key_file_get_value(key_file, group, key, NULL); + +            if (g_str_equal(title, "Extra1")) { +                column = shell->info_tree->col_extra1; +            } else if (g_str_equal(title, "Extra2")) { +                column = shell->info_tree->col_extra2; +            } else if (g_str_equal(title, "Value")) { +                column = shell->info_tree->col_value; +            } else if (g_str_equal(title, "TextValue")) { +                column = shell->info_tree->col_textvalue; +            } else if (g_str_equal(title, "Progress")) { +                column = shell->info_tree->col_progress; +            } + +            if (column) { +                gtk_tree_view_column_set_title(column, value); +                gtk_tree_view_column_set_visible(column, TRUE); +            } + +            g_free(value); +        } else if (g_str_equal(key, "OrderType")) { +            shell->_order_type = +                g_key_file_get_integer(key_file, group, key, NULL); +        } else if (g_str_has_prefix(key, "Icon$")) { +            struct UpdateTableItem *item; + +            const gchar *ikey = g_utf8_strchr(key, -1, '$'); +            gchar *tag, *name; +            key_get_components(ikey, NULL, &tag, &name, NULL, NULL, TRUE); + +            if (tag) +                item = g_hash_table_lookup(update_tbl, tag); +             else +                item = g_hash_table_lookup(update_tbl, name); +            g_free(name); +            g_free(tag); + +            if (item) { +                gchar *file = g_key_file_get_value(key_file, group, key, NULL); +                GdkPixbuf *pixbuf = icon_cache_get_pixbuf_at_size(file, 22, 22); + +                g_free(file); + +                if (item->is_iter) { +                    gtk_tree_store_set( +                        GTK_TREE_STORE(shell->info_tree->model), item->iter, +                        INFO_TREE_COL_PBUF, pixbuf, -1); +                } else { +                    GList *children = gtk_container_get_children(GTK_CONTAINER(item->widget)); +                    gtk_image_set_from_pixbuf(GTK_IMAGE(children->data), pixbuf); +                    gtk_widget_show(GTK_WIDGET(children->data)); +                    g_list_free(children); +                } +            } +        } else if (g_str_equal(key, "Zebra")) { +#if GTK_CHECK_VERSION(3, 0, 0) +#else +            gtk_tree_view_set_rules_hint( +                GTK_TREE_VIEW(shell->info_tree->view), +                g_key_file_get_boolean(key_file, group, key, NULL)); +#endif +        } +    } + +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), +                                      headers_visible); +} + +static void group_handle_normal(GKeyFile *key_file, +                                ShellModuleEntry *entry, +                                const gchar *group, +                                gchar **keys, +                                gsize ngroups) +{ +    GtkTreeIter parent; +    GtkTreeStore *store = GTK_TREE_STORE(shell->info_tree->model); +    gint i; + +    if (ngroups > 1) { +        gtk_tree_store_append(store, &parent, NULL); + +        gchar *tmp = g_strdup(group); +        strend(tmp, '#'); +        gtk_tree_store_set(store, &parent, INFO_TREE_COL_NAME, tmp, -1); +        g_free(tmp); +    } + +    g_key_file_set_list_separator(key_file, '|'); + +    for (i = 0; keys[i]; i++) { +        gchar *key = keys[i]; +        gchar **values; +        gsize vcount = 0; +        GtkTreeIter child; + +        values = g_key_file_get_string_list(key_file, group, key, &vcount, NULL); +        if (!vcount) { +            /* Check for empty value */ +            values = g_new0(gchar*, 2); +            values[0] = g_key_file_get_string(key_file, group, key, NULL); +            if (values[0]) { +                vcount = 1; +            } else { +                g_strfreev(values); +                continue; +            } +        } + +        if (entry->fieldfunc && values[0] && g_str_equal(values[0], "...")) { +            g_free(values[0]); +            values[0] = entry->fieldfunc(key); +        } + +        if (ngroups == 1) { +            gtk_tree_store_append(store, &child, NULL); +        } else { +            gtk_tree_store_append(store, &child, &parent); +        } + +        if (vcount > 0) +            gtk_tree_store_set(store, &child, INFO_TREE_COL_VALUE, +                               values[0], -1); +        if (vcount > 1) +            gtk_tree_store_set(store, &child, INFO_TREE_COL_EXTRA1, +                               values[1], -1); +        if (vcount > 2) +            gtk_tree_store_set(store, &child, INFO_TREE_COL_EXTRA2, +                               values[2], -1); + +        struct UpdateTableItem *item = g_new0(struct UpdateTableItem, 1); +        item->is_iter = TRUE; +        item->iter = gtk_tree_iter_copy(&child); +        gchar *flags, *tag, *name, *label; +        key_get_components(key, &flags, &tag, &name, &label, NULL, TRUE); + +        if (flags) { +            //TODO: name was formerly used where label is here. Check all uses +            //for problems. +            gtk_tree_store_set(store, &child, INFO_TREE_COL_NAME, label, +                               INFO_TREE_COL_DATA, flags, -1); +            g_hash_table_insert(update_tbl, tag, item); +            g_free(label); +        } else { +            gtk_tree_store_set(store, &child, INFO_TREE_COL_NAME, key, +                               INFO_TREE_COL_DATA, NULL, -1); +            g_hash_table_insert(update_tbl, name, item); +            g_free(tag); +        } +        g_free(flags); + +        g_strfreev(values); +    } +} + +static void update_progress() +{ +    GtkTreeModel *model = shell->info_tree->model; +    GtkTreeStore *store = GTK_TREE_STORE(model); +    GtkTreeIter iter, fiter; +    gchar *tmp; +    gdouble maxv = INT_MIN, minv = INT_MAX, coeff, cur; + +    if (!gtk_tree_model_get_iter_first(model, &fiter)) +	return; + +    /* finds the maximum value */ +    if (shell->normalize_percentage) { +	iter = fiter; +	do { +	    gtk_tree_model_get(model, &iter, INFO_TREE_COL_VALUE, &tmp, -1); + +	    cur = atof(tmp); +	    if (cur > maxv) +		maxv = cur; +	    if (cur < minv) +		minv = cur; + +	    g_free(tmp); +	} while (gtk_tree_model_iter_next(model, &iter)); + +	if (minv - maxv < 0.001) +	    maxv += 1.0f; +    } else { +	minv = 1.0f; +	maxv = 100.0f; +    } + +    coeff = (100.0f - 1.0f) / (maxv - minv); + +    /* fix the maximum relative percentage */ +    iter = fiter; +    do { +	char *space; +	char formatted[128]; +	gdouble pct; + +	gtk_tree_model_get(model, &iter, INFO_TREE_COL_VALUE, &tmp, -1); +	cur = atof(tmp); +	space = g_utf8_strchr(tmp, -1, ' '); + +	pct = coeff * (cur - minv) + 1.0f; +	if (shell->_order_type == SHELL_ORDER_ASCENDING) +	    pct = 100.0 - pct; +	pct = ceil(pct); + +	if (space) { +	    snprintf(formatted, sizeof(formatted), "%.2f%s", cur, space); +	} else { +	    snprintf(formatted, sizeof(formatted), "%.2f", cur); +	} + +	gtk_tree_store_set(store, &iter, INFO_TREE_COL_PROGRESS, pct, +			   INFO_TREE_COL_VALUE, strreplacechr(formatted, ",", +							      '.'), -1); + +	g_free(tmp); +    } while (gtk_tree_model_iter_next(model, &iter)); + +    /* now sort everything up. that wasn't as hard as i thought :) */ +    GtkTreeSortable *sortable = GTK_TREE_SORTABLE(shell->info_tree->model); + +    gtk_tree_sortable_set_sort_func(sortable, INFO_TREE_COL_VALUE, +				    info_tree_compare_val_func, 0, NULL); +    gtk_tree_sortable_set_sort_column_id(sortable, INFO_TREE_COL_VALUE, +					 GTK_SORT_DESCENDING); +    gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), +			    GTK_TREE_MODEL(sortable)); +} + +void shell_set_note_from_entry(ShellModuleEntry * entry) +{ +    if (entry->notefunc) { +	const gchar *note = module_entry_get_note(entry); + +	if (note) { +	    gtk_label_set_markup(GTK_LABEL(shell->note->label), note); +	    gtk_widget_show(shell->note->event_box); +	} else { +	    gtk_widget_hide(shell->note->event_box); +	} +    } else { +	gtk_widget_hide(shell->note->event_box); +    } +} + +void shell_clear_field_updates(void) +{ +    if (update_sfusrc) { +        GSList *sfusrc; + +        for (sfusrc = update_sfusrc; sfusrc; sfusrc = sfusrc->next) { +            ShellFieldUpdateSource *src = +                (ShellFieldUpdateSource *) sfusrc->data; +            g_source_remove(src->source_id); +            g_free(src->sfu->field_name); +            g_free(src->sfu); +            g_free(src); +        } + +        g_slist_free(update_sfusrc); +        update_sfusrc = NULL; +    } +} + +static gboolean +select_first_item(gpointer data) +{ +    GtkTreeIter first; + +    if (gtk_tree_model_get_iter_first(shell->info_tree->model, &first)) +        gtk_tree_selection_select_iter(shell->info_tree->selection, &first); + +    return FALSE; +} + +static gboolean select_marked_or_first_item(gpointer data) +{ +    GtkTreeIter first, it; +    gboolean found_selection = FALSE; +    gchar *datacol; + +    if (gtk_tree_model_get_iter_first(shell->info_tree->model, &first)) { +        it = first; +        while (gtk_tree_model_iter_next(shell->info_tree->model, &it)) { +            gtk_tree_model_get(shell->info_tree->model, &it, INFO_TREE_COL_DATA, +                               &datacol, -1); +            if (key_is_highlighted(datacol)) { +                gtk_tree_selection_select_iter(shell->info_tree->selection, +                                               &it); +                found_selection = TRUE; +            } +            g_free(datacol); +        } + +        if (!found_selection) +            gtk_tree_selection_select_iter(shell->info_tree->selection, &first); +    } +    return FALSE; +} + +static void module_selected_show_info_list(GKeyFile *key_file, +                                           ShellModuleEntry *entry, +                                           gchar **groups, +                                           gsize ngroups) +{ +    GtkTreeStore *store = GTK_TREE_STORE(shell->info_tree->model); +    gint i; + +    gtk_tree_store_clear(store); + +    g_object_ref(shell->info_tree->model); +    gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), NULL); + +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), +                                      FALSE); + +    for (i = 0; groups[i]; i++) { +        gchar **keys = g_key_file_get_keys(key_file, groups[i], NULL, NULL); + +        if (groups[i][0] == '$') { +            group_handle_special(key_file, entry, groups[i], keys); +        } else { +            group_handle_normal(key_file, entry, groups[i], keys, ngroups); +        } + +        g_strfreev(keys); +    } + +    g_object_unref(shell->info_tree->model); +    gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), +                            shell->info_tree->model); +    gtk_tree_view_expand_all(GTK_TREE_VIEW(shell->info_tree->view)); +    gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(shell->info_tree->view), +                                     ngroups > 1); +} + +static gboolean detail_activate_link (GtkLabel *label, gchar *uri, gpointer user_data) { +    return uri_open(uri); +} + +static gchar *vendor_info_markup(const Vendor *v) { +    if (!v) return NULL; +    gchar *ven_mt = NULL; +    gchar *full_link = NULL, *p = NULL; +    gchar *ven_tag = v->name_short ? g_strdup(v->name_short) : g_strdup(v->name); +    tag_vendor(&ven_tag, 0, ven_tag, v->ansi_color, FMT_OPT_PANGO); +    //if (v->name_short) +    //    ven_mt = appf(ven_mt, "\n", "%s", v->name); +    ven_mt = appf(ven_mt, "\n", "%s", ven_tag); +    if (v->url) { +        if (!g_str_has_prefix(v->url, "http") ) +            full_link = g_strdup_printf("http://%s", v->url); +        ven_mt = appf(ven_mt, "\n", "<b>%s:</b> <a href=\"%s\">%s</a>", _("URL"), full_link ? full_link : v->url, v->url); +        g_free(full_link); +        full_link = NULL; +    } +    if (v->url_support) { +        if (!g_str_has_prefix(v->url_support, "http") ) +            full_link = g_strdup_printf("http://%s", v->url_support); +        ven_mt = appf(ven_mt, "\n", "<b>%s:</b> <a href=\"%s\">%s</a>", _("Support URL"), full_link ? full_link : v->url_support, v->url_support); +        g_free(full_link); +        full_link = NULL; +    } +    if (v->wikipedia) { +        /* sending the title to wikipedia.com/wiki will autmatically handle the language and section parts, +         * but perhaps that shouldn't be relied on so much? */ +        full_link = g_strdup_printf("http://wikipedia.com/wiki/%s", v->wikipedia); +        for(p = full_link; *p; p++) { +            if (*p == ' ') *p = '_'; +        } +        ven_mt = appf(ven_mt, "\n", "<b>%s:</b> <a href=\"%s\">%s</a>", _("Wikipedia"), full_link ? full_link : v->wikipedia, v->wikipedia); +        g_free(full_link); +        full_link = NULL; +    } +    g_free(ven_tag); +    return ven_mt; +} + +static void module_selected_show_info_detail(GKeyFile *key_file, +                                             ShellModuleEntry *entry, +                                             gchar **groups) +{ +    gint i; + +    detail_view_clear(shell->detail_view); + +    for (i = 0; groups[i]; i++) { +        gsize nkeys; +        gchar **keys = g_key_file_get_keys(key_file, groups[i], &nkeys, NULL); +        gchar *group_label = g_strdup(groups[i]); +        strend(group_label, '#'); + +        if (entry && groups[i][0] == '$') { +            group_handle_special(key_file, entry, groups[i], keys); +        } else { +            gchar *tmp = g_strdup_printf("<b>%s</b>", group_label); +            GtkWidget *label = gtk_label_new(tmp); +            gtk_label_set_use_markup(GTK_LABEL(label), TRUE); +            GtkWidget *frame = gtk_frame_new(NULL); +            gtk_frame_set_label_widget(GTK_FRAME(frame), label); +            gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); +            g_free(tmp); + +            gtk_container_set_border_width(GTK_CONTAINER(frame), 6); +            gtk_box_pack_start(GTK_BOX(shell->detail_view->view), frame, FALSE, +                               FALSE, 0); + +            GtkWidget *table = gtk_table_new(nkeys, 2, FALSE); +            gtk_container_set_border_width(GTK_CONTAINER(table), 4); +            gtk_container_add(GTK_CONTAINER(frame), table); + +            gint j, a = 0; +            for (j = 0; keys[j]; j++) { +                gchar *key_markup; +                gchar *value; +                gchar *name, *label, *tag, *flags; +                key_get_components(keys[j], &flags, &tag, &name, &label, NULL, TRUE); + +                value = g_key_file_get_string(key_file, groups[i], keys[j], NULL); + +                if (entry && entry->fieldfunc && value && g_str_equal(value, "...")) { +                    g_free(value); +                    value = entry->fieldfunc(keys[j]); +                } + +                gboolean has_ven = key_value_has_vendor_string(flags); +                const Vendor *v = has_ven ? vendor_match(value, NULL) : NULL; + +                key_markup = +                    g_strdup_printf("<span color=\"#666\">%s</span>", label); + +                GtkWidget *key_label = gtk_label_new(key_markup); +                gtk_label_set_use_markup(GTK_LABEL(key_label), TRUE); +                gtk_misc_set_alignment(GTK_MISC(key_label), 1.0f, 0.5f); + +                GtkWidget *value_label = gtk_label_new(value); +                gtk_label_set_use_markup(GTK_LABEL(value_label), TRUE); +                gtk_label_set_selectable(GTK_LABEL(value_label), TRUE); +#if !GTK_CHECK_VERSION(3, 0, 0) +                gtk_label_set_line_wrap(GTK_LABEL(value_label), TRUE); +#endif +                gtk_misc_set_alignment(GTK_MISC(value_label), 0.0f, 0.5f); + +                GtkWidget *value_icon = gtk_image_new(); + +                GtkWidget *value_box = gtk_hbox_new(FALSE, 4); +                gtk_box_pack_start(GTK_BOX(value_box), value_icon, FALSE, FALSE, 0); +                gtk_box_pack_start(GTK_BOX(value_box), value_label, TRUE, TRUE, 0); + +                g_signal_connect(key_label, "activate-link", G_CALLBACK(detail_activate_link), NULL); +                g_signal_connect(value_label, "activate-link", G_CALLBACK(detail_activate_link), NULL); + +                gtk_widget_show(key_label); +                gtk_widget_show(value_box); +                gtk_widget_show(value_label); + +                gtk_table_attach(GTK_TABLE(table), key_label, 0, 1, j + a, j + a + 1, +                                 GTK_FILL, GTK_FILL, 6, 4); +                gtk_table_attach(GTK_TABLE(table), value_box, 1, 2, j + a, j + a + 1, +                                 GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 4); + +                if (v) { +                    a++; /* insert a row */ +                    gchar *vendor_markup = vendor_info_markup(v); +                    GtkWidget *vendor_label = gtk_label_new(vendor_markup); +                    gtk_label_set_use_markup(GTK_LABEL(vendor_label), TRUE); +                    gtk_label_set_selectable(GTK_LABEL(vendor_label), TRUE); +                    gtk_misc_set_alignment(GTK_MISC(vendor_label), 0.0f, 0.5f); +                    g_signal_connect(vendor_label, "activate-link", G_CALLBACK(detail_activate_link), NULL); +                    GtkWidget *vendor_box = gtk_hbox_new(FALSE, 4); +                    gtk_box_pack_start(GTK_BOX(vendor_box), vendor_label, TRUE, TRUE, 0); +                    gtk_table_attach(GTK_TABLE(table), vendor_box, 1, 2, j + a, j + a + 1, +                                     GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 4); +                    gtk_widget_show(vendor_box); +                    gtk_widget_show(vendor_label); +                    g_free(vendor_markup); +                } + +                struct UpdateTableItem *item = g_new0(struct UpdateTableItem, 1); +                item->is_iter = FALSE; +                item->widget = g_object_ref(value_box); + +                if (tag) { +                    g_hash_table_insert(update_tbl, tag, item); +                    g_free(name); +                } else { +                    g_hash_table_insert(update_tbl, name, item); +                    g_free(tag); +                } + +                g_free(flags); +                g_free(value); +                g_free(key_markup); +                g_free(label); +            } + +            gtk_widget_show(table); +            gtk_widget_show(label); +            gtk_widget_show(frame); +        } + +        g_strfreev(keys); +        g_free(group_label); +    } +} + +static void +module_selected_show_info(ShellModuleEntry *entry, gboolean reload) +{ +    GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(shell->info_tree->view)); +    gsize ngroups; +    gint i; + +    gdk_window_freeze_updates(gdk_window); + +    module_entry_scan(entry); +    if (!reload) { +        /* recreate the iter hash table */ +        h_hash_table_remove_all(update_tbl); +    } +    shell_clear_field_updates(); + +    GKeyFile *key_file = g_key_file_new(); +    gchar *key_data = module_entry_function(entry); + +    g_key_file_load_from_data(key_file, key_data, strlen(key_data), 0, NULL); +    set_view_type(g_key_file_get_integer(key_file, "$ShellParam$", +                                         "ViewType", NULL), reload); + +    gchar **groups = g_key_file_get_groups(key_file, &ngroups); + +    for (i = 0; groups[i]; i++) { +	if (groups[i][0] == '$') +	    ngroups--; +    } + +    if (shell->view_type == SHELL_VIEW_DETAIL) { +        module_selected_show_info_detail(key_file, entry, groups); +    } else { +        module_selected_show_info_list(key_file, entry, groups, ngroups); +    } + +    g_strfreev(groups); +    g_key_file_free(key_file); +    g_free(key_data); + +    switch (shell->view_type) { +    case SHELL_VIEW_PROGRESS_DUAL: +    case SHELL_VIEW_PROGRESS: +        update_progress(); +        break; +    } + +    if (!reload) { +        switch (shell->view_type) { +        case SHELL_VIEW_DUAL: +        case SHELL_VIEW_LOAD_GRAPH: +        case SHELL_VIEW_PROGRESS_DUAL: +            g_idle_add(select_marked_or_first_item, NULL); +        } +    } +    shell_set_note_from_entry(entry); + +    gdk_window_thaw_updates(gdk_window); +} + +static void info_selected_show_extra(const gchar *tag) +{ +    if (!tag || !shell->selected->morefunc) +        return; + +    GKeyFile *key_file = g_key_file_new(); +    gchar *key_data = shell->selected->morefunc((gchar *)tag); +    gchar **groups; + +    g_key_file_load_from_data(key_file, key_data, strlen(key_data), 0, NULL); +    groups = g_key_file_get_groups(key_file, NULL); + +    module_selected_show_info_detail(key_file, NULL, groups); + +    g_strfreev(groups); +    g_key_file_free(key_file); +    g_free(key_data); +} + +static gchar *detail_view_clear_value(gchar *value) +{ +     GKeyFile *keyfile; +     gchar *return_value; + +     keyfile = g_key_file_new(); +     if (!value) return_value = g_strdup(""); +     else +     if (g_key_file_load_from_data(keyfile, value, +                                   strlen(value), 0, NULL)) { +          gchar **groups; +          gint group; + +          return_value = g_strdup(""); + +          groups = g_key_file_get_groups(keyfile, NULL); +          for (group = 0; groups[group]; group++) { +               gchar **keys; +               gint key; + +               keys = g_key_file_get_keys(keyfile, groups[group], NULL, NULL); +               for (key = 0; keys[key]; key++) { +                  gchar *temp = keys[key]; + +                  if (*temp == '$') { +                     temp++; +                     while (*temp && *temp != '$') +                        temp++; +                     temp++; + +                     return_value = h_strdup_cprintf("%s\n", return_value, temp); +                  } else { +                     return_value = g_key_file_get_string(keyfile, groups[group], +                                                          keys[key], NULL); +                  } +               } + +               g_strfreev(keys); +          } + +          g_strfreev(groups); +     } else { +          return_value = g_strdup(value); +     } + +     g_key_file_free(keyfile); + +     return g_strstrip(return_value); +} + +static void detail_view_add_item(DetailView *detail_view, +                                 gchar *icon, +                                 gchar *name, +                                 gchar *value) +{ +    GtkWidget *frame; +    GtkWidget *frame_label_box; +    GtkWidget *frame_image; +    GtkWidget *frame_label; +    GtkWidget *content; +    GtkWidget *alignment; +    gchar *temp; + +    temp = detail_view_clear_value(value); + +    /* creates the frame */ +    frame = gtk_frame_new(NULL); +    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); + +#if GTK_CHECK_VERSION(3, 0, 0) +    frame_label_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +#else +    frame_label_box = gtk_hbox_new(FALSE, 5); +#endif +    frame_image = icon_cache_get_image(icon); +    frame_label = gtk_label_new(name); +    gtk_label_set_use_markup(GTK_LABEL(frame_label), TRUE); +    gtk_box_pack_start(GTK_BOX(frame_label_box), frame_image, FALSE, FALSE, 0); +    gtk_box_pack_start(GTK_BOX(frame_label_box), frame_label, FALSE, FALSE, 0); + +    content = gtk_label_new(temp); +    /* TODO:GTK3 gtk_alignment_new(), etc is deprecated from 3.14 */ +#if GTK_CHECK_VERSION(3, 0, 0) +    GtkWidget *frame_box; +    frame_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +    gtk_widget_set_margin_start(GTK_WIDGET(frame_box), 48); +    gtk_box_pack_start(GTK_BOX(frame_box), content, FALSE, FALSE, 0); +    gtk_container_add(GTK_CONTAINER(frame), frame_box); +#else +    alignment = gtk_alignment_new(0.5, 0.5, 1, 1); +    gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 48, 0); +    gtk_widget_show(alignment); +    gtk_container_add(GTK_CONTAINER(frame), alignment); +    gtk_misc_set_alignment(GTK_MISC(content), 0.0, 0.5); +    gtk_container_add(GTK_CONTAINER(alignment), content); +#endif + +    gtk_widget_show_all(frame); +    gtk_widget_show_all(frame_label_box); + +    gtk_frame_set_label_widget(GTK_FRAME(frame), frame_label_box); + +    /* pack the item on the detail_view screen */ +    gtk_box_pack_start(GTK_BOX(shell->detail_view->view), frame, FALSE, FALSE, +                       4); + +    g_free(temp); +} + +static void detail_view_create_header(DetailView *detail_view, +                                        gchar *title) +{ +    GtkWidget *header, *label; +    gchar *temp; + +    temp = g_strdup_printf(_("<b>%s \342\206\222 Summary</b>"), title); + +    header = gtk_menu_item_new_with_label(temp); +    gtk_menu_item_select(GTK_MENU_ITEM(header)); +    gtk_widget_show(header); + +    label = gtk_bin_get_child(GTK_BIN(header)); +    gtk_label_set_use_markup(GTK_LABEL(label), TRUE); + +    gtk_box_pack_start(GTK_BOX(shell->detail_view->view), header, FALSE, FALSE, 4); + +    g_free(temp); +} + +static void shell_show_detail_view(void) +{ +    GKeyFile *keyfile; +    gchar *detail; + +    set_view_type(SHELL_VIEW_DETAIL, FALSE); +    detail_view_clear(shell->detail_view); +    detail_view_create_header(shell->detail_view, shell->selected_module->name); + +    keyfile = g_key_file_new(); +    detail = shell->selected_module->summaryfunc(); + +    if (g_key_file_load_from_data(keyfile, detail, +                                  strlen(detail), 0, NULL)) { +         gchar **groups; +         gint group; + +         groups = g_key_file_get_groups(keyfile, NULL); + +         for (group = 0; groups[group]; group++) { +             gchar *icon, *method, *method_result; + +             shell_status_pulse(); + +             icon = g_key_file_get_string(keyfile, groups[group], "Icon", NULL); +             method = g_key_file_get_string(keyfile, groups[group], "Method", NULL); +             if (method) { +                  method_result = module_call_method(method); +             } else { +                  method_result = g_strdup("N/A"); +             } + +             detail_view_add_item(shell->detail_view, +                                    icon, groups[group], method_result); +             shell_status_pulse(); + +             g_free(icon); +             g_free(method); +             g_free(method_result); +         } + +         g_strfreev(groups); +    } else { +         DEBUG("error while parsing detail_view"); +         set_view_type(SHELL_VIEW_NORMAL, FALSE); +    } + +    g_free(detail); +    g_key_file_free(keyfile); + +    shell_view_set_enabled(TRUE); +} + +static void module_selected(gpointer data) +{ +    ShellTree *shelltree = shell->tree; +    GtkTreeModel *model = GTK_TREE_MODEL(shelltree->model); +    GtkTreeIter iter, parent; +    ShellModuleEntry *entry; +    static ShellModuleEntry *current = NULL; +    static gboolean updating = FALSE; +    GtkScrollbar *hscrollbar, *vscrollbar; + +    /* Gets the currently selected item on the left-side TreeView; if there is +       no selection, silently return */ +    if (!gtk_tree_selection_get_selected(shelltree->selection, &model, &iter)) { +        return; +    } + +    /* Mark the currently selected module as "unselected"; this is used to kill +       the update timeout. */ +    if (current) { +        current->selected = FALSE; +    } + +    if (updating) { +        return; +    } else { +        updating = TRUE; +    } + +    if (!gtk_tree_model_iter_parent(model, &parent, &iter)) { +        memcpy(&parent, &iter, sizeof(iter)); +    } + +    gtk_tree_model_get(model, &parent, TREE_COL_MODULE, &shell->selected_module, +                       -1); + +    /* Get the current selection and shows its related info */ +    gtk_tree_model_get(model, &iter, TREE_COL_MODULE_ENTRY, &entry, -1); +    if (entry && !entry->selected) { +        gchar *title; + +        shell_status_set_enabled(TRUE); +        shell_status_update(_("Updating...")); + +        entry->selected = TRUE; +        shell->selected = entry; +        module_selected_show_info(entry, FALSE); + +        gtk_tree_view_columns_autosize(GTK_TREE_VIEW(shell->info_tree->view)); + +        /* urgh. why don't GTK do this when the model is cleared? */ +#if GTK_CHECK_VERSION(3, 0, 0) +        /* TODO:GTK3 */ +#else +        RANGE_SET_VALUE(info_tree, vscrollbar, 0.0); +        RANGE_SET_VALUE(info_tree, hscrollbar, 0.0); +        RANGE_SET_VALUE(detail_view, vscrollbar, 0.0); +        RANGE_SET_VALUE(detail_view, hscrollbar, 0.0); +#endif + +        title = g_strdup_printf("%s - %s", shell->selected_module->name, +                                entry->name); +        shell_set_title(shell, title); +        g_free(title); + +        shell_action_set_enabled("RefreshAction", TRUE); +        shell_action_set_enabled("CopyAction", TRUE); + +        shell_status_update(_("Done.")); +        shell_status_set_enabled(FALSE); +    } else { +        shell_set_title(shell, NULL); +        shell_action_set_enabled("RefreshAction", FALSE); +        shell_action_set_enabled("CopyAction", FALSE); + +        gtk_tree_store_clear(GTK_TREE_STORE(shell->info_tree->model)); +        set_view_type(SHELL_VIEW_NORMAL, FALSE); + +        if (shell->selected_module->summaryfunc) { +            shell_show_detail_view(); +        } +    } + +    current = entry; +    updating = FALSE; +} + +static void info_selected(GtkTreeSelection * ts, gpointer data) +{ +    ShellInfoTree *info = (ShellInfoTree *) data; +    GtkTreeModel *model = GTK_TREE_MODEL(info->model); +    GtkTreeIter parent; +    gchar *datacol, *mi_tag; + +    if (!gtk_tree_selection_get_selected(ts, &model, &parent)) +	return; + +    if (shell->view_type == SHELL_VIEW_NORMAL || +        shell->view_type == SHELL_VIEW_PROGRESS) { +        gtk_tree_selection_unselect_all(ts); +        return; +    } + +    gtk_tree_model_get(model, &parent, INFO_TREE_COL_DATA, &datacol, -1); +    mi_tag = key_mi_tag(datacol); +    info_selected_show_extra(mi_tag); +    g_free(mi_tag); +} + +static ShellInfoTree *info_tree_new(void) +{ +    ShellInfoTree *info; +    GtkWidget *treeview, *scroll; +    GtkTreeModel *model; +    GtkTreeStore *store; +    GtkTreeViewColumn *column; +    GtkCellRenderer *cr_text, *cr_pbuf, *cr_progress; +    GtkTreeSelection *sel; + +    info = g_new0(ShellInfoTree, 1); + +    scroll = gtk_scrolled_window_new(NULL, NULL); +    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW +					(scroll), GTK_SHADOW_IN); +    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), +				   GTK_POLICY_AUTOMATIC, +				   GTK_POLICY_ALWAYS); + +    store = +	gtk_tree_store_new(INFO_TREE_NCOL, G_TYPE_STRING, G_TYPE_STRING, +			   G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_FLOAT, +                           G_TYPE_STRING, G_TYPE_STRING); +    model = GTK_TREE_MODEL(store); +    treeview = gtk_tree_view_new_with_model(model); +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); +    gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE); + +    info->col_progress = column = gtk_tree_view_column_new(); +    gtk_tree_view_column_set_visible(column, FALSE); +    gtk_tree_view_column_set_min_width(column, 240); +    gtk_tree_view_column_set_clickable(column, TRUE); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + +    cr_progress = gtk_cell_renderer_progress_new(); +    gtk_tree_view_column_pack_start(column, cr_progress, TRUE); +    gtk_tree_view_column_add_attribute(column, cr_progress, "value", +				       INFO_TREE_COL_PROGRESS); +    gtk_tree_view_column_add_attribute(column, cr_progress, "text", +				       INFO_TREE_COL_VALUE); +    gtk_tree_view_column_set_visible(column, FALSE); + +    info->col_textvalue = column = gtk_tree_view_column_new(); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); +    gtk_tree_view_column_set_clickable(column, TRUE); + +    cr_pbuf = gtk_cell_renderer_pixbuf_new(); +    gtk_tree_view_column_pack_start(column, cr_pbuf, FALSE); +    gtk_tree_view_column_add_attribute(column, cr_pbuf, "pixbuf", +				       INFO_TREE_COL_PBUF); + +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_text, TRUE); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", +				       INFO_TREE_COL_NAME); + +    info->col_extra1 = column = gtk_tree_view_column_new(); +    gtk_tree_view_column_set_visible(column, FALSE); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); +    gtk_tree_view_column_set_clickable(column, TRUE); + +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_text, FALSE); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", +				       INFO_TREE_COL_EXTRA1); + +    info->col_extra2 = column = gtk_tree_view_column_new(); +    gtk_tree_view_column_set_visible(column, FALSE); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); +    gtk_tree_view_column_set_clickable(column, TRUE); + +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_text, FALSE); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", +				       INFO_TREE_COL_EXTRA2); + +    info->col_value = column = gtk_tree_view_column_new(); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); +    gtk_tree_view_column_set_clickable(column, TRUE); + +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_text, FALSE); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", +				       INFO_TREE_COL_VALUE); + +    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + +    g_signal_connect(G_OBJECT(sel), "changed", (GCallback)info_selected, info); + +    gtk_container_add(GTK_CONTAINER(scroll), treeview); + +    info->scroll = scroll; +    info->view = treeview; +    info->model = model; +    info->selection = sel; + +    gtk_widget_show_all(scroll); + +    return info; +} + +static ShellTree *tree_new() +{ +    ShellTree *shelltree; +    GtkWidget *treeview, *scroll; +    GtkTreeModel *model; +    GtkTreeStore *store; +    GtkCellRenderer *cr_text, *cr_pbuf; +    GtkTreeViewColumn *column; +    GtkTreeSelection *sel; + +    shelltree = g_new0(ShellTree, 1); + +    scroll = gtk_scrolled_window_new(NULL, NULL); +    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW +					(scroll), GTK_SHADOW_IN); +    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), +				   GTK_POLICY_NEVER, +				   GTK_POLICY_AUTOMATIC); + +    store = gtk_tree_store_new(TREE_NCOL, GDK_TYPE_PIXBUF, G_TYPE_STRING, +			       G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_BOOLEAN); +    model = GTK_TREE_MODEL(store); +    treeview = gtk_tree_view_new_with_model(model); +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + +#if GTK_CHECK_VERSION(2,12,0) +    gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(treeview), FALSE); +    gtk_tree_view_set_level_indentation(GTK_TREE_VIEW(treeview), 24); +#endif + +    column = gtk_tree_view_column_new(); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + +    cr_pbuf = gtk_cell_renderer_pixbuf_new(); +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_pbuf, FALSE); +    gtk_tree_view_column_pack_start(column, cr_text, TRUE); + +    gtk_tree_view_column_add_attribute(column, cr_pbuf, "pixbuf", +				       TREE_COL_PBUF); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", +				       TREE_COL_NAME); + +    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); +    g_signal_connect(G_OBJECT(sel), "changed", (GCallback) module_selected, +		     NULL); + +    gtk_container_add(GTK_CONTAINER(scroll), treeview); + +    shelltree->scroll = scroll; +    shelltree->view = treeview; +    shelltree->model = model; +    shelltree->modules = NULL; +    shelltree->selection = sel; + +    gtk_widget_show_all(scroll); + +    return shelltree; +} + +gboolean key_is_flagged(const gchar *key) { +    return (key && *key == '$' && strchr(key+1, '$')) ? TRUE : FALSE; +} + +gboolean key_is_highlighted(const gchar *key) { +    gchar *flags; +    key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); +    if (flags && strchr(flags, '*')) { +        g_free(flags); +        return TRUE; +    } +    return FALSE; +} + +gboolean key_wants_details(const gchar *key) { +    gchar *flags; +    key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); +    if (flags && strchr(flags, '!')) { +        g_free(flags); +        return TRUE; +    } +    return FALSE; +} + +gboolean key_value_has_vendor_string(const gchar *key) { +    gchar *flags; +    key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); +    if (flags && strchr(flags, '^')) { +        g_free(flags); +        return TRUE; +    } +    return FALSE; +} + +gboolean key_label_is_escaped(const gchar *key) { +    gchar *flags; +    key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); +    if (flags && strchr(flags, '@')) { +        g_free(flags); +        return TRUE; +    } +    return FALSE; +} + +gchar *key_mi_tag(const gchar *key) { +    static char flag_list[] = "*!^@"; +    gchar *p = (gchar*)key, *l, *t; + +    if (key_is_flagged(key)) { +        l = strchr(key+1, '$'); +        if (*p == '$') p++; /* skip first if exists */ +        while(p < l && strchr(flag_list, *p)) { +            p++; +        } +        if (strlen(p)) { +            t = g_strdup(p); +            *(strchr(t, '$')) = 0; +            return t; +        } +    } +    return NULL; +} + +const gchar *key_get_name(const gchar *key) { +    if (key_is_flagged(key)) +        return strchr(key+1, '$')+1; +    return key; +} + +/* key syntax: + *  [$[<flags>][<tag>]$]<name>[#[<dis>]] + * + * example for key = "$*!Foo$Bar#7": + * flags = "$*!^Foo$"  // key_is/wants_*() still works on flags + * tag = "Foo"        // the moreinfo/icon tag + * name = "Bar#7"     // the full unique name + * label = "Bar"      // the label displayed + * dis = "7" + */ +void key_get_components(const gchar *key, +    gchar **flags, gchar **tag, gchar **name, gchar **label, gchar **dis, +    gboolean null_empty) { + +    if (null_empty) { +#define K_NULL_EMPTY(f) if (f) { *f = NULL; } +        K_NULL_EMPTY(flags); +        K_NULL_EMPTY(tag); +        K_NULL_EMPTY(name); +        K_NULL_EMPTY(label); +        K_NULL_EMPTY(dis); +    } + +    if (!key || !*key) +        return; + +    const gchar *np = g_utf8_strchr(key+1, -1, '$') + 1; +    if (*key == '$' && np) { +        /* is flagged */ +        gchar *f = g_strdup(key); +        gchar *s = g_utf8_strchr(f+1, -1, '$'); +        if(s==NULL) { +	    DEBUG("ERROR NOT FOUND"); +        }else{ + /*            if((s-f+1)>strlen(key)) { +	    DEBUG("ERROR TOO LATE"); +        }else{*/ +	  *(g_utf8_strchr(f+1, -1, '$') + 1) = 0; +	  if (flags) +	    *flags = g_strdup(f); +	  if (tag) +	    *tag = key_mi_tag(f); +	  g_free(f); +	  //} +	} +    } else +        np = key; + +    if (name) +        *name = g_strdup(np); +    if (label) { +        *label = g_strdup(np); +        gchar *lbp = g_utf8_strchr(*label, -1, '#'); +        if (lbp) +            *lbp = 0; +        if (lbp && dis) +            *dis = g_strdup(lbp + 1); + +        if (flags && *flags && strchr(*flags, '@')) { +            gchar *ol = *label; +            *label = g_strcompress(ol); +            g_free(ol); +        } +    } +} diff --git a/shell/stock.c b/shell/stock.c new file mode 100644 index 00000000..27b2eedb --- /dev/null +++ b/shell/stock.c @@ -0,0 +1,93 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br> + * + *    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, version 2 or later. + * + *    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 St, Fifth Floor, Boston, MA  02110-1301 USA + */ + +#include <config.h> +#include <gtk/gtk.h> +#include <stock.h> +#include <iconcache.h> + +static struct { +    gchar *filename; +    gchar *stock_id; +} stock_icons[] = { +    { "clipboard.png", HI_STOCK_CLIPBOARD}, +    { "refresh.png", HI_STOCK_REFRESH}, +    { "report.png", HI_STOCK_REPORT}, +    { "internet.png", HI_STOCK_INTERNET}, +    { "module.png", HI_STOCK_MODULE}, +    { "about-modules.png", HI_STOCK_ABOUT_MODULES}, +    { "server_sync.png", HI_STOCK_SYNC_MENU}, +    { "face-grin.png", HI_STOCK_DONATE}, +    { "server.png", HI_STOCK_SERVER}, +}; + +static GtkIconFactory *icon_factory; + +void stock_icon_register(gchar * filename, gchar * stock_id) +{ +    GtkIconSet *icon_set; +    GtkIconSource *icon_source; + +    icon_set = gtk_icon_set_new(); +    icon_source = gtk_icon_source_new(); + +    gtk_icon_source_set_pixbuf(icon_source, +			       icon_cache_get_pixbuf(filename)); +    gtk_icon_set_add_source(icon_set, icon_source); +    gtk_icon_source_free(icon_source); + +    gtk_icon_factory_add(icon_factory, stock_id, icon_set); + +    gtk_icon_set_unref(icon_set); +} + +void stock_icon_register_pixbuf(GdkPixbuf * pixbuf, gchar * stock_id) +{ +    GtkIconSet *icon_set; +    GtkIconSource *icon_source; + +    icon_set = gtk_icon_set_new(); +    icon_source = gtk_icon_source_new(); + +    gtk_icon_source_set_pixbuf(icon_source, pixbuf); +    gtk_icon_set_add_source(icon_set, icon_source); +    gtk_icon_source_free(icon_source); + +    gtk_icon_factory_add(icon_factory, stock_id, icon_set); + +    gtk_icon_set_unref(icon_set); +} + +void stock_icons_init(void) +{ +    guint i; +    guint n_stock_icons = G_N_ELEMENTS(stock_icons); + +    DEBUG("initializing stock icons"); + +    icon_factory = gtk_icon_factory_new(); + +    for (i = 0; i < n_stock_icons; i++) { +	stock_icon_register(stock_icons[i].filename, +			    stock_icons[i].stock_id); +    } + +    gtk_icon_factory_add_default(icon_factory); + +    g_object_unref(icon_factory); +} diff --git a/shell/syncmanager.c b/shell/syncmanager.c new file mode 100644 index 00000000..0f08cf87 --- /dev/null +++ b/shell/syncmanager.c @@ -0,0 +1,734 @@ +/* + *    HardInfo - Displays System Information + *    Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br> + * + *    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, version 2 or later. + * + *    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 St, Fifth Floor, Boston, MA  02110-1301 USA + */ + +#include "config.h" +#include "hardinfo.h" +#include "iconcache.h" +#include "syncmanager.h" + +#include <libsoup/soup.h> + +#include <stdarg.h> +#include <string.h> + +#ifndef SOUP_CHECK_VERSION +    #define SOUP_CHECK_VERSION(a,b,c) 0 +#endif + +typedef struct _SyncDialog SyncDialog; +typedef struct _SyncNetArea SyncNetArea; +typedef struct _SyncNetAction SyncNetAction; + +struct _SyncNetArea { +    GtkWidget *vbox; +}; + +struct _SyncNetAction { +    SyncEntry *entry; +    GError *error; +}; + +struct _SyncDialog { +    GtkWidget *dialog; +    GtkWidget *label; + +    GtkWidget *button_sync; +    GtkWidget *button_cancel; +    GtkWidget *button_close; +    GtkWidget *button_priv_policy; + +    GtkWidget *scroll_box; + +    SyncNetArea *sna; + +    gboolean flag_cancel : 1; +}; + +static GSList *entries = NULL; +static SoupSession *session = NULL; +static GMainLoop *loop; +static GQuark err_quark; + +//Note there are personal information involved and very old +//linux systems does not work with HTTPS so use HTTP for now +#define API_SERVER_URI "http://api.hardinfo2.org" + +#define LABEL_SYNC_DEFAULT                                                     \ +    _("<big><b>Synchronize with Central Database</b></big>\n"                  \ +      "The following information may be synchronized\n"                         \ +      "with the HardInfo central database.") +#define LABEL_SYNC_SYNCING                                                     \ +    _("<big><b>Synchronizing</b></big>\n"                                      \ +      "This may take some time.") + +static SyncDialog *sync_dialog_new(GtkWidget *parent); +static void sync_dialog_destroy(SyncDialog *sd); +static void sync_dialog_start_sync(SyncDialog *sd); + +static SyncNetArea *sync_dialog_netarea_new(void); +static void sync_dialog_netarea_destroy(SyncNetArea *sna); +static void sync_dialog_netarea_show(SyncDialog *sd); +#if 0 +static void sync_dialog_netarea_hide(SyncDialog * sd); +#endif +static void +sync_dialog_netarea_start_actions(SyncDialog *sd, SyncNetAction *sna, gint n); + +#define SNA_ERROR(code, message, ...)                                          \ +    if (!sna->error) {                                                         \ +        sna->error = g_error_new(err_quark, code, message, ##__VA_ARGS__);     \ +    } + +gint sync_manager_count_entries(void) +{ +    return g_slist_length(entries); +} + +void sync_manager_add_entry(SyncEntry *entry) +{ +    DEBUG("registering syncmanager entry ''%s''", entry->name); + +    entry->selected = TRUE; +    entries = g_slist_append(entries, entry); +} + +void sync_manager_clear_entries(void) +{ +    DEBUG("clearing syncmanager entries"); + +    g_slist_free(entries); +    entries = NULL; +} + +void sync_manager_show(GtkWidget *parent) +{ +    SyncDialog *sd = sync_dialog_new(parent); + +    err_quark = g_quark_from_static_string("syncmanager"); + +    if (gtk_dialog_run(GTK_DIALOG(sd->dialog)) == GTK_RESPONSE_ACCEPT) { +        shell_view_set_enabled(FALSE); +        shell_status_set_enabled(TRUE); +        shell_set_transient_dialog(GTK_WINDOW(sd->dialog)); + +        sync_dialog_start_sync(sd); + +        shell_set_transient_dialog(NULL); +        shell_status_set_enabled(FALSE); +        shell_view_set_enabled(TRUE); +    } + +    sync_dialog_destroy(sd); +} + +static gboolean _cancel_sync(GtkWidget *widget, gpointer data) +{ +    SyncDialog *sd = (SyncDialog *)data; + +    if (session) { +        soup_session_abort(session); +    } + +    sd->flag_cancel = TRUE; +    g_main_loop_quit(loop); + +    gtk_widget_set_sensitive(widget, FALSE); + +    return FALSE; +} + +static SyncNetAction *sync_manager_get_selected_actions(gint *n) +{ +    gint i; +    GSList *entry; +    SyncNetAction *actions; + +    actions = g_new0(SyncNetAction, g_slist_length(entries)); + +    for (entry = entries, i = 0; entry; entry = entry->next) { +        SyncEntry *e = (SyncEntry *)entry->data; + +        if (e->selected) { +            SyncNetAction sna = {.entry = e}; +            actions[i++] = sna; +        } +    } + +    *n = i; +    return actions; +} + +#if !SOUP_CHECK_VERSION(3,0,0) +static SoupURI *sync_manager_get_proxy(void) +{ +    const gchar *conf; + +    if (!(conf = g_getenv("HTTP_PROXY"))) { +        if (!(conf = g_getenv("http_proxy"))) { +            return NULL; +        } +    } + +    return soup_uri_new(conf); +} +#endif + +static void ensure_soup_session(void) +{ +    if (!session) { +#if SOUP_CHECK_VERSION(3,0,0) +        session = soup_session_new_with_options("timeout", 10, NULL); +#else +#if SOUP_CHECK_VERSION(2,42,0) +        SoupURI *proxy = sync_manager_get_proxy(); + +        session = soup_session_new_with_options( +            SOUP_SESSION_TIMEOUT, 10, SOUP_SESSION_PROXY_URI, proxy, NULL); +#else +        SoupURI *proxy = sync_manager_get_proxy(); +        session = soup_session_async_new_with_options( +            SOUP_SESSION_TIMEOUT, 10, SOUP_SESSION_PROXY_URI, proxy, NULL); +#endif +#endif +    } +} + +static void sync_dialog_start_sync(SyncDialog *sd) +{ +    gint nactions; +    SyncNetAction *actions; + +    ensure_soup_session(); + +    loop = g_main_loop_new(NULL, FALSE); + +    gtk_widget_hide(sd->button_sync); +    gtk_widget_hide(sd->button_priv_policy); +    sync_dialog_netarea_show(sd); +    g_signal_connect(G_OBJECT(sd->button_cancel), "clicked", +                     (GCallback)_cancel_sync, sd); + +    actions = sync_manager_get_selected_actions(&nactions); +    sync_dialog_netarea_start_actions(sd, actions, nactions); +    g_free(actions); + +    if (sd->flag_cancel) { +        gtk_widget_hide(sd->button_cancel); +        gtk_widget_show(sd->button_close); + +        /* wait for the user to close the dialog */ +        g_main_loop_run(loop); +    } + +    g_main_loop_unref(loop); +} + +#if SOUP_CHECK_VERSION(2,42,0) +static void got_response(GObject *source, GAsyncResult *res, gpointer user_data) +#else +static void got_response(SoupSession *source, SoupMessage *res, gpointer user_data) +#endif +{ +    SyncNetAction *sna = user_data; +    GInputStream *is; +#if SOUP_CHECK_VERSION(2,42,0) +#else +    const guint8 *buf=NULL; +    gsize len,datawritten; +    SoupBuffer *soupmsg=NULL; +#endif + +#if SOUP_CHECK_VERSION(2,42,0) +    is = soup_session_send_finish(session, res, &sna->error); +    if (is == NULL) +        goto out; +    if (sna->error != NULL) +        goto out; +#endif + +    if (sna->entry->file_name != NULL) { +        //check for missing config dirs +        g_mkdir(g_get_user_config_dir(), 0766); +        g_mkdir(g_build_filename(g_get_user_config_dir(),"hardinfo2",NULL), 0766); +        // +        gchar *path = g_build_filename(g_get_user_config_dir(), "hardinfo2", +                                       sna->entry->file_name, NULL); +        GFile *file = g_file_new_for_path(path); +        GFileOutputStream *output = +            g_file_replace(file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, +                           NULL, &sna->error); + +        if (output != NULL) { +#if SOUP_CHECK_VERSION(2,42,0) +            g_output_stream_splice(G_OUTPUT_STREAM(output), is, +                                   G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, +                                   &sna->error); +#else +            soupmsg=soup_message_body_flatten(res->response_body); +            if(soupmsg){ +                soup_buffer_get_data(soupmsg,&buf,&len); +                DEBUG("got file with len: %u", (unsigned int)len); +                if(len>0){ +                    g_output_stream_write_all(G_OUTPUT_STREAM(output),buf,len,&datawritten,NULL,&sna->error); +		    soup_buffer_free(soupmsg); +	        } +	    } +#endif +        } + +        g_free(path); +        g_object_unref(file); +    } + +out: +    g_main_loop_quit(loop); +#if SOUP_CHECK_VERSION(2,42,0) +    g_object_unref(is); +#endif +} + +static gboolean send_request_for_net_action(SyncNetAction *sna) +{ +    gchar *uri; +    SoupMessage *msg; +    guint response_code; + +    uri = g_strdup_printf("%s/%s", API_SERVER_URI, sna->entry->file_name); + +    if (sna->entry->generate_contents_for_upload == NULL) { +        msg = soup_message_new("GET", uri); +    } else { +        gsize size; +        gchar *contents = sna->entry->generate_contents_for_upload(&size); + +        msg = soup_message_new("POST", uri); + +#if SOUP_CHECK_VERSION(3, 0, 0) +        GBytes *cont = g_bytes_new_static(contents,size); +        soup_message_set_request_body_from_bytes(msg, "application/octet-stream", cont); +#else +        soup_message_set_request(msg, "application/octet-stream", +                                 SOUP_MEMORY_TAKE, contents, size); +#endif +    } + +#if SOUP_CHECK_VERSION(3, 0, 0) +    soup_session_send_async(session, msg, G_PRIORITY_DEFAULT, NULL, got_response, sna); +#else +#if SOUP_CHECK_VERSION(2,42,0) +    soup_session_send_async(session, msg, NULL, got_response, sna); +#else +    soup_session_queue_message(session, msg, got_response, sna); +#endif +#endif +    g_main_loop_run(loop); + +    g_object_unref(msg); +    g_free(uri); + +    if (sna->error != NULL) { +        DEBUG("Error while sending request: %s", sna->error->message); +        g_error_free(sna->error); +        sna->error = NULL; +        return FALSE; +    } + +    return TRUE; +} + +static void +sync_dialog_netarea_start_actions(SyncDialog *sd, SyncNetAction sna[], gint n) +{ +    gint i; +    GtkWidget **labels; +    GtkWidget **status_labels; +    const gchar *done_str = "\342\234\223"; +    const gchar *error_str = "\342\234\227"; +    const gchar *curr_str = "\342\226\266"; +    const gchar *empty_str = "\302\240\302\240"; + +    labels = g_new0(GtkWidget *, n); +    status_labels = g_new0(GtkWidget *, n); + +    for (i = 0; i < n; i++) { +        GtkWidget *hbox; + +        hbox = gtk_hbox_new(FALSE, 5); + +        labels[i] = gtk_label_new(_(sna[i].entry->name)); +        status_labels[i] = gtk_label_new(empty_str); + +        gtk_label_set_use_markup(GTK_LABEL(labels[i]), TRUE); +        gtk_label_set_use_markup(GTK_LABEL(status_labels[i]), TRUE); + +        gtk_misc_set_alignment(GTK_MISC(labels[i]), 0.0, 0.5); +        gtk_misc_set_alignment(GTK_MISC(status_labels[i]), 1.0, 0.5); + +        gtk_box_pack_start(GTK_BOX(hbox), status_labels[i], FALSE, FALSE, 0); +        gtk_box_pack_start(GTK_BOX(hbox), labels[i], TRUE, TRUE, 0); +        gtk_box_pack_start(GTK_BOX(sd->sna->vbox), hbox, FALSE, FALSE, 3); + +        gtk_widget_show_all(hbox); +    } + +    while (gtk_events_pending()) +        gtk_main_iteration(); + +    for (i = 0; i < n; i++) { +        gchar *markup; + +        if (sd->flag_cancel) { +            markup = g_strdup_printf("<s>%s</s> <i>%s</i>", +                                     _(sna[i].entry->name), +                                     _("(canceled)")); +            gtk_label_set_markup(GTK_LABEL(labels[i]), markup); +            g_free(markup); + +            gtk_label_set_markup(GTK_LABEL(status_labels[i]), error_str); +            break; +        } + +        markup = g_strdup_printf("<b>%s</b>", _(sna[i].entry->name)); +        gtk_label_set_markup(GTK_LABEL(labels[i]), markup); +        g_free(markup); + +        gtk_label_set_markup(GTK_LABEL(status_labels[i]), curr_str); + +        if (sna[i].entry && !send_request_for_net_action(&sna[i])) { +            markup = g_strdup_printf("<b><s>%s</s></b> <i>%s</i>", +                                     _(sna[i].entry->name), _("(failed)")); +            gtk_label_set_markup(GTK_LABEL(labels[i]), markup); +            g_free(markup); + +            sd->flag_cancel = TRUE; + +            gtk_label_set_markup(GTK_LABEL(status_labels[i]), error_str); +            if (sna[i].error) { +                if (sna[i].error->code != 1) { +                    /* the user has not cancelled something... */ +                    g_warning(_("Failed while performing \"%s\". Please file a " +                                "bug report " +                                "if this problem persists. (Use the " +                                "Help\342\206\222Report" +                                " bug option.)\n\nDetails: %s"), +                                _(sna[i].entry->name), sna[i].error->message); +                } + +                g_error_free(sna[i].error); +            } else { +                g_warning(_("Failed while performing \"%s\". Please file a bug " +                            "report " +                            "if this problem persists. (Use the " +                            "Help\342\206\222Report" +                            " bug option.)"), +                            _(sna[i].entry->name)); +            } +            break; +        } + +        gtk_label_set_markup(GTK_LABEL(status_labels[i]), done_str); +        gtk_label_set_markup(GTK_LABEL(labels[i]), _(sna[i].entry->name)); +    } + +    g_free(labels); +    g_free(status_labels); +} + +static SyncNetArea *sync_dialog_netarea_new(void) +{ +    SyncNetArea *sna = g_new0(SyncNetArea, 1); + +    sna->vbox = gtk_vbox_new(FALSE, 0); + +    gtk_container_set_border_width(GTK_CONTAINER(sna->vbox), 10); + +    gtk_widget_show_all(sna->vbox); +    gtk_widget_hide(sna->vbox); + +    return sna; +} + +static void sync_dialog_netarea_destroy(SyncNetArea *sna) +{ +    g_return_if_fail(sna != NULL); + +    g_free(sna); +} + +static void sync_dialog_netarea_show(SyncDialog *sd) +{ +    g_return_if_fail(sd && sd->sna); + +    gtk_widget_hide(GTK_WIDGET(sd->scroll_box)); +    gtk_widget_show(GTK_WIDGET(sd->sna->vbox)); + +    gtk_label_set_markup(GTK_LABEL(sd->label), LABEL_SYNC_SYNCING); +    gtk_window_set_default_size(GTK_WINDOW(sd->dialog), 0, 0); +    gtk_window_reshow_with_initial_size(GTK_WINDOW(sd->dialog)); +} + +static void sync_dialog_netarea_hide(SyncDialog *sd) +{ +    g_return_if_fail(sd && sd->sna); + +    gtk_widget_show(GTK_WIDGET(sd->scroll_box)); +    gtk_widget_hide(GTK_WIDGET(sd->sna->vbox)); + +    gtk_label_set_markup(GTK_LABEL(sd->label), LABEL_SYNC_DEFAULT); +    gtk_window_reshow_with_initial_size(GTK_WINDOW(sd->dialog)); +} + +static void populate_store(GtkListStore *store) +{ +    GSList *entry; +    SyncEntry *e; + +    gtk_list_store_clear(store); + +    for (entry = entries; entry; entry = entry->next) { +        GtkTreeIter iter; + +        e = (SyncEntry *)entry->data; + +        e->selected = TRUE; + +        gtk_list_store_append(store, &iter); +        gtk_list_store_set(store, &iter, 0, TRUE, 1, _(e->name), 2, e, -1); +    } +} + +static void sel_toggle(GtkCellRendererToggle *cellrenderertoggle, +                       gchar *path_str, +                       GtkTreeModel *model) +{ +    GtkTreeIter iter; +    GtkTreePath *path = gtk_tree_path_new_from_string(path_str); +    SyncEntry *se; +    gboolean active; + +    gtk_tree_model_get_iter(model, &iter, path); +    gtk_tree_model_get(model, &iter, 0, &active, 2, &se, -1); + +    se->selected = !active; + +    gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, se->selected, -1); +    gtk_tree_path_free(path); +} + +static void close_clicked(void) { g_main_loop_quit(loop); } + +static SyncDialog *sync_dialog_new(GtkWidget *parent) +{ +    SyncDialog *sd; +    GtkWidget *dialog; +    GtkWidget *dialog1_vbox; +    GtkWidget *scrolledwindow2; +    GtkWidget *treeview2; +    GtkWidget *dialog1_action_area; +    GtkWidget *button8; +    GtkWidget *button7; +    GtkWidget *button6; +    GtkWidget *priv_policy_btn; +    GtkWidget *label; +    GtkWidget *hbox; + +    GtkTreeViewColumn *column; +    GtkTreeModel *model; +    GtkListStore *store; +    GtkCellRenderer *cr_text, *cr_toggle; + +    sd = g_new0(SyncDialog, 1); +    sd->sna = sync_dialog_netarea_new(); + +    dialog = gtk_dialog_new(); +    gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent)); +    gtk_window_set_title(GTK_WINDOW(dialog), _("Synchronize")); +    gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); +    gtk_window_set_icon(GTK_WINDOW(dialog), +                        icon_cache_get_pixbuf("syncmanager.png")); +    gtk_window_set_default_size(GTK_WINDOW(dialog), 420, 260); +    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT); +    gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG); + +    gtk_container_set_border_width(GTK_CONTAINER(dialog), 5); + +#if GTK_CHECK_VERSION(2, 14, 0) +    dialog1_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); +#else +    dialog1_vbox = GTK_DIALOG(dialog)->vbox; +#endif +    gtk_box_set_spacing(GTK_BOX(dialog1_vbox), 5); +    gtk_container_set_border_width(GTK_CONTAINER(dialog1_vbox), 4); +    gtk_widget_show(dialog1_vbox); +#if GTK_CHECK_VERSION(3, 0, 0) +    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +#else +    hbox = gtk_hbox_new(FALSE, 5); +#endif +    gtk_box_pack_start(GTK_BOX(dialog1_vbox), hbox, FALSE, FALSE, 0); + +    label = gtk_label_new(LABEL_SYNC_DEFAULT); +    gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); +    gtk_label_set_use_markup(GTK_LABEL(label), TRUE); +#if GTK_CHECK_VERSION(3, 0, 0) +    gtk_widget_set_valign(label, GTK_ALIGN_CENTER); +#else +    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); +#endif + +    gtk_box_pack_start(GTK_BOX(hbox), icon_cache_get_image("syncmanager.png"), +                       FALSE, FALSE, 0); +    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); +    gtk_widget_show_all(hbox); + +    gtk_box_pack_start(GTK_BOX(dialog1_vbox), sd->sna->vbox, TRUE, TRUE, 0); + +    scrolledwindow2 = gtk_scrolled_window_new(NULL, NULL); +    gtk_widget_show(scrolledwindow2); +    gtk_box_pack_start(GTK_BOX(dialog1_vbox), scrolledwindow2, TRUE, TRUE, 0); +    gtk_widget_set_size_request(scrolledwindow2, -1, 200); +    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow2), +                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); +    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow2), +                                        GTK_SHADOW_IN); + +    store = +        gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER); +    model = GTK_TREE_MODEL(store); + +    treeview2 = gtk_tree_view_new_with_model(model); +    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview2), FALSE); +    gtk_widget_show(treeview2); +    gtk_container_add(GTK_CONTAINER(scrolledwindow2), treeview2); + +    column = gtk_tree_view_column_new(); +    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview2), column); + +    cr_toggle = gtk_cell_renderer_toggle_new(); +    gtk_tree_view_column_pack_start(column, cr_toggle, FALSE); +    g_signal_connect(cr_toggle, "toggled", G_CALLBACK(sel_toggle), model); +    gtk_tree_view_column_add_attribute(column, cr_toggle, "active", 0); + +    cr_text = gtk_cell_renderer_text_new(); +    gtk_tree_view_column_pack_start(column, cr_text, TRUE); +    gtk_tree_view_column_add_attribute(column, cr_text, "markup", 1); + +    populate_store(store); + +    priv_policy_btn = gtk_link_button_new_with_label( +            "https://github.com/hardinfo2/hardinfo2?tab=readme-ov-file#privacy-policy", +            _("Privacy Policy")); +    gtk_widget_show(priv_policy_btn); +    gtk_box_pack_start(GTK_BOX(dialog1_vbox), priv_policy_btn, FALSE, FALSE, 0); + +#if GTK_CHECK_VERSION(2, 14, 0) +    dialog1_action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog)); +#else +    dialog1_action_area = GTK_DIALOG(dialog)->action_area; +#endif +    gtk_widget_show(dialog1_action_area); +    gtk_button_box_set_layout(GTK_BUTTON_BOX(dialog1_action_area), +                              GTK_BUTTONBOX_END); + +    button8 = gtk_button_new_with_mnemonic(_("_Cancel")); +    gtk_widget_show(button8); +    gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button8, +                                 GTK_RESPONSE_CANCEL); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button8, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button8, GTK_CAN_DEFAULT); +#endif +    button7 = gtk_button_new_with_mnemonic(_("_Synchronize")); +    gtk_widget_show(button7); +    gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button7, +                                 GTK_RESPONSE_ACCEPT); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button7, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button7, GTK_CAN_DEFAULT); +#endif +    button6 = gtk_button_new_from_stock(GTK_STOCK_CLOSE); +    g_signal_connect(G_OBJECT(button6), "clicked", (GCallback)close_clicked, +                     NULL); +    gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button6, +                                 GTK_RESPONSE_ACCEPT); +#if GTK_CHECK_VERSION(2, 18, 0) +    gtk_widget_set_can_default(button6, TRUE); +#else +    GTK_WIDGET_SET_FLAGS(button6, GTK_CAN_DEFAULT); +#endif + +    sd->dialog = dialog; +    sd->button_sync = button7; +    sd->button_cancel = button8; +    sd->button_close = button6; +    sd->button_priv_policy = priv_policy_btn; +    sd->scroll_box = scrolledwindow2; +    sd->label = label; + +    return sd; +} + +static void sync_dialog_destroy(SyncDialog *sd) +{ +    gtk_widget_destroy(sd->dialog); +    sync_dialog_netarea_destroy(sd->sna); +    g_free(sd); +} + +static gboolean sync_one(gpointer data) +{ +    SyncNetAction *sna = data; + +    if (sna->entry->generate_contents_for_upload) +        goto out; + +    DEBUG("Syncronizing: %s", sna->entry->name); + +    gchar *msg = g_strdup_printf(_("Synchronizing: %s"), _(sna->entry->name)); +    shell_status_update(msg); +    shell_status_pulse(); +    g_free(msg); + +    send_request_for_net_action(sna); + +out: +    g_main_loop_unref(loop); +    idle_free(sna); + +    return FALSE; +} + +void sync_manager_update_on_startup(void) +{ +    GSList *entry; + +    ensure_soup_session(); + +    loop = g_main_loop_new(NULL, FALSE); + +    for (entry = entries; entry; entry = entry->next) { +        SyncNetAction *action = g_new0(SyncNetAction, 1); + +        action->entry = entry->data; +        loop = g_main_loop_ref(loop); + +        g_idle_add(sync_one, action); +    } +} | 
