diff options
author | Simon Quigley <tsimonq2@ubuntu.com> | 2017-06-19 15:19:47 -0500 |
---|---|---|
committer | Simon Quigley <tsimonq2@ubuntu.com> | 2017-06-19 15:19:47 -0500 |
commit | 7aacc9f2510901c9e97b30fa9bcb550bb7f99c03 (patch) | |
tree | 16908948750c11da8332d80d8bb9b339399ee4d7 /shell | |
parent | 7c47b5b9584f5011aeba18d7e1b26b3d3124825f (diff) |
New upstream version 0.5.1+git20170605
Diffstat (limited to 'shell')
-rw-r--r-- | shell/callbacks.c | 265 | ||||
-rw-r--r-- | shell/iconcache.c | 100 | ||||
-rw-r--r-- | shell/loadgraph.c | 349 | ||||
-rw-r--r-- | shell/menu.c | 171 | ||||
-rw-r--r-- | shell/report.c | 900 | ||||
-rw-r--r-- | shell/shell.c | 1876 | ||||
-rw-r--r-- | shell/stock.c | 91 | ||||
-rw-r--r-- | shell/syncmanager.c | 768 |
8 files changed, 4520 insertions, 0 deletions
diff --git a/shell/callbacks.c b/shell/callbacks.c new file mode 100644 index 00000000..d5ac34da --- /dev/null +++ b/shell/callbacks.c @@ -0,0 +1,265 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2009 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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. + * + * 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 "xmlrpc-server.h" + +#include "config.h" + +void cb_sync_manager() +{ + Shell *shell = shell_get_main_shell(); + + sync_manager_show(shell->window); +} + +void cb_save_graphic() +{ + Shell *shell = shell_get_main_shell(); + GtkWidget *dialog; + gchar *filename; + + /* save the pixbuf to a png file */ + dialog = gtk_file_chooser_dialog_new(_("Save Image"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, + GTK_RESPONSE_ACCEPT, NULL); + + filename = g_strconcat(shell->selected->name, ".png", NULL); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename); + g_free(filename); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + gtk_widget_destroy(dialog); + + shell_status_update(_("Saving image...")); + + tree_view_save_image(filename); + + shell_status_update(_("Done.")); + g_free(filename); + + return; + } + + gtk_widget_destroy(dialog); +} + +void cb_open_web_page() +{ + open_url("http://www.hardinfo.org"); +} + +void cb_report_bug() +{ + open_url("https://github.com/lpereira/hardinfo"); +} + +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; + 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(); + + text = g_strdup_printf(_("%s Module"), sm->name); + gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), text); + 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() +{ + GtkWidget *about; + const gchar *authors[] = { + _("Author:"), + "Leandro A. F. Pereira", + "", + _("Contributors:"), + "Agney Lopes Roth Ferraz", + "Andrey Esin", + "", + _("Based on work by:"), + _("MD5 implementation by Colin Plumb (see md5.c for details)"), + _("SHA1 implementation by Steve Reid (see sha1.c for details)"), + _("Blowfish implementation by Paul Kocher (see blowfich.c for details)"), + _("Raytracing benchmark by John Walker (see fbench.c for details)"), + _("FFT benchmark by Scott Robert Ladd (see fftbench.c for details)"), + _("Some code partly based on x86cpucaps by Osamu Kayasono"), + _("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"), + NULL + }; + const gchar *artists[] = { + _("Jakub Szypulka"), + _("Tango Project"), + _("The GNOME Project"), + _("VMWare, Inc. (USB icon from VMWare Workstation 6)"), + NULL + }; + + about = gtk_about_dialog_new(); + gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "HardInfo"); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION); + gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), + "Copyright \302\251 2003-2016 " + "Leandro A. F. Pereira"); + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), + _("System information and benchmark tool")); + gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), + icon_cache_get_pixbuf("logo.png")); + + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), + _("HardInfo 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.\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")); +#if GTK_CHECK_VERSION(2,8,0) + gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(about), TRUE); +#endif + + gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors); + gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about), artists); + + gtk_dialog_run(GTK_DIALOG(about)); + gtk_widget_destroy(about); +} + +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..74b19b0c --- /dev/null +++ b/shell/iconcache.c @@ -0,0 +1,100 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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. + * + * 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.c b/shell/loadgraph.c new file mode 100644 index 00000000..8fbeef48 --- /dev/null +++ b/shell/loadgraph.c @@ -0,0 +1,349 @@ +/* + * 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 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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" + +static void _draw(LoadGraph * lg); + +LoadGraph *load_graph_new(gint size) +{ + LoadGraph *lg; + + lg = g_new0(LoadGraph, 1); + + size++; + + lg->suffix = 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; +} + +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; + + _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); + +#if 0 /* old-style grid */ + gdk_rgb_gc_set_foreground(lg->grid, 0x707070); +#endif + + gdk_gc_set_line_attributes(lg->trace, + 1, GDK_LINE_SOLID, + GDK_CAP_PROJECTING, GDK_JOIN_ROUND); + +#if 0 /* old-style fill */ + gdk_gc_set_line_attributes(lg->fill, + 1, GDK_LINE_SOLID, + GDK_CAP_BUTT, GDK_JOIN_BEVEL); +#endif + + /* configures the expose event */ + g_signal_connect(G_OBJECT(lg->area), "expose-event", + (GCallback) _expose, lg); +} + +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); + +#if 0 /* old-style drawing */ + for (i = 0; i < lg->size; i++) { + gint this = lg->height - lg->data[i] * lg->scale; + gint next = lg->height - lg->data[i + 1] * lg->scale; + gint i4 = i * 4; + + gdk_draw_line(draw, lg->fill, i4, this, i4, lg->height); + gdk_draw_line(draw, lg->fill, i4 + 2, this, i4 + 2, lg->height); + } + + for (i = 0; i < lg->size; i++) { + gint this = lg->height - lg->data[i] * lg->scale; + gint next = lg->height - lg->data[i + 1] * lg->scale; + gint i4 = i * 4; + + gdk_draw_line(draw, lg->trace, i4, this, i4 + 2, + (this + next) / 2); + gdk_draw_line(draw, lg->trace, i4 + 2, (this + next) / 2, + i4 + 4, next); + } +#endif + + gtk_widget_queue_draw(lg->area); +} + +void load_graph_update(LoadGraph * lg, gint value) +{ + gint i; + + 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); +} + +#ifdef LOADGRAPH_UNIT_TEST +gboolean lg_update(gpointer d) +{ + LoadGraph *lg = (LoadGraph *) d; + + static int i = 0; + static int j = 1; + + if (i > 150) { + j = -1; + } else if (i < 0) { + j = 1; + } + + i += j; + if (rand() % 10 > 8) + i *= 2; + if (rand() % 10 < 2) + i /= 2; + load_graph_update(lg, i + rand() % 50); + + return TRUE; +} + +int main(int argc, char **argv) +{ + + + LoadGraph *lg; + GtkWidget *window; + + gtk_init(&argc, &argv); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_show(window); + + lg = load_graph_new(50); + gtk_container_add(GTK_CONTAINER(window), load_graph_get_framed(lg)); + gtk_container_set_border_width(GTK_CONTAINER(window), 20); + load_graph_configure_expose(lg); + + lg_update(lg); + + g_timeout_add(100, lg_update, lg); + + gtk_main(); + + return 0; +} +#endif diff --git a/shell/menu.c b/shell/menu.c new file mode 100644 index 00000000..f5724d31 --- /dev/null +++ b/shell/menu.c @@ -0,0 +1,171 @@ +/* + * HardInfo + * Copyright(C) 2003-2007 Leandro 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. + * + * 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" + +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 */ + NULL, /* tooltip */ + G_CALLBACK(cb_generate_report)}, + + {"SyncManagerAction", HI_STOCK_SYNC_MENU, + N_("_Network Updater..."), NULL, + NULL, + G_CALLBACK(cb_sync_manager)}, + + {"OpenAction", GTK_STOCK_OPEN, + N_("_Open..."), NULL, + NULL, + G_CALLBACK(cb_sync_manager)}, + + {"CopyAction", GTK_STOCK_COPY, + N_("_Copy to Clipboard"), "<control>C", + N_("Copy to clipboard"), + G_CALLBACK(cb_copy_to_clipboard)}, + + {"SaveGraphAction", GTK_STOCK_SAVE_AS, + N_("_Save image as..."), "<control>S", + NULL, + G_CALLBACK(cb_save_graphic)}, + + {"RefreshAction", GTK_STOCK_REFRESH, + N_("_Refresh"), "F5", + NULL, + G_CALLBACK(cb_refresh)}, + + {"HomePageAction", HI_STOCK_INTERNET, + N_("_Open HardInfo 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", GTK_STOCK_ABOUT, + N_("_About HardInfo"), NULL, + N_("Displays program version information"), + G_CALLBACK(cb_about)}, + + {"QuitAction", GTK_STOCK_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)}, +}; + +/* 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("HardInfo"); + 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, "hardinfo" );//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..3b5d3bdb --- /dev/null +++ b/shell/report.c @@ -0,0 +1,900 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2008 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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. + * + * 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> + +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}, + {NULL, NULL, NULL, NULL} +}; + +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) +{ + ctx->keyvalue(ctx, key, value); +} + +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; +} + +void report_context_configure(ReportContext * ctx, GKeyFile * keyfile) +{ + gchar **keys; + const gchar *group = "$ShellParam$"; + + /* FIXME: sometime in the future we'll save images in the report. this + flag will be set if we should support that. + + so i don't forget how to encode the images inside the html files: + https://en.wikipedia.org/wiki/Data:_URI_scheme */ + + ctx->is_image_enabled = (g_key_file_get_boolean(keyfile, + group, + "ViewType", + NULL) == SHELL_VIEW_PROGRESS); + + + 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; + } + } + } + + g_strfreev(keys); + } + +} + +void report_table(ReportContext * ctx, gchar * text) +{ + GKeyFile *key_file = g_key_file_new(); + gchar **groups; + gint i; + + /* 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); + +#if 0 + if (ctx->is_image_enabled) { + report_embed_image(ctx, key_file, group); + } else { +#endif + keys = g_key_file_get_keys(key_file, tmpgroup, NULL, NULL); + for (j = 0; keys[j]; j++) { + gchar *key = keys[j]; + gchar *value; + + value = g_key_file_get_value(key_file, tmpgroup, key, NULL); + + 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 == '$') { + report_key_value(ctx, strchr(key + 1, '$') + 1, + value); + } else { + report_key_value(ctx, key, value); + } + + } + + g_free(value); + } + + g_strfreev(keys); +#if 0 + } +#endif + 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: 30px 0 10px 0 }\n" + " .sstitle{ font: bold 80%% serif; color: #000000; background: #efefef }\n" + " .field { font: 80%% sans-serif; color: #000000; padding: 2px; padding-left: 50px }\n" + " .value { font: 80%% sans-serif; color: #505050 }\n" + "</style>\n" "</head><body>\n", + VERSION); +} + +static void report_html_footer(ReportContext * ctx) +{ + ctx->output = h_strconcat(ctx->output, + "</table></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; + } + + ctx->output = h_strdup_cprintf("<table><tr><td colspan=\"%d\" class=\"stit" + "le\">%s</td></tr>\n", + ctx->output, + columns, + text); +} + +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, + text); +} + +static void +report_html_key_value(ReportContext * ctx, gchar * key, gchar * value) +{ + gint columns = report_get_visible_columns(ctx); + gchar **values; + gint i; + + if (columns == 2) { + ctx->output = h_strdup_cprintf("<tr><td class=\"field\">%s</td>" + "<td class=\"value\">%s</td></tr>\n", + ctx->output, + key, value); + } else { + values = g_strsplit(value, "|", columns); + + ctx->output = h_strdup_cprintf("\n<tr>\n<td class=\"field\">%s</td>", ctx->output, key); + + for (i = columns - 2; 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); + } +} + +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) +{ + ctx->output = h_strdup_cprintf("-%s-\n", ctx->output, text); +} + +static void +report_text_key_value(ReportContext * ctx, gchar * key, gchar * value) +{ + gint columns = report_get_visible_columns(ctx); + gchar **values; + gint i; + + if (columns == 2) { + if (strlen(value)) + ctx->output = h_strdup_cprintf("%s\t\t: %s\n", ctx->output, key, value); + else + ctx->output = h_strdup_cprintf("%s\n", ctx->output, key); + } else { + values = g_strsplit(value, "|", columns); + + ctx->output = h_strdup_cprintf("%s\t", ctx->output, key); + + for (i = columns - 2; i >= 0; i--) { + ctx->output = h_strdup_cprintf("%s\t", + ctx->output, + values[i]); + } + + ctx->output = h_strdup_cprintf("\n", ctx->output); + + g_strfreev(values); + } +} + +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) + 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 (!params.gui_running) + fprintf(stderr, "\033[2K\033[40;32;1m %s\033[0m\n", + entry->name); + + 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; + + 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); + + 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->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; + + 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->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; +} + +void report_context_free(ReportContext * ctx) +{ + g_hash_table_destroy(ctx->column_titles); + g_free(ctx->output); + g_free(ctx); +} + +void report_create_from_module_list(ReportContext * ctx, GSList * modules) +{ + 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; + + 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); + 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?")); + gtk_dialog_add_buttons(GTK_DIALOG(dialog), + GTK_STOCK_NO, GTK_RESPONSE_REJECT, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *temp; + + temp = g_strdup_printf("file://%s", file); + open_url(temp); + + g_free(temp); + } + + gtk_widget_destroy(dialog); + } + + report_context_free(ctx); + g_free(file); + + 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); + + dialog1_vbox = GTK_DIALOG(dialog)->vbox; + gtk_box_set_spacing(GTK_BOX(dialog1_vbox), 5); + gtk_container_set_border_width(GTK_CONTAINER(dialog1_vbox), 4); + gtk_widget_show(dialog1_vbox); + + hbox = gtk_hbox_new(FALSE, 5); + 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); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + + 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); + + hbox = gtk_hbox_new(FALSE, 5); + 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); + + vbuttonbox3 = gtk_vbutton_box_new(); + 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); + GTK_WIDGET_SET_FLAGS(button3, GTK_CAN_DEFAULT); + 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); + GTK_WIDGET_SET_FLAGS(button6, GTK_CAN_DEFAULT); + g_signal_connect(button6, "clicked", G_CALLBACK(report_dialog_sel_all), + rd); + + dialog1_action_area = GTK_DIALOG(dialog)->action_area; + gtk_widget_show(dialog1_action_area); + gtk_button_box_set_layout(GTK_BUTTON_BOX(dialog1_action_area), + GTK_BUTTONBOX_END); + + button8 = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_widget_show(button8); + gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button8, + GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS(button8, GTK_CAN_DEFAULT); + + button7 = gtk_button_new_with_mnemonic(_("_Generate")); + gtk_widget_show(button7); + gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button7, + GTK_RESPONSE_ACCEPT); + GTK_WIDGET_SET_FLAGS(button7, GTK_CAN_DEFAULT); + + 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..bf866e84 --- /dev/null +++ b/shell/shell.c @@ -0,0 +1,1876 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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. + * + * 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 <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 "callbacks.h" + +/* + * Internal Prototypes ******************************************************** + */ + +static void create_window(); +static ShellTree *tree_new(void); +static ShellInfoTree *info_tree_new(gboolean extra); + +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(gchar * data); +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; + +/* + * 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->model)); + gtk_tree_store_clear(GTK_TREE_STORE(shell->moreinfo->model)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info->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 { + 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 (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->hpaned, 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); + shell_action_set_enabled("SaveGraphAction", + setting ? shell->view_type == + SHELL_VIEW_PROGRESS : FALSE); +} + +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 { + 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); + + hbox = gtk_hbox_new(FALSE, 3); + 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"), subtitle); + gtk_window_set_title(GTK_WINDOW(shell->window), tmp); + + g_free(tmp); + } else { + gtk_window_set_title(GTK_WINDOW(shell->window), _("System Information")); + } +} + +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("logo.png")); + shell_set_title(shell, NULL); + gtk_window_set_default_size(GTK_WINDOW(shell->window), 800, 600); + g_signal_connect(G_OBJECT(shell->window), "destroy", destroy_me, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_widget_show(vbox); + gtk_container_add(GTK_CONTAINER(shell->window), vbox); + shell->vbox = vbox; + + menu_init(shell); + + hbox = gtk_hbox_new(FALSE, 5); + 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(""); + gtk_misc_set_alignment(GTK_MISC(shell->status), 0.0, 0.5); + gtk_widget_show(shell->status); + gtk_box_pack_start(GTK_BOX(hbox), shell->status, FALSE, FALSE, 5); + + shell->hpaned = gtk_hpaned_new(); + gtk_widget_show(shell->hpaned); + gtk_box_pack_end(GTK_BOX(vbox), shell->hpaned, TRUE, TRUE, 0); + gtk_paned_set_position(GTK_PANED(shell->hpaned), 210); + + vbox = gtk_vbox_new(FALSE, 5); + gtk_widget_show(vbox); + gtk_paned_add2(GTK_PANED(shell->hpaned), vbox); + + shell->note = note_new(); + gtk_box_pack_end(GTK_BOX(vbox), shell->note->event_box, FALSE, FALSE, 0); + + shell->vpaned = gtk_vpaned_new(); + 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); +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(menuitem), TRUE); +#endif + 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", + 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; + + 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 __tree_iter_destroy(gpointer data) +{ + gtk_tree_iter_free((GtkTreeIter *) data); +} + +ShellSummary *summary_new(void) +{ + ShellSummary *summary; + + summary = g_new0(ShellSummary, 1); + summary->scroll = gtk_scrolled_window_new(NULL, NULL); + summary->view = gtk_vbox_new(FALSE, 5); + summary->items = NULL; + + gtk_container_set_border_width(GTK_CONTAINER(summary->view), 6); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(summary->scroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(summary->scroll), + summary->view); + gtk_widget_show_all(summary->scroll); + + return summary; +} + +void shell_init(GSList * modules) +{ + if (shell) { + g_error("Shell already created"); + return; + } + + DEBUG("initializing shell"); + + 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->tree = tree_new(); + shell->info = info_tree_new(FALSE); + shell->moreinfo = info_tree_new(TRUE); + shell->loadgraph = load_graph_new(75); + shell->summary = summary_new(); + + update_tbl = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, __tree_iter_destroy); + + gtk_paned_pack1(GTK_PANED(shell->hpaned), shell->tree->scroll, + SHELL_PACK_RESIZE, SHELL_PACK_SHRINK); + gtk_paned_pack1(GTK_PANED(shell->vpaned), shell->info->scroll, + SHELL_PACK_RESIZE, SHELL_PACK_SHRINK); + + gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), + shell->moreinfo->scroll, NULL); + gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), + load_graph_get_framed(shell->loadgraph), + NULL); + gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), + shell->summary->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(); + + 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->hpaned); + + 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_enabled("SaveGraphAction", FALSE); + shell_action_set_active("SidePaneAction", TRUE); + shell_action_set_active("ToolbarAction", TRUE); + +#ifndef HAS_LIBSOUP + shell_action_set_enabled("SyncManagerAction", FALSE); +#else + shell_action_set_enabled("SyncManagerAction", sync_manager_count_entries() > 0); +#endif +} + +static gboolean update_field(gpointer data) +{ + ShellFieldUpdate *fu; + GtkTreeIter *iter; + + fu = (ShellFieldUpdate *) data; + g_return_val_if_fail(fu != NULL, FALSE); + + DEBUG("update_field [%s]", fu->field_name); + + iter = g_hash_table_lookup(update_tbl, fu->field_name); + if (!iter) { + return FALSE; + } + + /* if the entry is still selected, update it */ + if (iter && fu->entry->selected && fu->entry->fieldfunc) { + GtkTreeStore *store = GTK_TREE_STORE(shell->info->model); + gchar *value = fu->entry->fieldfunc(_(fu->field_name)); + + /* + * 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->selection, + iter)) { + load_graph_update(shell->loadgraph, atoi(value)); + } + + gtk_tree_store_set(store, iter, INFO_TREE_COL_VALUE, value, -1); + + 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; +} + +#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)) + +static gboolean reload_section(gpointer data) +{ + ShellModuleEntry *entry = (ShellModuleEntry *) data; + + /* if the entry is still selected, update it */ + if (entry->selected) { + GtkTreePath *path = NULL; + GtkTreeIter iter; + double pos_info_scroll, pos_more_scroll; + + /* save current position */ + pos_info_scroll = RANGE_GET_VALUE(info, vscrollbar); + pos_more_scroll = RANGE_GET_VALUE(moreinfo, vscrollbar); + + /* avoid drawing the window while we reload */ + gdk_window_freeze_updates(shell->window->window); + + /* gets the current selected path */ + if (gtk_tree_selection_get_selected + (shell->info->selection, &shell->info->model, &iter)) { + path = gtk_tree_model_get_path(shell->info->model, &iter); + } + + /* update the information, clear the treeview and populate it again */ + module_entry_reload(entry); + info_selected_show_extra(NULL); /* clears the more info store */ + module_selected_show_info(entry, TRUE); + + /* if there was a selection, reselect it */ + if (path) { + gtk_tree_selection_select_path(shell->info->selection, path); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(shell->info->view), path, NULL, + FALSE); + gtk_tree_path_free(path); + } else { + /* restore position */ + RANGE_SET_VALUE(info, vscrollbar, pos_info_scroll); + RANGE_SET_VALUE(moreinfo, vscrollbar, pos_more_scroll); + } + + /* make the window drawable again */ + gdk_window_thaw_updates(shell->window->window); + } + + /* 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 (viewtype < SHELL_VIEW_NORMAL || viewtype >= SHELL_VIEW_N_VIEWS) + viewtype = SHELL_VIEW_NORMAL; + + shell->normalize_percentage = TRUE; + shell->view_type = viewtype; + + /* reset to the default model */ + gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info->view), + shell->info->model); + + /* reset to the default view columns */ + if (!reload) { + gtk_tree_view_column_set_visible(shell->info->col_extra1, FALSE); + gtk_tree_view_column_set_visible(shell->info->col_extra2, FALSE); + gtk_tree_view_column_set_visible(shell->info->col_progress, FALSE); + gtk_tree_view_column_set_visible(shell->info->col_value, TRUE); + } + + /* turn off the rules hint */ + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(shell->info->view), FALSE); + + /* turn off the save graphic action */ + shell_action_set_enabled("SaveGraphAction", FALSE); + + close_note(NULL, NULL); + + switch (viewtype) { + default: + case SHELL_VIEW_NORMAL: + gtk_widget_show(shell->info->scroll); + gtk_widget_hide(shell->notebook); + + if (!reload) { + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info->view), FALSE); + } + break; + case SHELL_VIEW_DUAL: + gtk_widget_show(shell->info->scroll); + gtk_widget_show(shell->moreinfo->scroll); + gtk_notebook_set_page(GTK_NOTEBOOK(shell->notebook), 0); + gtk_widget_show(shell->notebook); + + gtk_paned_set_position(GTK_PANED(shell->vpaned), + shell->hpaned->allocation.height / 2); + break; + case SHELL_VIEW_LOAD_GRAPH: + gtk_widget_show(shell->info->scroll); + gtk_notebook_set_page(GTK_NOTEBOOK(shell->notebook), 1); + gtk_widget_show(shell->notebook); + load_graph_clear(shell->loadgraph); + + gtk_paned_set_position(GTK_PANED(shell->vpaned), + shell->hpaned->allocation.height - + shell->loadgraph->height - 16); + break; + case SHELL_VIEW_PROGRESS_DUAL: + gtk_widget_show(shell->notebook); + gtk_widget_show(shell->moreinfo->scroll); + + gtk_notebook_set_page(GTK_NOTEBOOK(shell->notebook), 0); + /* fallthrough */ + case SHELL_VIEW_PROGRESS: + gtk_widget_show(shell->info->scroll); + shell_action_set_enabled("SaveGraphAction", TRUE); + + if (!reload) { + gtk_tree_view_column_set_visible(shell->info->col_progress, TRUE); + gtk_tree_view_column_set_visible(shell->info->col_value, FALSE); + } + + if (viewtype == SHELL_VIEW_PROGRESS) + gtk_widget_hide(shell->notebook); + break; + case SHELL_VIEW_SUMMARY: + gtk_notebook_set_page(GTK_NOTEBOOK(shell->notebook), 2); + + gtk_widget_show(shell->notebook); + gtk_widget_hide(shell->info->scroll); + gtk_widget_hide(shell->moreinfo->scroll); + } +} + +static void +group_handle_special(GKeyFile * key_file, ShellModuleEntry * entry, + gchar * group, gchar ** keys, gboolean reload) +{ + if (g_str_equal(group, "$ShellParam$")) { + 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); + + fu->field_name = g_strdup(g_utf8_strchr(key, -1, '$') + 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->col_extra1; + } else if (g_str_equal(title, "Extra2")) { + column = shell->info->col_extra2; + } else if (g_str_equal(title, "Value")) { + column = shell->info->col_value; + } else if (g_str_equal(title, "TextValue")) { + column = shell->info->col_textvalue; + } else if (g_str_equal(title, "Progress")) { + column = shell->info->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_equal(key, "ViewType")) { + set_view_type(g_key_file_get_integer(key_file, group, + key, NULL), reload); + } else if (g_str_has_prefix(key, "Icon")) { + GtkTreeIter *iter = g_hash_table_lookup(update_tbl, + g_utf8_strchr(key, + -1, '$') + 1); + + if (iter) { + gchar *file = + g_key_file_get_value(key_file, group, key, NULL); + gtk_tree_store_set(GTK_TREE_STORE(shell->info->model), + iter, INFO_TREE_COL_PBUF, + icon_cache_get_pixbuf_at_size(file, + 22, + 22), + -1); + g_free(file); + } + } else if (g_str_equal(key, "Zebra")) { + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW + (shell->info->view), + g_key_file_get_boolean + (key_file, group, key, NULL)); + } + } + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info->view), headers_visible); + } else { + g_warning("Unknown parameter group: ``%s''", group); + } +} + +static void +group_handle_normal(GKeyFile * key_file, ShellModuleEntry * entry, + gchar * group, gchar ** keys, gsize ngroups) +{ + GtkTreeIter parent; + GtkTreeStore *store = GTK_TREE_STORE(shell->info->model); + gchar *tmp = g_strdup(group); + gint i; + + if (ngroups > 1) { + gtk_tree_store_append(store, &parent, NULL); + + strend(tmp, '#'); + gtk_tree_store_set(store, &parent, INFO_TREE_COL_NAME, tmp, -1); + g_free(tmp); + } + + for (i = 0; keys[i]; i++) { + gchar *key = keys[i]; + gchar *value; + GtkTreeIter child; + + value = g_key_file_get_value(key_file, group, key, NULL); + if (entry->fieldfunc && value && g_str_equal(value, "...")) { + g_free(value); + value = entry->fieldfunc(_(key)); + } + + if ((key && value) && g_utf8_validate(key, -1, NULL) && g_utf8_validate(value, -1, NULL)) { + if (ngroups == 1) { + gtk_tree_store_append(store, &child, NULL); + } else { + gtk_tree_store_append(store, &child, &parent); + } + + /* FIXME: use g_key_file_get_string_list? */ + if (g_utf8_strchr(value, -1, '|')) { + gchar **columns = g_strsplit(value, "|", 0); + + gtk_tree_store_set(store, &child, INFO_TREE_COL_VALUE, columns[0], -1); + if (columns[1]) { + gtk_tree_store_set(store, &child, INFO_TREE_COL_EXTRA1, columns[1], -1); + if (columns[2]) { + gtk_tree_store_set(store, &child, INFO_TREE_COL_EXTRA2, columns[2], -1); + } + } + + g_strfreev(columns); + } else { + gtk_tree_store_set(store, &child, INFO_TREE_COL_VALUE, value, -1); + } + + strend(key, '#'); + + if (*key == '$') { + gchar **tmp; + + tmp = g_strsplit(++key, "$", 0); + + gtk_tree_store_set(store, &child, INFO_TREE_COL_NAME, + tmp[1], INFO_TREE_COL_DATA, tmp[0], -1); + + g_strfreev(tmp); + } else { + gtk_tree_store_set(store, &child, INFO_TREE_COL_NAME, _(key), + INFO_TREE_COL_DATA, NULL, -1); + } + + g_hash_table_insert(update_tbl, g_strdup(key), + gtk_tree_iter_copy(&child)); + + } + + g_free(value); + } +} + +static void +moreinfo_handle_normal(GKeyFile * key_file, gchar * group, gchar ** keys) +{ + GtkTreeIter parent; + GtkTreeStore *store = GTK_TREE_STORE(shell->moreinfo->model); + gint i; + + gtk_tree_store_append(store, &parent, NULL); + gtk_tree_store_set(store, &parent, INFO_TREE_COL_NAME, group, -1); + + for (i = 0; keys[i]; i++) { + gchar *key = keys[i]; + GtkTreeIter child; + gchar *value; + + value = g_key_file_get_value(key_file, group, key, NULL); + + if (g_utf8_validate(key, -1, NULL) + && g_utf8_validate(value, -1, NULL)) { + strend(key, '#'); + + gtk_tree_store_append(store, &child, &parent); + gtk_tree_store_set(store, &child, INFO_TREE_COL_VALUE, + value, INFO_TREE_COL_NAME, key, -1); + } + + g_free(value); + } +} + +static void update_progress() +{ + GtkTreeModel *model = shell->info->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->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->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 void +module_selected_show_info(ShellModuleEntry * entry, gboolean reload) +{ + GKeyFile *key_file = g_key_file_new(); + GtkTreeStore *store; + gchar *key_data, **groups; + gboolean has_shell_param = FALSE; + gint i; + gsize ngroups; + + module_entry_scan(entry); + key_data = module_entry_function(entry); + + /* */ + gdk_window_freeze_updates(shell->info->view->window); + + g_object_ref(shell->info->model); + gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info->view), NULL); + + /* reset the view type to normal */ + set_view_type(SHELL_VIEW_NORMAL, reload); + + if (!reload) { + /* recreate the iter hash table */ + h_hash_table_remove_all(update_tbl); + } + + shell_clear_field_updates(); + + store = GTK_TREE_STORE(shell->info->model); + + gtk_tree_store_clear(store); + + g_key_file_load_from_data(key_file, key_data, strlen(key_data), 0, + NULL); + groups = g_key_file_get_groups(key_file, &ngroups); + + for (i = 0; groups[i]; i++) + if (groups[i][0] == '$') + ngroups--; + + for (i = 0; groups[i]; i++) { + gchar *group = groups[i]; + gchar **keys = g_key_file_get_keys(key_file, group, NULL, NULL); + + if (*group == '$') { + group_handle_special(key_file, entry, group, keys, reload); + has_shell_param = TRUE; + } else { + group_handle_normal(key_file, entry, group, keys, ngroups); + } + + g_strfreev(keys); + } + + /* */ + if (!has_shell_param) { + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info->view), FALSE); + } + + /* */ + g_object_unref(shell->info->model); + gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info->view), shell->info->model); + gtk_tree_view_expand_all(GTK_TREE_VIEW(shell->info->view)); + + gdk_window_thaw_updates(shell->info->view->window); + shell_set_note_from_entry(entry); + + if (shell->view_type == SHELL_VIEW_PROGRESS || shell->view_type == SHELL_VIEW_PROGRESS_DUAL) { + update_progress(); + } + +#if GTK_CHECK_VERSION(2,12,0) + if (ngroups == 1) { + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(shell->info->view), + FALSE); + } else { + gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(shell->info->view), + TRUE); + } +#endif + + g_strfreev(groups); + g_key_file_free(key_file); + g_free(key_data); +} + +static void info_selected_show_extra(gchar * data) +{ + GtkTreeStore *store; + + store = GTK_TREE_STORE(shell->moreinfo->model); + gtk_tree_store_clear(store); + + if (!shell->selected->morefunc) + return; + + if (data) { + GKeyFile *key_file = g_key_file_new(); + gchar *key_data = shell->selected->morefunc(data); + gchar **groups; + gint i; + + g_key_file_load_from_data(key_file, key_data, strlen(key_data), 0, + NULL); + groups = g_key_file_get_groups(key_file, NULL); + + for (i = 0; groups[i]; i++) { + gchar *group = groups[i]; + gchar **keys = + g_key_file_get_keys(key_file, group, NULL, NULL); + + moreinfo_handle_normal(key_file, group, keys); + } + + gtk_tree_view_expand_all(GTK_TREE_VIEW(shell->moreinfo->view)); + + g_strfreev(groups); + g_key_file_free(key_file); + g_free(key_data); + } +} + +static gchar *shell_summary_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 shell_summary_add_item(ShellSummary *summary, + 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 = shell_summary_clear_value(value); + + /* creates the frame */ + frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(frame), + GTK_SHADOW_NONE); + + frame_label_box = gtk_hbox_new(FALSE, 5); + 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); + + alignment = gtk_alignment_new(0.5, 0.5, 1, 1); + gtk_widget_show(alignment); + gtk_container_add(GTK_CONTAINER(frame), alignment); + gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 48, 0); + + content = gtk_label_new(temp); + gtk_misc_set_alignment(GTK_MISC(content), 0.0, 0.5); + gtk_container_add(GTK_CONTAINER(alignment), content); + + 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 summary screen */ + gtk_box_pack_start(GTK_BOX(shell->summary->view), frame, FALSE, FALSE, 4); + + /* add the item to the list of summary items */ + summary->items = g_slist_prepend(summary->items, frame); + + g_free(temp); +} + +static void shell_summary_clear(ShellSummary *summary) +{ + GSList *item; + + for (item = summary->items; item; item = item->next) { + gtk_widget_destroy(GTK_WIDGET(item->data)); + } + + g_slist_free(summary->items); + summary->items = NULL; + + if (summary->header) gtk_widget_destroy(summary->header); + summary->header = NULL; +} +static void shell_summary_create_header(ShellSummary *summary, + 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->summary->view), header, FALSE, FALSE, 4); + + summary->header = header; + + g_free(temp); +} + +static void shell_show_summary(void) +{ + GKeyFile *keyfile; + gchar *summary; + + set_view_type(SHELL_VIEW_SUMMARY, FALSE); + shell_summary_clear(shell->summary); + shell_summary_create_header(shell->summary, shell->selected_module->name); + + keyfile = g_key_file_new(); + summary = shell->selected_module->summaryfunc(); + + if (g_key_file_load_from_data(keyfile, summary, + strlen(summary), 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"); + } + + shell_summary_add_item(shell->summary, + 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 summary"); + set_view_type(SHELL_VIEW_NORMAL, FALSE); + } + + g_free(summary); + 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; + + /* 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); + + info_selected_show_extra(NULL); /* clears the more info store */ + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(shell->info->view)); + + /* urgh. why don't GTK do this when the model is cleared? */ + RANGE_SET_VALUE(info, vscrollbar, 0.0); + RANGE_SET_VALUE(info, hscrollbar, 0.0); + RANGE_SET_VALUE(moreinfo, vscrollbar, 0.0); + RANGE_SET_VALUE(moreinfo, hscrollbar, 0.0); + + 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->model)); + set_view_type(SHELL_VIEW_NORMAL, FALSE); + + if (shell->selected_module->summaryfunc) { + shell_show_summary(); + } + } + + 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; + + 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); + info_selected_show_extra(datacol); + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(shell->moreinfo->view)); +} + +static ShellInfoTree *info_tree_new(gboolean extra) +{ + 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)); + + if (!extra) + 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; +} diff --git a/shell/stock.c b/shell/stock.c new file mode 100644 index 00000000..ad1a0135 --- /dev/null +++ b/shell/stock.c @@ -0,0 +1,91 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2007 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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. + * + * 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[] = { + { "report.png", HI_STOCK_REPORT}, + { "internet.png", HI_STOCK_INTERNET}, + { "module.png", HI_STOCK_MODULE}, + { "about-modules.png", HI_STOCK_ABOUT_MODULES}, + { "syncmanager-small.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) +{ + gint 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..0b95e720 --- /dev/null +++ b/shell/syncmanager.c @@ -0,0 +1,768 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2009 Leandro A. F. Pereira <leandro@hardinfo.org> + * + * 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. + * + * 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 "syncmanager.h" +#include "iconcache.h" +#include "hardinfo.h" +#include "config.h" + +#ifdef HAS_LIBSOUP +#include <libsoup/soup.h> + +#include <stdarg.h> +#include <string.h> + +typedef struct _SyncDialog SyncDialog; +typedef struct _SyncNetArea SyncNetArea; +typedef struct _SyncNetAction SyncNetAction; + +struct _SyncNetArea { + GtkWidget *vbox; +}; + +struct _SyncNetAction { + gchar *name; + gboolean(*do_action) (SyncDialog * sd, gpointer sna); + + SyncEntry *entry; + GError *error; +}; + +struct _SyncDialog { + GtkWidget *dialog; + GtkWidget *label; + + GtkWidget *button_sync; + GtkWidget *button_cancel; + GtkWidget *button_close; + + GtkWidget *scroll_box; + + SyncNetArea *sna; + + gboolean flag_cancel:1; +}; + +static GSList *entries = NULL; +static SoupSession *session = NULL; +static GMainLoop *loop; +static GQuark err_quark; + +#define XMLRPC_SERVER_URI "https://xmlrpc.hardinfo.org/" +#define XMLRPC_SERVER_API_VERSION 1 + +#define LABEL_SYNC_DEFAULT _("<big><b>Synchronize with Central Database</b></big>\n" \ + "The following information may be synchronized " \ + "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__); \ + } +#endif /* HAS_LIBSOUP */ + + +gint sync_manager_count_entries(void) +{ +#ifdef HAS_LIBSOUP + return g_slist_length(entries); +#else + return 0; +#endif +} + +void sync_manager_add_entry(SyncEntry * entry) +{ +#ifdef HAS_LIBSOUP + DEBUG("registering syncmanager entry ''%s''", entry->fancy_name); + + entry->selected = TRUE; + entries = g_slist_append(entries, entry); +#else + DEBUG("libsoup support is disabled."); +#endif /* HAS_LIBSOUP */ +} + +void sync_manager_clear_entries(void) +{ +#ifdef HAS_LIBSOUP + DEBUG("clearing syncmanager entries"); + + g_slist_free(entries); + entries = NULL; +#else + DEBUG("libsoup support is disabled."); +#endif /* HAS_LIBSOUP */ +} + +void sync_manager_show(GtkWidget *parent) +{ +#ifndef HAS_LIBSOUP + g_warning + (_("HardInfo was compiled without libsoup support. (Network Updater requires it.)")); +#else /* !HAS_LIBSOUP */ + 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); + + sync_dialog_start_sync(sd); + + shell_status_set_enabled(FALSE); + shell_view_set_enabled(TRUE); + } + + sync_dialog_destroy(sd); +#endif /* HAS_LIBSOUP */ +} + +#ifdef HAS_LIBSOUP +static gint _soup_get_xmlrpc_value_int(SoupMessage * msg, + SyncNetAction * sna) +{ + gint int_value = -1; + + sna->error = NULL; + + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + SNA_ERROR(1, _("%s (error #%d)"), msg->reason_phrase, + msg->status_code); + goto bad; + } + + GVariant *value = soup_xmlrpc_parse_response(msg->response_body->data, + msg->response_body->length, + "h", NULL); + if (!value) { + SNA_ERROR(2, _("Could not parse XML-RPC response")); + goto bad; + } + + int_value = g_variant_get_int32(value); + g_variant_unref(value); + +bad: + return int_value; +} + +static gchar *_soup_get_xmlrpc_value_string(SoupMessage * msg, + SyncNetAction * sna) +{ + gchar *string = NULL; + + sna->error = NULL; + + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + SNA_ERROR(1, _("%s (error #%d)"), msg->reason_phrase, + msg->status_code); + goto bad; + } + + GVariant *value = soup_xmlrpc_parse_response(msg->response_body->data, + msg->response_body->length, + "s", NULL); + if (!value) { + SNA_ERROR(2, _("Could not parse XML-RPC response")); + goto bad; + } + + string = g_strdup(g_variant_get_string(value, NULL)); + g_variant_unref(value); + +bad: + return string; +} + +static gboolean _soup_xmlrpc_call(gchar * method, SyncNetAction * sna, + SoupSessionCallback callback) +{ + SoupMessage *msg; + + sna->error = NULL; + + msg = soup_xmlrpc_message_new(XMLRPC_SERVER_URI, method, NULL, NULL); + if (!msg) + return FALSE; + + DEBUG("calling xmlrpc method %s", method); + + soup_session_queue_message(session, msg, callback, sna); + g_main_run(loop); + + return TRUE; +} + +static gboolean _soup_xmlrpc_call_with_parameters(gchar * method, + SyncNetAction * sna, + SoupSessionCallback + callback, ...) +{ + SoupMessage *msg; + GVariantBuilder builder; + GVariant *parameters; + gchar *argument, *body; + va_list ap; + + sna->error = NULL; + + msg = soup_message_new("POST", XMLRPC_SERVER_URI); + + DEBUG("calling xmlrpc method %s", method); + if (!msg) + return FALSE; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + va_start(ap, callback); + while ((argument = va_arg(ap, gchar *))) { + g_variant_builder_add(&builder, "s", argument); + DEBUG("with parameter: %s", argument); + } + va_end(ap); + parameters = g_variant_builder_end(&builder); + g_variant_builder_unref(&builder); + + body = soup_xmlrpc_build_request(method, parameters, NULL); + g_variant_unref(parameters); + if (body) { + soup_message_set_request(msg, "text/xml", + SOUP_MEMORY_TAKE, body, strlen(body)); + + soup_session_queue_message(session, msg, callback, sna); + g_main_run(loop); + + return TRUE; + } + + return FALSE; +} + +static void _action_check_api_version_got_response(SoupSession * session, + SoupMessage * msg, + gpointer user_data) +{ + SyncNetAction *sna = (SyncNetAction *) user_data; + gint version = _soup_get_xmlrpc_value_int(msg, sna); + + if (version != XMLRPC_SERVER_API_VERSION) { + SNA_ERROR(5, _("Server says it supports API version %d, but " + "this version of HardInfo only supports API " + "version %d."), version, XMLRPC_SERVER_API_VERSION); + } + + g_main_quit(loop); +} + +static gboolean _action_check_api_version(SyncDialog * sd, + gpointer user_data) +{ + SyncNetAction *sna = (SyncNetAction *) user_data; + + if (!_soup_xmlrpc_call("server.getAPIVersion", sna, + _action_check_api_version_got_response)) + return FALSE; + + return sna->error ? FALSE : TRUE; +} + +static void _action_call_function_got_response(SoupSession * session, + SoupMessage * msg, + gpointer user_data) +{ + SyncNetAction *sna = (SyncNetAction *) user_data; + gchar *string; + + if ((string = _soup_get_xmlrpc_value_string(msg, sna)) && + sna->entry->save_to) { + DEBUG("received string: %s\n", string); + gchar *filename = g_build_filename(g_get_home_dir(), ".hardinfo", + sna->entry->save_to, NULL); + + DEBUG("saving to %s", filename); + + g_file_set_contents(filename, string, -1, NULL); + g_free(filename); + } + + if (sna->entry->callback) { + sna->entry->callback(sna->entry, string); + } + + g_free(string); + g_main_quit(loop); +} + +static gboolean _action_call_function(SyncDialog * sd, gpointer user_data) +{ + SyncNetAction *sna = (SyncNetAction *) user_data; + + if (sna->entry) { + gchar *str_data = NULL; + + if (sna->entry->get_data) + str_data = sna->entry->get_data(); + + if (!_soup_xmlrpc_call_with_parameters("sync.callFunctionEx", sna, + _action_call_function_got_response, + VERSION, ARCH, + sna->entry->name, + str_data, NULL)) { + g_free(str_data); + + return FALSE; + } + + g_free(str_data); + } + + return sna->error ? FALSE : TRUE; +} + +static gboolean _cancel_sync(GtkWidget * widget, gpointer data) +{ + SyncDialog *sd = (SyncDialog *) data; + + if (session) { + soup_session_abort(session); + } + + sd->flag_cancel = TRUE; + g_main_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; + SyncNetAction + action_check_api = { _("Contacting HardInfo Central Database"), _action_check_api_version }, + action_clean_up = { _("Cleaning up"), NULL}; + + actions = g_new0(SyncNetAction, 2 + g_slist_length(entries)); + + for (entry = entries, i = 1; entry; entry = entry->next) { + SyncEntry *e = (SyncEntry *) entry->data; + + if (e->selected) { + SyncNetAction sna = + { e->fancy_name, _action_call_function, e }; + + actions[i++] = sna; + } + } + + actions[0] = action_check_api; + actions[i++] = action_clean_up; + + *n = i; + return actions; +} + +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); +} + +static void sync_dialog_start_sync(SyncDialog * sd) +{ + gint nactions; + SyncNetAction *actions; + + if (!session) { + SoupURI *proxy = sync_manager_get_proxy(); + + session = soup_session_new_with_options(SOUP_SESSION_TIMEOUT, 10, + SOUP_SESSION_PROXY_URI, + proxy, NULL); + /* Crashes if we unref the proxy? O_o */ + /*if (proxy) + g_object_unref(proxy); */ + } + + loop = g_main_loop_new(NULL, TRUE); + + gtk_widget_hide(sd->button_sync); + 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_run(loop); + } + + g_main_loop_unref(loop); +} + +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].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>(canceled)</i>"), + sna[i].name); + 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].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].do_action && !sna[i].do_action(sd, &sna[i])) { + markup = + g_strdup_printf(_("<b><s>%s</s></b> <i>(failed)</i>"), + sna[i].name); + 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].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].name); + } + break; + } + + gtk_label_set_markup(GTK_LABEL(status_labels[i]), done_str); + gtk_label_set_markup(GTK_LABEL(labels[i]), sna[i].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)); +} + +#if 0 +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)); +} +#endif + +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->fancy_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_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 *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), _("Network Updater")); + 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); + + dialog1_vbox = GTK_DIALOG(dialog)->vbox; + gtk_box_set_spacing(GTK_BOX(dialog1_vbox), 5); + gtk_container_set_border_width(GTK_CONTAINER(dialog1_vbox), 4); + gtk_widget_show(dialog1_vbox); + + hbox = gtk_hbox_new(FALSE, 5); + 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); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + + 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); + + dialog1_action_area = GTK_DIALOG(dialog)->action_area; + gtk_widget_show(dialog1_action_area); + gtk_button_box_set_layout(GTK_BUTTON_BOX(dialog1_action_area), + GTK_BUTTONBOX_END); + + button8 = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_widget_show(button8); + gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button8, + GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS(button8, GTK_CAN_DEFAULT); + + button7 = gtk_button_new_with_mnemonic(_("_Synchronize")); + gtk_widget_show(button7); + gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button7, + GTK_RESPONSE_ACCEPT); + GTK_WIDGET_SET_FLAGS(button7, GTK_CAN_DEFAULT); + + 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); + GTK_WIDGET_SET_FLAGS(button6, GTK_CAN_DEFAULT); + + sd->dialog = dialog; + sd->button_sync = button7; + sd->button_cancel = button8; + sd->button_close = button6; + 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); +} +#endif /* HAS_LIBSOUP */ |