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); + } +} |