/* * HardInfo - Displays System Information * Copyright (C) 2003-2007 L. A. F. Pereira * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 or later. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include "config.h" #include "hardinfo.h" #include "shell.h" #include "syncmanager.h" #include "iconcache.h" #include "menu.h" #include "stock.h" #include "uri_handler.h" #include "callbacks.h" struct UpdateTableItem { union { GtkWidget *widget; GtkTreeIter *iter; }; gboolean is_iter; }; /* * Internal Prototypes ******************************************************** */ static void create_window(void); static ShellTree *tree_new(void); static ShellInfoTree *info_tree_new(void); static void module_selected(gpointer data); static void module_selected_show_info(ShellModuleEntry * entry, gboolean reload); static void info_selected(GtkTreeSelection * ts, gpointer data); static void info_selected_show_extra(const gchar *tag); static gboolean reload_section(gpointer data); static gboolean rescan_section(gpointer data); static gboolean update_field(gpointer data); /* * Globals ******************************************************************** */ static Shell *shell = NULL; static GHashTable *update_tbl = NULL; static GSList *update_sfusrc = NULL; gchar *lginterval = NULL; /* * Code :) ******************************************************************** */ Shell *shell_get_main_shell(void) { return shell; } void shell_ui_manager_set_visible(const gchar * path, gboolean setting) { GtkWidget *widget; if (!params.gui_running) return; widget = gtk_ui_manager_get_widget(shell->ui_manager, path); if (!widget) return; if (setting) gtk_widget_show(widget); else gtk_widget_hide(widget); } void shell_clear_tree_models(Shell *shell) { gtk_tree_store_clear(GTK_TREE_STORE(shell->tree->model)); gtk_tree_store_clear(GTK_TREE_STORE(shell->info_tree->model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), FALSE); } void shell_clear_timeouts(Shell *shell) { h_hash_table_remove_all(update_tbl); } void shell_action_set_property(const gchar * action_name, const gchar * property, gboolean setting) { GtkAction *action; if (!params.gui_running) return; action = gtk_action_group_get_action(shell->action_group, action_name); if (action) { GValue value = { 0 }; g_value_init(&value, G_TYPE_BOOLEAN); g_value_set_boolean(&value, setting); g_object_set_property(G_OBJECT(action), property, &value); g_value_unset(&value); } } void shell_action_set_label(const gchar * action_name, gchar * label) { #if GTK_CHECK_VERSION(2,16,0) if (params.gui_running && shell->action_group) { GtkAction *action; action = gtk_action_group_get_action(shell->action_group, action_name); if (action) { gtk_action_set_label(action, label); } } #endif } void shell_action_set_enabled(const gchar * action_name, gboolean setting) { if (params.gui_running && shell->action_group) { GtkAction *action; action = gtk_action_group_get_action(shell->action_group, action_name); if (action) { gtk_action_set_sensitive(action, setting); } } } gboolean shell_action_get_enabled(const gchar * action_name) { GtkAction *action; if (!params.gui_running) return FALSE; action = gtk_action_group_get_action(shell->action_group, action_name); if (action) { return gtk_action_get_sensitive(action); } return FALSE; } void shell_set_side_pane_visible(gboolean setting) { if (!params.gui_running) return; if (setting) gtk_widget_show(shell->tree->scroll); else gtk_widget_hide(shell->tree->scroll); } gboolean shell_action_get_active(const gchar * action_name) { GtkAction *action; GSList *proxies; /* FIXME: Ugh. Are you sure there isn't any simpler way? O_o */ if (!params.gui_running) return FALSE; action = gtk_action_group_get_action(shell->action_group, action_name); if (action) { proxies = gtk_action_get_proxies(action); for (; proxies; proxies = proxies->next) { GtkWidget *widget = (GtkWidget *) proxies->data; if (GTK_IS_CHECK_MENU_ITEM(widget)) { return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (widget)); } } } return FALSE; } void shell_action_set_active(const gchar * action_name, gboolean setting) { GtkAction *action; GSList *proxies; /* FIXME: Ugh. Are you sure there isn't any simpler way? O_o */ if (!params.gui_running) return; action = gtk_action_group_get_action(shell->action_group, action_name); if (action) { proxies = gtk_action_get_proxies(action); for (; proxies; proxies = proxies->next) { GtkWidget *widget = (GtkWidget *) proxies->data; if (GTK_IS_CHECK_MENU_ITEM(widget)) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), setting); return; } } } } void shell_status_pulse(void) { if (params.gui_running) { if (shell->_pulses++ == 5) { /* we're pulsing for some time, disable the interface and change the cursor to a hourglass */ shell_view_set_enabled(FALSE); } gtk_progress_bar_pulse(GTK_PROGRESS_BAR(shell->progress)); while (gtk_events_pending()) gtk_main_iteration(); } else if (!params.quiet) { static gint counter = 0; fprintf(stderr, "\033[2K\033[40;37;1m %c\033[0m\r", "|/-\\"[counter++ % 4]); } } void shell_status_set_percentage(gint percentage) { if (params.gui_running) { gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(shell->progress), (float) percentage / 100.0); while (gtk_events_pending()) gtk_main_iteration(); } else if (!params.quiet) { if (percentage < 1 || percentage >= 100) { fprintf(stderr, "\033[2K"); } else { gchar pbar[] = "----------"; memset(pbar, '#', percentage / 10); fprintf(stderr, "\r\033[40;37;1m%3d%% \033[40;34;1m" "%s\033[0m\r", percentage, pbar); } } } void shell_view_set_enabled(gboolean setting) { if (!params.gui_running) return; if (setting) { shell->_pulses = 0; widget_set_cursor(shell->window, GDK_LEFT_PTR); } else { widget_set_cursor(shell->window, GDK_WATCH); } gtk_widget_set_sensitive(shell->hbox, setting); shell_action_set_enabled("ViewMenuAction", setting); shell_action_set_enabled("ConnectToAction", setting); shell_action_set_enabled("RefreshAction", setting); shell_action_set_enabled("CopyAction", setting); shell_action_set_enabled("ReportAction", setting); shell_action_set_enabled("SyncManagerAction", setting && sync_manager_count_entries() > 0); } void shell_status_set_enabled(gboolean setting) { if (!params.gui_running) return; if (setting) gtk_widget_show(shell->progress); else { gtk_widget_hide(shell->progress); shell_view_set_enabled(TRUE); shell_status_update(_("Done.")); } } void shell_do_reload(void) { if (!params.gui_running || !shell->selected) return; shell_action_set_enabled("RefreshAction", FALSE); shell_action_set_enabled("CopyAction", FALSE); shell_action_set_enabled("ReportAction", FALSE); shell_status_set_enabled(TRUE); module_entry_reload(shell->selected); module_selected(NULL); shell_action_set_enabled("RefreshAction", TRUE); shell_action_set_enabled("CopyAction", TRUE); shell_action_set_enabled("ReportAction", TRUE); } void shell_status_update(const gchar * message) { if (params.gui_running) { gtk_label_set_markup(GTK_LABEL(shell->status), message); gtk_progress_bar_pulse(GTK_PROGRESS_BAR(shell->progress)); while (gtk_events_pending()) gtk_main_iteration(); } else if (!params.quiet) { fprintf(stderr, "\033[2K\033[40;37;1m %s\033[0m\r", message); } } static void destroy_me(void) { cb_quit(); } static void close_note(GtkWidget * widget, gpointer user_data) { gtk_widget_hide(shell->note->event_box); } static ShellNote *note_new(void) { ShellNote *note; GtkWidget *hbox, *icon, *button; GtkWidget *border_box; /* colors stolen from gtkinfobar.c */ GdkColor info_default_border_color = { 0, 0xb800, 0xad00, 0x9d00 }; GdkColor info_default_fill_color = { 0, 0xff00, 0xff00, 0xbf00 }; note = g_new0(ShellNote, 1); note->label = gtk_label_new(""); note->event_box = gtk_event_box_new(); button = gtk_button_new(); border_box = gtk_event_box_new(); gtk_container_set_border_width(GTK_CONTAINER(border_box), 1); gtk_container_add(GTK_CONTAINER(note->event_box), border_box); gtk_widget_show(border_box); gtk_widget_modify_bg(border_box, GTK_STATE_NORMAL, &info_default_fill_color); gtk_widget_modify_bg(note->event_box, GTK_STATE_NORMAL, &info_default_border_color); icon = icon_cache_get_image("close.png"); gtk_widget_show(icon); gtk_container_add(GTK_CONTAINER(button), icon); gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); g_signal_connect(G_OBJECT(button), "clicked", (GCallback) close_note, NULL); #if GTK_CHECK_VERSION(3, 0, 0) hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); #else hbox = gtk_hbox_new(FALSE, 3); #endif icon = icon_cache_get_image("dialog-information.png"); gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), note->label, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 2); gtk_container_add(GTK_CONTAINER(border_box), hbox); gtk_widget_show_all(hbox); return note; } void shell_set_title(Shell *shell, gchar *subtitle) { if (subtitle) { gchar *tmp; tmp = g_strdup_printf(_("%s - System Information and Benchmark"), subtitle); gtk_window_set_title(GTK_WINDOW(shell->window), tmp); g_free(tmp); } else { gtk_window_set_title(GTK_WINDOW(shell->window), _("System Information and Benchmark")); } } static void create_window(void) { GtkWidget *vbox, *hbox; shell = g_new0(Shell, 1); shell->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_icon(GTK_WINDOW(shell->window), icon_cache_get_pixbuf("hardinfo2.png")); shell_set_title(shell, NULL); gtk_window_set_default_size(GTK_WINDOW(shell->window), 1024, 800); g_signal_connect(G_OBJECT(shell->window), "destroy", destroy_me, NULL); #if GTK_CHECK_VERSION(3, 0, 0) vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); #else vbox = gtk_vbox_new(FALSE, 0); #endif gtk_widget_show(vbox); gtk_container_add(GTK_CONTAINER(shell->window), vbox); shell->vbox = vbox; menu_init(shell); #if GTK_CHECK_VERSION(3, 0, 0) hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); #else hbox = gtk_hbox_new(FALSE, 5); #endif gtk_widget_show(hbox); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); shell->progress = gtk_progress_bar_new(); gtk_widget_set_size_request(shell->progress, 80, 10); gtk_widget_hide(shell->progress); gtk_box_pack_end(GTK_BOX(hbox), shell->progress, FALSE, FALSE, 5); shell->status = gtk_label_new(""); #if GTK_CHECK_VERSION(3, 0, 0) gtk_widget_set_valign(GTK_WIDGET(shell->status), GTK_ALIGN_CENTER); #else gtk_misc_set_alignment(GTK_MISC(shell->status), 0.0, 0.5); #endif gtk_widget_show(shell->status); gtk_box_pack_start(GTK_BOX(hbox), shell->status, FALSE, FALSE, 5); #if GTK_CHECK_VERSION(3, 0, 0) shell->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); #else shell->hbox = gtk_hbox_new(FALSE, 5); #endif gtk_widget_show(shell->hbox); gtk_box_pack_end(GTK_BOX(vbox), shell->hbox, TRUE, TRUE, 0); #if GTK_CHECK_VERSION(3, 0, 0) vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); #else vbox = gtk_vbox_new(FALSE, 5); #endif gtk_widget_show(vbox); gtk_box_pack_end(GTK_BOX(shell->hbox), vbox, TRUE, TRUE, 0); shell->note = note_new(); gtk_box_pack_end(GTK_BOX(vbox), shell->note->event_box, FALSE, FALSE, 0); #if GTK_CHECK_VERSION(3, 0, 0) shell->vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); #else shell->vpaned = gtk_vpaned_new(); #endif gtk_box_pack_start(GTK_BOX(vbox), shell->vpaned, TRUE, TRUE, 0); gtk_widget_show(shell->vpaned); shell->notebook = gtk_notebook_new(); gtk_paned_add2(GTK_PANED(shell->vpaned), shell->notebook); gtk_widget_show(shell->window); while (gtk_events_pending()) gtk_main_iteration(); } static void view_menu_select_entry(gpointer data, gpointer data2) { GtkTreePath *path; GtkTreeIter *iter = (GtkTreeIter *) data2; path = gtk_tree_model_get_path(shell->tree->model, iter); gtk_tree_selection_select_path(shell->tree->selection, path); gtk_tree_view_set_cursor(GTK_TREE_VIEW(shell->tree->view), path, NULL, FALSE); gtk_tree_path_free(path); } static void menu_item_set_icon_always_visible(Shell *shell, gchar *parent_path, gchar *item_id) { GtkWidget *menuitem; gchar *path; path = g_strdup_printf("%s/%s", parent_path, item_id); menuitem = gtk_ui_manager_get_widget(shell->ui_manager, path); //gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(menuitem), TRUE); g_free(path); } static void add_module_to_menu(gchar * name, GdkPixbuf * pixbuf) { GtkAction *action; GtkWidget *menuitem; gchar *about_module = g_strdup_printf("AboutModule%s", name); gchar *path; gint merge_id; GtkActionEntry entries[] = { { name, /* name */ name, /* stockid */ name, /* label */ NULL, /* accelerator */ NULL, /* tooltip */ NULL, /* callback */ }, { about_module, name, name, NULL, name, (GCallback) cb_about_module, }, }; stock_icon_register_pixbuf(pixbuf, name); if ((action = gtk_action_group_get_action(shell->action_group, name))) { gtk_action_group_remove_action(shell->action_group, action); } if ((action = gtk_action_group_get_action(shell->action_group, about_module))) { gtk_action_group_remove_action(shell->action_group, action); } gtk_action_group_add_actions(shell->action_group, entries, 2, NULL); merge_id = gtk_ui_manager_new_merge_id(shell->ui_manager); gtk_ui_manager_add_ui(shell->ui_manager, merge_id, "/menubar/ViewMenu/LastSep", name, name, GTK_UI_MANAGER_MENU, TRUE); shell->merge_ids = g_slist_prepend(shell->merge_ids, GINT_TO_POINTER(merge_id)); merge_id = gtk_ui_manager_new_merge_id(shell->ui_manager); gtk_ui_manager_add_ui(shell->ui_manager, merge_id, // "/menubar/HelpMenu/HelpMenuModules/LastSep", "/menubar/HelpMenu/LastSep", about_module, about_module, GTK_UI_MANAGER_AUTO, TRUE); shell->merge_ids = g_slist_prepend(shell->merge_ids, GINT_TO_POINTER(merge_id)); menu_item_set_icon_always_visible(shell, "/menubar/ViewMenu", name); } static void add_module_entry_to_view_menu(gchar * module, gchar * name, GdkPixbuf * pixbuf, GtkTreeIter * iter) { GtkAction *action; GtkWidget *menuitem; gint merge_id; gchar *path; GtkActionEntry entry = { name, /* name */ name, /* stockid */ name, /* label */ NULL, /* accelerator */ NULL, /* tooltip */ (GCallback) view_menu_select_entry, /* callback */ }; stock_icon_register_pixbuf(pixbuf, name); if ((action = gtk_action_group_get_action(shell->action_group, name))) { gtk_action_group_remove_action(shell->action_group, action); } gtk_action_group_add_actions(shell->action_group, &entry, 1, iter); merge_id = gtk_ui_manager_new_merge_id(shell->ui_manager); path = g_strdup_printf("/menubar/ViewMenu/%s", module); gtk_ui_manager_add_ui(shell->ui_manager, merge_id, path, name, name, GTK_UI_MANAGER_AUTO, FALSE); shell->merge_ids = g_slist_prepend(shell->merge_ids, GINT_TO_POINTER(merge_id)); menu_item_set_icon_always_visible(shell, path, name); g_free(path); } void shell_add_modules_to_gui(gpointer _shell_module, gpointer _shell_tree) { ShellModule *module = (ShellModule *) _shell_module; ShellTree *shelltree = (ShellTree *) _shell_tree; GtkTreeStore *store = GTK_TREE_STORE(shelltree->model); GtkTreeIter parent; if (!module) { return; } gtk_tree_store_append(store, &parent, NULL); gtk_tree_store_set(store, &parent, TREE_COL_NAME, module->name, TREE_COL_MODULE, module, TREE_COL_MODULE_ENTRY, NULL, TREE_COL_SEL, FALSE, -1); if (module->icon) { gtk_tree_store_set(store, &parent, TREE_COL_PBUF, module->icon, -1); } //add_module_to_menu(module->name, module->icon); if (module->entries) { ShellModuleEntry *entry; GSList *p; for (p = module->entries; p; p = g_slist_next(p)) { GtkTreeIter child; entry = (ShellModuleEntry *) p->data; if (entry->flags & MODULE_FLAG_HIDE) continue; gtk_tree_store_append(store, &child, &parent); gtk_tree_store_set(store, &child, TREE_COL_NAME, entry->name, TREE_COL_MODULE_ENTRY, entry, TREE_COL_SEL, FALSE, -1); if (entry->icon) { gtk_tree_store_set(store, &child, TREE_COL_PBUF, entry->icon, -1); } add_module_entry_to_view_menu(module->name, entry->name, entry->icon, gtk_tree_iter_copy(&child)); shell_status_pulse(); } } } static void destroy_update_tbl_value(gpointer data) { struct UpdateTableItem *item = data; if (item->is_iter) { gtk_tree_iter_free(item->iter); } else { g_object_unref(item->widget); } g_free(item); } DetailView *detail_view_new(void) { DetailView *detail_view; detail_view = g_new0(DetailView, 1); detail_view->scroll = gtk_scrolled_window_new(NULL, NULL); #if GTK_CHECK_VERSION(3, 0, 0) detail_view->view = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); #else detail_view->view = gtk_vbox_new(FALSE, 0); #endif gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(detail_view->scroll), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(detail_view->scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); #if GTK_CHECK_VERSION(3, 0, 0) gtk_container_add(GTK_CONTAINER(detail_view->scroll), detail_view->view); #else gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(detail_view->scroll), detail_view->view); #endif gtk_widget_show_all(detail_view->scroll); return detail_view; } static gboolean select_first_tree_item(gpointer data) { GtkTreeIter first; if (gtk_tree_model_get_iter_first(shell->tree->model, &first)) gtk_tree_selection_select_iter(shell->tree->selection, &first); return FALSE; } static void check_for_updates(void) { GKeyFile *key_file = g_key_file_new(); gchar *conf_path = g_build_filename(g_get_user_config_dir(), "hardinfo2", "settings.ini", NULL); g_key_file_load_from_file( key_file, conf_path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); gboolean setting = g_key_file_get_boolean(key_file, "Sync", "OnStartup", NULL); shell_action_set_active("SyncOnStartupAction", setting); g_free(conf_path); g_key_file_free(key_file); if (setting) { sync_manager_update_on_startup(); } } gboolean hardinfo_link(const gchar *uri) { /* Clicked link events pass through here on their * way to the default handler (xdg-open). * * TODO: In the future, links could be used to * jump to different pages in hardinfo. * * if (g_str_has_prefix(uri, "hardinfo:")) { * hardinfo_navigate(g_utf8_strchr(uri, strlen("hardinfo2"), ':') + 1); * return TRUE; * } */ return FALSE; /* didn't handle it */ } void shell_set_transient_dialog(GtkWindow *dialog) { shell->transient_dialog = dialog ? dialog : GTK_WINDOW(shell->window); } void shell_init(GSList * modules) { if (shell) { g_error("Shell already created"); return; } DEBUG("initializing shell"); uri_set_function(hardinfo_link); params.fmt_opts = FMT_OPT_PANGO; create_window(); shell_action_set_property("ConnectToAction", "is-important", TRUE); shell_action_set_property("CopyAction", "is-important", TRUE); shell_action_set_property("RefreshAction", "is-important", TRUE); shell_action_set_property("ReportAction", "is-important", TRUE); shell_action_set_property("ReportBugAction", "is-important", TRUE); shell_action_set_property("SyncManagerAction", "is-important", TRUE); shell->tree = tree_new(); shell->info_tree = info_tree_new(); shell->loadgraph = load_graph_new(75); shell->detail_view = detail_view_new(); shell->transient_dialog = GTK_WINDOW(shell->window); update_tbl = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_update_tbl_value); gtk_box_pack_start(GTK_BOX(shell->hbox), shell->tree->scroll, FALSE, FALSE, 0); gtk_paned_pack1(GTK_PANED(shell->vpaned), shell->info_tree->scroll, SHELL_PACK_RESIZE, SHELL_PACK_SHRINK); gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), load_graph_get_framed(shell->loadgraph), NULL); gtk_notebook_append_page(GTK_NOTEBOOK(shell->notebook), shell->detail_view->scroll, NULL); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(shell->notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(shell->notebook), FALSE); shell_status_set_enabled(TRUE); shell_status_update(_("Loading modules...")); shell->tree->modules = modules ? modules : modules_load_all(); check_for_updates(); g_slist_foreach(shell->tree->modules, shell_add_modules_to_gui, shell->tree); gtk_tree_view_expand_all(GTK_TREE_VIEW(shell->tree->view)); gtk_widget_show_all(shell->hbox); load_graph_configure_expose(shell->loadgraph); gtk_widget_hide(shell->notebook); gtk_widget_hide(shell->note->event_box); shell_status_update(_("Done.")); shell_status_set_enabled(FALSE); shell_action_set_enabled("RefreshAction", FALSE); shell_action_set_enabled("CopyAction", FALSE); shell_action_set_active("SidePaneAction", TRUE); shell_action_set_active("ToolbarAction", TRUE); shell_action_set_enabled("SyncManagerAction", sync_manager_count_entries() > 0); /* Should select Computer Summary (note: not Computer/Summary) */ g_idle_add(select_first_tree_item, NULL); } static gboolean update_field(gpointer data) { ShellFieldUpdate *fu; struct UpdateTableItem *item; fu = (ShellFieldUpdate *)data; g_return_val_if_fail(fu != NULL, FALSE); DEBUG("update_field [%s]", fu->field_name); item = g_hash_table_lookup(update_tbl, fu->field_name); if (!item) { return FALSE; } /* if the entry is still selected, update it */ if (fu->entry->selected && fu->entry->fieldfunc) { gchar *value = fu->entry->fieldfunc(fu->field_name); gdouble v; if (item->is_iter) { /* * this function is also used to feed the load graph when ViewType * is SHELL_VIEW_LOAD_GRAPH */ if (shell->view_type == SHELL_VIEW_LOAD_GRAPH && gtk_tree_selection_iter_is_selected(shell->info_tree->selection, item->iter)) { load_graph_set_title(shell->loadgraph, fu->field_name); v=atof(value); //fix KiB->Bytes for UberGraph (GTK3) #if GTK_CHECK_VERSION(3, 0, 0) if(strstr(value,"KiB")) v*=1024; #endif load_graph_update(shell->loadgraph, v); } GtkTreeStore *store = GTK_TREE_STORE(shell->info_tree->model); gtk_tree_store_set(store, item->iter, INFO_TREE_COL_VALUE, value, -1); } else { GList *children = gtk_container_get_children(GTK_CONTAINER(item->widget)); gtk_label_set_markup(GTK_LABEL(children->next->data), value); g_list_free(children); } g_free(value); return TRUE; } if (update_sfusrc) { GSList *sfu; for (sfu = update_sfusrc; sfu; sfu = sfu->next) { g_free(sfu->data); } g_slist_free(update_sfusrc); update_sfusrc = NULL; } /* otherwise, cleanup and destroy the timeout */ g_free(fu->field_name); g_free(fu); return FALSE; } #if GTK_CHECK_VERSION(3, 0, 0) #define RANGE_SET_VALUE(...) #define RANGE_GET_VALUE(...) 0 #else #define RANGE_SET_VALUE(tree, scrollbar, value) \ do { \ GtkRange CONCAT(*range, __LINE__) = \ GTK_RANGE(GTK_SCROLLED_WINDOW(shell->tree->scroll)->scrollbar); \ gtk_range_set_value(CONCAT(range, __LINE__), value); \ gtk_adjustment_value_changed(GTK_ADJUSTMENT( \ gtk_range_get_adjustment(CONCAT(range, __LINE__)))); \ } while (0) #define RANGE_GET_VALUE(tree, scrollbar) \ gtk_range_get_value( \ GTK_RANGE(GTK_SCROLLED_WINDOW(shell->tree->scroll)->scrollbar)) #endif static void destroy_widget(GtkWidget *widget, gpointer user_data) { gtk_widget_destroy(widget); } static void detail_view_clear(DetailView *detail_view) { gtk_container_forall(GTK_CONTAINER(shell->detail_view->view), destroy_widget, NULL); RANGE_SET_VALUE(detail_view, vscrollbar, 0.0); RANGE_SET_VALUE(detail_view, hscrollbar, 0.0); } static gboolean reload_section(gpointer data) { ShellModuleEntry *entry = (ShellModuleEntry *)data; #if GTK_CHECK_VERSION(2, 14, 0) GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(shell->window)); #endif /* if the entry is still selected, update it */ if (entry->selected) { GtkTreePath *path = NULL; GtkTreeIter iter; double pos_info_scroll; double pos_detail_scroll; /* save current position */ #if GTK_CHECK_VERSION(3, 0, 0) /* TODO:GTK3 */ #else pos_info_scroll = RANGE_GET_VALUE(info_tree, vscrollbar); pos_detail_scroll = RANGE_GET_VALUE(detail_view, vscrollbar); #endif /* avoid drawing the window while we reload */ #if GTK_CHECK_VERSION(2, 14, 0) gdk_window_freeze_updates(gdk_window); #else gdk_window_freeze_updates(shell->window->window); #endif /* gets the current selected path */ if (gtk_tree_selection_get_selected(shell->info_tree->selection, &shell->info_tree->model, &iter)) { path = gtk_tree_model_get_path(shell->info_tree->model, &iter); } /* update the information, clear the treeview and populate it again */ module_entry_reload(entry); detail_view_clear(shell->detail_view); module_selected_show_info(entry, TRUE); /* if there was a selection, reselect it */ if (path) { gtk_tree_selection_select_path(shell->info_tree->selection, path); gtk_tree_view_set_cursor(GTK_TREE_VIEW(shell->info_tree->view), path, NULL, FALSE); gtk_tree_path_free(path); } else { /* restore position */ #if GTK_CHECK_VERSION(3, 0, 0) /* TODO:GTK3 */ #else RANGE_SET_VALUE(info_tree, vscrollbar, pos_info_scroll); #endif } #if !GTK_CHECK_VERSION(3, 0, 0) RANGE_SET_VALUE(detail_view, vscrollbar, pos_detail_scroll); #endif /* make the window drawable again */ #if GTK_CHECK_VERSION(2, 14, 0) gdk_window_thaw_updates(gdk_window); #else gdk_window_thaw_updates(shell->window->window); #endif } /* destroy the timeout: it'll be set up again */ return FALSE; } static gboolean rescan_section(gpointer data) { ShellModuleEntry *entry = (ShellModuleEntry *) data; module_entry_reload(entry); return entry->selected; } static gint compare_float(float a, float b) { return (a > b) - (a < b); } static gint info_tree_compare_val_func(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer userdata) { gint ret = 0; gchar *col1, *col2; gtk_tree_model_get(model, a, INFO_TREE_COL_VALUE, &col1, -1); gtk_tree_model_get(model, b, INFO_TREE_COL_VALUE, &col2, -1); if (!col1 && !col2) ret = 0; else if (!col1) ret = -1; else if (!col2) ret = 1; else if (shell->_order_type == SHELL_ORDER_ASCENDING) ret = compare_float(atof(col2), atof(col1)); else ret = compare_float(atof(col1), atof(col2)); g_free(col1); g_free(col2); return ret; } static void set_view_type(ShellViewType viewtype, gboolean reload) { #if GTK_CHECK_VERSION(2, 18, 0) GtkAllocation* alloc; #endif gboolean type_changed = FALSE; if (viewtype != shell->view_type) type_changed = TRUE; if (viewtype < SHELL_VIEW_NORMAL || viewtype >= SHELL_VIEW_N_VIEWS) viewtype = SHELL_VIEW_NORMAL; shell->normalize_percentage = TRUE; shell->view_type = viewtype; shell->_order_type = SHELL_ORDER_DESCENDING; /* use an unsorted tree model */ GtkTreeSortable *sortable = GTK_TREE_SORTABLE(shell->info_tree->model); gtk_tree_sortable_set_sort_column_id(sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING); gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), GTK_TREE_MODEL(sortable)); /* reset to the default view columns */ if (!reload) { gtk_tree_view_column_set_visible(shell->info_tree->col_extra1, FALSE); gtk_tree_view_column_set_visible(shell->info_tree->col_extra2, FALSE); gtk_tree_view_column_set_visible(shell->info_tree->col_progress, FALSE); gtk_tree_view_column_set_visible(shell->info_tree->col_value, TRUE); } /* turn off the rules hint */ #if GTK_CHECK_VERSION(3, 0, 0) #else gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(shell->info_tree->view), FALSE); #endif close_note(NULL, NULL); detail_view_clear(shell->detail_view); switch (viewtype) { default: case SHELL_VIEW_NORMAL: gtk_widget_show(shell->info_tree->scroll); gtk_widget_hide(shell->notebook); if (!reload) { gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), FALSE); } break; case SHELL_VIEW_DUAL: gtk_widget_show(shell->info_tree->scroll); gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 1); gtk_widget_show(shell->notebook); if (type_changed) { #if GTK_CHECK_VERSION(2, 18, 0) alloc = g_new(GtkAllocation, 1); gtk_widget_get_allocation(shell->hbox, alloc); gtk_paned_set_position(GTK_PANED(shell->vpaned), alloc->height / 2); g_free(alloc); #else gtk_paned_set_position(GTK_PANED(shell->vpaned), shell->hbox->allocation.height / 2); #endif } break; case SHELL_VIEW_LOAD_GRAPH: gtk_widget_show(shell->info_tree->scroll); gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 0); gtk_widget_show(shell->notebook); load_graph_clear(shell->loadgraph); if (type_changed) { #if GTK_CHECK_VERSION(2, 18, 0) alloc = g_new(GtkAllocation, 1); gtk_widget_get_allocation(shell->hbox, alloc); gtk_paned_set_position(GTK_PANED(shell->vpaned), alloc->height - load_graph_get_height(shell->loadgraph) - 16); g_free(alloc); #else gtk_paned_set_position(GTK_PANED(shell->vpaned), shell->hbox->allocation.height - load_graph_get_height(shell->loadgraph) - 16); #endif } break; case SHELL_VIEW_PROGRESS_DUAL: gtk_widget_show(shell->notebook); gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 1); /* fallthrough */ case SHELL_VIEW_PROGRESS: gtk_widget_show(shell->info_tree->scroll); if (!reload) { gtk_tree_view_column_set_visible(shell->info_tree->col_progress, TRUE); gtk_tree_view_column_set_visible(shell->info_tree->col_value, FALSE); } if (viewtype == SHELL_VIEW_PROGRESS) gtk_widget_hide(shell->notebook); break; case SHELL_VIEW_DETAIL: gtk_notebook_set_current_page(GTK_NOTEBOOK(shell->notebook), 1); gtk_widget_show(shell->notebook); gtk_widget_hide(shell->info_tree->scroll); } } static void group_handle_special(GKeyFile *key_file, ShellModuleEntry *entry, const gchar *group, gchar **keys) { if (!g_str_equal(group, "$ShellParam$")) { g_warning("Unknown parameter group: ``%s''", group); return; } gboolean headers_visible = FALSE; gint i; for (i = 0; keys[i]; i++) { gchar *key = keys[i]; if (g_str_has_prefix(key, "UpdateInterval$")) { ShellFieldUpdate *fu = g_new0(ShellFieldUpdate, 1); ShellFieldUpdateSource *sfutbl; gint ms; ms = g_key_file_get_integer(key_file, group, key, NULL); /* Old style used just the label which has to be checked by translating it, * and potentially could by ambiguous. * New style can use tag or label. If new style including a tag, * send both tag and label and let the hi_get_field() function use * key_get_components() to split it. */ const gchar *chk = g_utf8_strchr(key, -1, '$'); fu->field_name = g_strdup(key_is_flagged(chk) ? chk : chk + 1); fu->entry = entry; sfutbl = g_new0(ShellFieldUpdateSource, 1); sfutbl->source_id = g_timeout_add(ms, update_field, fu); sfutbl->sfu = fu; update_sfusrc = g_slist_prepend(update_sfusrc, sfutbl); } else if (g_str_equal(key, "NormalizePercentage")) { shell->normalize_percentage = g_key_file_get_boolean(key_file, group, key, NULL); } else if (g_str_equal(key, "LoadGraphSuffix")) { gchar *suffix = g_key_file_get_value(key_file, group, key, NULL); load_graph_set_data_suffix(shell->loadgraph, suffix); g_free(suffix); } else if (g_str_equal(key, "ReloadInterval")) { gint ms; ms = g_key_file_get_integer(key_file, group, key, NULL); g_timeout_add(ms, reload_section, entry); } else if (g_str_equal(key, "RescanInterval")) { gint ms; ms = g_key_file_get_integer(key_file, group, key, NULL); g_timeout_add(ms, rescan_section, entry); } else if (g_str_equal(key, "ShowColumnHeaders")) { headers_visible = g_key_file_get_boolean(key_file, group, key, NULL); } else if (g_str_has_prefix(key, "ColumnTitle")) { GtkTreeViewColumn *column = NULL; gchar *value, *title = g_utf8_strchr(key, -1, '$') + 1; value = g_key_file_get_value(key_file, group, key, NULL); if (g_str_equal(title, "Extra1")) { column = shell->info_tree->col_extra1; } else if (g_str_equal(title, "Extra2")) { column = shell->info_tree->col_extra2; } else if (g_str_equal(title, "Value")) { column = shell->info_tree->col_value; } else if (g_str_equal(title, "TextValue")) { column = shell->info_tree->col_textvalue; } else if (g_str_equal(title, "Progress")) { column = shell->info_tree->col_progress; } if (column) { gtk_tree_view_column_set_title(column, value); gtk_tree_view_column_set_visible(column, TRUE); } g_free(value); } else if (g_str_equal(key, "OrderType")) { shell->_order_type = g_key_file_get_integer(key_file, group, key, NULL); } else if (g_str_has_prefix(key, "Icon$")) { struct UpdateTableItem *item; const gchar *ikey = g_utf8_strchr(key, -1, '$'); gchar *tag, *name; key_get_components(ikey, NULL, &tag, &name, NULL, NULL, TRUE); if (tag) item = g_hash_table_lookup(update_tbl, tag); else item = g_hash_table_lookup(update_tbl, name); g_free(name); g_free(tag); if (item) { gchar *file = g_key_file_get_value(key_file, group, key, NULL); GdkPixbuf *pixbuf = icon_cache_get_pixbuf_at_size(file, 22, 22); g_free(file); if (item->is_iter) { gtk_tree_store_set( GTK_TREE_STORE(shell->info_tree->model), item->iter, INFO_TREE_COL_PBUF, pixbuf, -1); } else { GList *children = gtk_container_get_children(GTK_CONTAINER(item->widget)); gtk_image_set_from_pixbuf(GTK_IMAGE(children->data), pixbuf); gtk_widget_show(GTK_WIDGET(children->data)); g_list_free(children); } } } else if (g_str_equal(key, "Zebra")) { #if GTK_CHECK_VERSION(3, 0, 0) #else gtk_tree_view_set_rules_hint( GTK_TREE_VIEW(shell->info_tree->view), g_key_file_get_boolean(key_file, group, key, NULL)); #endif } } gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), headers_visible); } static void group_handle_normal(GKeyFile *key_file, ShellModuleEntry *entry, const gchar *group, gchar **keys, gsize ngroups) { GtkTreeIter parent; GtkTreeStore *store = GTK_TREE_STORE(shell->info_tree->model); gint i; if (ngroups > 1) { gtk_tree_store_append(store, &parent, NULL); gchar *tmp = g_strdup(group); strend(tmp, '#'); gtk_tree_store_set(store, &parent, INFO_TREE_COL_NAME, tmp, -1); g_free(tmp); } g_key_file_set_list_separator(key_file, '|'); for (i = 0; keys[i]; i++) { gchar *key = keys[i]; gchar **values; gsize vcount = 0; GtkTreeIter child; values = g_key_file_get_string_list(key_file, group, key, &vcount, NULL); if (!vcount) { /* Check for empty value */ values = g_new0(gchar*, 2); values[0] = g_key_file_get_string(key_file, group, key, NULL); if (values[0]) { vcount = 1; } else { g_strfreev(values); continue; } } if (entry->fieldfunc && values[0] && g_str_equal(values[0], "...")) { g_free(values[0]); values[0] = entry->fieldfunc(key); } if (ngroups == 1) { gtk_tree_store_append(store, &child, NULL); } else { gtk_tree_store_append(store, &child, &parent); } if (vcount > 0) gtk_tree_store_set(store, &child, INFO_TREE_COL_VALUE, values[0], -1); if (vcount > 1) gtk_tree_store_set(store, &child, INFO_TREE_COL_EXTRA1, values[1], -1); if (vcount > 2) gtk_tree_store_set(store, &child, INFO_TREE_COL_EXTRA2, values[2], -1); struct UpdateTableItem *item = g_new0(struct UpdateTableItem, 1); item->is_iter = TRUE; item->iter = gtk_tree_iter_copy(&child); gchar *flags, *tag, *name, *label; key_get_components(key, &flags, &tag, &name, &label, NULL, TRUE); if (flags) { //TODO: name was formerly used where label is here. Check all uses //for problems. gtk_tree_store_set(store, &child, INFO_TREE_COL_NAME, label, INFO_TREE_COL_DATA, flags, -1); g_hash_table_insert(update_tbl, tag, item); g_free(label); } else { gtk_tree_store_set(store, &child, INFO_TREE_COL_NAME, key, INFO_TREE_COL_DATA, NULL, -1); g_hash_table_insert(update_tbl, name, item); g_free(tag); } g_free(flags); g_strfreev(values); } } static void update_progress() { GtkTreeModel *model = shell->info_tree->model; GtkTreeStore *store = GTK_TREE_STORE(model); GtkTreeIter iter, fiter; gchar *tmp; gdouble maxv = INT_MIN, minv = INT_MAX, coeff, cur; if (!gtk_tree_model_get_iter_first(model, &fiter)) return; /* finds the maximum value */ if (shell->normalize_percentage) { iter = fiter; do { gtk_tree_model_get(model, &iter, INFO_TREE_COL_VALUE, &tmp, -1); cur = atof(tmp); if (cur > maxv) maxv = cur; if (cur < minv) minv = cur; g_free(tmp); } while (gtk_tree_model_iter_next(model, &iter)); if (minv - maxv < 0.001) maxv += 1.0f; } else { minv = 1.0f; maxv = 100.0f; } coeff = (100.0f - 1.0f) / (maxv - minv); /* fix the maximum relative percentage */ iter = fiter; do { char *space; char formatted[128]; gdouble pct; gtk_tree_model_get(model, &iter, INFO_TREE_COL_VALUE, &tmp, -1); cur = atof(tmp); space = g_utf8_strchr(tmp, -1, ' '); pct = coeff * (cur - minv) + 1.0f; if (shell->_order_type == SHELL_ORDER_ASCENDING) pct = 100.0 - pct; pct = ceil(pct); if (space) { snprintf(formatted, sizeof(formatted), "%.2f%s", cur, space); } else { snprintf(formatted, sizeof(formatted), "%.2f", cur); } gtk_tree_store_set(store, &iter, INFO_TREE_COL_PROGRESS, pct, INFO_TREE_COL_VALUE, strreplacechr(formatted, ",", '.'), -1); g_free(tmp); } while (gtk_tree_model_iter_next(model, &iter)); /* now sort everything up. that wasn't as hard as i thought :) */ GtkTreeSortable *sortable = GTK_TREE_SORTABLE(shell->info_tree->model); gtk_tree_sortable_set_sort_func(sortable, INFO_TREE_COL_VALUE, info_tree_compare_val_func, 0, NULL); gtk_tree_sortable_set_sort_column_id(sortable, INFO_TREE_COL_VALUE, GTK_SORT_DESCENDING); gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), GTK_TREE_MODEL(sortable)); } void shell_set_note_from_entry(ShellModuleEntry * entry) { if (entry->notefunc) { const gchar *note = module_entry_get_note(entry); if (note) { gtk_label_set_markup(GTK_LABEL(shell->note->label), note); gtk_widget_show(shell->note->event_box); } else { gtk_widget_hide(shell->note->event_box); } } else { gtk_widget_hide(shell->note->event_box); } } void shell_clear_field_updates(void) { if (update_sfusrc) { GSList *sfusrc; for (sfusrc = update_sfusrc; sfusrc; sfusrc = sfusrc->next) { ShellFieldUpdateSource *src = (ShellFieldUpdateSource *) sfusrc->data; g_source_remove(src->source_id); g_free(src->sfu->field_name); g_free(src->sfu); g_free(src); } g_slist_free(update_sfusrc); update_sfusrc = NULL; } } static gboolean select_first_item(gpointer data) { GtkTreeIter first; if (gtk_tree_model_get_iter_first(shell->info_tree->model, &first)) gtk_tree_selection_select_iter(shell->info_tree->selection, &first); return FALSE; } static gboolean select_marked_or_first_item(gpointer data) { GtkTreeIter first, it; gboolean found_selection = FALSE; gchar *datacol; if (gtk_tree_model_get_iter_first(shell->info_tree->model, &first)) { it = first; while (gtk_tree_model_iter_next(shell->info_tree->model, &it)) { gtk_tree_model_get(shell->info_tree->model, &it, INFO_TREE_COL_DATA, &datacol, -1); if (key_is_highlighted(datacol)) { gtk_tree_selection_select_iter(shell->info_tree->selection, &it); found_selection = TRUE; } g_free(datacol); } if (!found_selection) gtk_tree_selection_select_iter(shell->info_tree->selection, &first); } return FALSE; } static void module_selected_show_info_list(GKeyFile *key_file, ShellModuleEntry *entry, gchar **groups, gsize ngroups) { GtkTreeStore *store = GTK_TREE_STORE(shell->info_tree->model); gint i; gtk_tree_store_clear(store); g_object_ref(shell->info_tree->model); gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), NULL); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(shell->info_tree->view), FALSE); for (i = 0; groups[i]; i++) { gchar **keys = g_key_file_get_keys(key_file, groups[i], NULL, NULL); if (groups[i][0] == '$') { group_handle_special(key_file, entry, groups[i], keys); } else { group_handle_normal(key_file, entry, groups[i], keys, ngroups); } g_strfreev(keys); } g_object_unref(shell->info_tree->model); gtk_tree_view_set_model(GTK_TREE_VIEW(shell->info_tree->view), shell->info_tree->model); gtk_tree_view_expand_all(GTK_TREE_VIEW(shell->info_tree->view)); gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(shell->info_tree->view), ngroups > 1); } static gboolean detail_activate_link (GtkLabel *label, gchar *uri, gpointer user_data) { return uri_open(uri); } static gchar *vendor_info_markup(const Vendor *v) { if (!v) return NULL; gchar *ven_mt = NULL; gchar *full_link = NULL, *p = NULL; gchar *ven_tag = v->name_short ? g_strdup(v->name_short) : g_strdup(v->name); tag_vendor(&ven_tag, 0, ven_tag, v->ansi_color, FMT_OPT_PANGO); //if (v->name_short) // ven_mt = appf(ven_mt, "\n", "%s", v->name); ven_mt = appf(ven_mt, "\n", "%s", ven_tag); if (v->url) { if (!g_str_has_prefix(v->url, "http") ) full_link = g_strdup_printf("http://%s", v->url); ven_mt = appf(ven_mt, "\n", "%s: %s", _("URL"), full_link ? full_link : v->url, v->url); g_free(full_link); full_link = NULL; } if (v->url_support) { if (!g_str_has_prefix(v->url_support, "http") ) full_link = g_strdup_printf("http://%s", v->url_support); ven_mt = appf(ven_mt, "\n", "%s: %s", _("Support URL"), full_link ? full_link : v->url_support, v->url_support); g_free(full_link); full_link = NULL; } if (v->wikipedia) { /* sending the title to wikipedia.com/wiki will autmatically handle the language and section parts, * but perhaps that shouldn't be relied on so much? */ full_link = g_strdup_printf("http://wikipedia.com/wiki/%s", v->wikipedia); for(p = full_link; *p; p++) { if (*p == ' ') *p = '_'; } ven_mt = appf(ven_mt, "\n", "%s: %s", _("Wikipedia"), full_link ? full_link : v->wikipedia, v->wikipedia); g_free(full_link); full_link = NULL; } g_free(ven_tag); return ven_mt; } static void module_selected_show_info_detail(GKeyFile *key_file, ShellModuleEntry *entry, gchar **groups) { gint i; detail_view_clear(shell->detail_view); for (i = 0; groups[i]; i++) { gsize nkeys; gchar **keys = g_key_file_get_keys(key_file, groups[i], &nkeys, NULL); gchar *group_label = g_strdup(groups[i]); strend(group_label, '#'); if (entry && groups[i][0] == '$') { group_handle_special(key_file, entry, groups[i], keys); } else { gchar *tmp = g_strdup_printf("%s", group_label); GtkWidget *label = gtk_label_new(tmp); gtk_label_set_use_markup(GTK_LABEL(label), TRUE); GtkWidget *frame = gtk_frame_new(NULL); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); g_free(tmp); gtk_container_set_border_width(GTK_CONTAINER(frame), 6); gtk_box_pack_start(GTK_BOX(shell->detail_view->view), frame, FALSE, FALSE, 0); GtkWidget *table = gtk_table_new(nkeys, 2, FALSE); gtk_container_set_border_width(GTK_CONTAINER(table), 4); gtk_container_add(GTK_CONTAINER(frame), table); gint j, a = 0; for (j = 0; keys[j]; j++) { gchar *key_markup; gchar *value; gchar *name, *label, *tag, *flags; key_get_components(keys[j], &flags, &tag, &name, &label, NULL, TRUE); value = g_key_file_get_string(key_file, groups[i], keys[j], NULL); if (entry && entry->fieldfunc && value && g_str_equal(value, "...")) { g_free(value); value = entry->fieldfunc(keys[j]); } gboolean has_ven = key_value_has_vendor_string(flags); const Vendor *v = has_ven ? vendor_match(value, NULL) : NULL; key_markup = g_strdup_printf("%s", label); GtkWidget *key_label = gtk_label_new(key_markup); gtk_label_set_use_markup(GTK_LABEL(key_label), TRUE); gtk_misc_set_alignment(GTK_MISC(key_label), 1.0f, 0.5f); GtkWidget *value_label = gtk_label_new(value); gtk_label_set_use_markup(GTK_LABEL(value_label), TRUE); gtk_label_set_selectable(GTK_LABEL(value_label), TRUE); #if !GTK_CHECK_VERSION(3, 0, 0) gtk_label_set_line_wrap(GTK_LABEL(value_label), TRUE); #endif gtk_misc_set_alignment(GTK_MISC(value_label), 0.0f, 0.5f); GtkWidget *value_icon = gtk_image_new(); GtkWidget *value_box = gtk_hbox_new(FALSE, 4); gtk_box_pack_start(GTK_BOX(value_box), value_icon, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(value_box), value_label, TRUE, TRUE, 0); g_signal_connect(key_label, "activate-link", G_CALLBACK(detail_activate_link), NULL); g_signal_connect(value_label, "activate-link", G_CALLBACK(detail_activate_link), NULL); gtk_widget_show(key_label); gtk_widget_show(value_box); gtk_widget_show(value_label); gtk_table_attach(GTK_TABLE(table), key_label, 0, 1, j + a, j + a + 1, GTK_FILL, GTK_FILL, 6, 4); gtk_table_attach(GTK_TABLE(table), value_box, 1, 2, j + a, j + a + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 4); if (v) { a++; /* insert a row */ gchar *vendor_markup = vendor_info_markup(v); GtkWidget *vendor_label = gtk_label_new(vendor_markup); gtk_label_set_use_markup(GTK_LABEL(vendor_label), TRUE); gtk_label_set_selectable(GTK_LABEL(vendor_label), TRUE); gtk_misc_set_alignment(GTK_MISC(vendor_label), 0.0f, 0.5f); g_signal_connect(vendor_label, "activate-link", G_CALLBACK(detail_activate_link), NULL); GtkWidget *vendor_box = gtk_hbox_new(FALSE, 4); gtk_box_pack_start(GTK_BOX(vendor_box), vendor_label, TRUE, TRUE, 0); gtk_table_attach(GTK_TABLE(table), vendor_box, 1, 2, j + a, j + a + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 4); gtk_widget_show(vendor_box); gtk_widget_show(vendor_label); g_free(vendor_markup); } struct UpdateTableItem *item = g_new0(struct UpdateTableItem, 1); item->is_iter = FALSE; item->widget = g_object_ref(value_box); if (tag) { g_hash_table_insert(update_tbl, tag, item); g_free(name); } else { g_hash_table_insert(update_tbl, name, item); g_free(tag); } g_free(flags); g_free(value); g_free(key_markup); g_free(label); } gtk_widget_show(table); gtk_widget_show(label); gtk_widget_show(frame); } g_strfreev(keys); g_free(group_label); } } static void module_selected_show_info(ShellModuleEntry *entry, gboolean reload) { GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(shell->info_tree->view)); gsize ngroups; gint i; gdk_window_freeze_updates(gdk_window); module_entry_scan(entry); if (!reload) { /* recreate the iter hash table */ h_hash_table_remove_all(update_tbl); } shell_clear_field_updates(); GKeyFile *key_file = g_key_file_new(); gchar *key_data = module_entry_function(entry); g_key_file_load_from_data(key_file, key_data, strlen(key_data), 0, NULL); set_view_type(g_key_file_get_integer(key_file, "$ShellParam$", "ViewType", NULL), reload); gchar **groups = g_key_file_get_groups(key_file, &ngroups); for (i = 0; groups[i]; i++) { if (groups[i][0] == '$') ngroups--; } if (shell->view_type == SHELL_VIEW_DETAIL) { module_selected_show_info_detail(key_file, entry, groups); } else { module_selected_show_info_list(key_file, entry, groups, ngroups); } g_strfreev(groups); g_key_file_free(key_file); g_free(key_data); switch (shell->view_type) { case SHELL_VIEW_PROGRESS_DUAL: case SHELL_VIEW_PROGRESS: update_progress(); break; } if (!reload) { switch (shell->view_type) { case SHELL_VIEW_DUAL: case SHELL_VIEW_LOAD_GRAPH: case SHELL_VIEW_PROGRESS_DUAL: g_idle_add(select_marked_or_first_item, NULL); } } shell_set_note_from_entry(entry); gdk_window_thaw_updates(gdk_window); } static void info_selected_show_extra(const gchar *tag) { if (!tag || !shell->selected->morefunc) return; GKeyFile *key_file = g_key_file_new(); gchar *key_data = shell->selected->morefunc((gchar *)tag); gchar **groups; g_key_file_load_from_data(key_file, key_data, strlen(key_data), 0, NULL); groups = g_key_file_get_groups(key_file, NULL); module_selected_show_info_detail(key_file, NULL, groups); g_strfreev(groups); g_key_file_free(key_file); g_free(key_data); } static gchar *detail_view_clear_value(gchar *value) { GKeyFile *keyfile; gchar *return_value; keyfile = g_key_file_new(); if (!value) return_value = g_strdup(""); else if (g_key_file_load_from_data(keyfile, value, strlen(value), 0, NULL)) { gchar **groups; gint group; return_value = g_strdup(""); groups = g_key_file_get_groups(keyfile, NULL); for (group = 0; groups[group]; group++) { gchar **keys; gint key; keys = g_key_file_get_keys(keyfile, groups[group], NULL, NULL); for (key = 0; keys[key]; key++) { gchar *temp = keys[key]; if (*temp == '$') { temp++; while (*temp && *temp != '$') temp++; temp++; return_value = h_strdup_cprintf("%s\n", return_value, temp); } else { return_value = g_key_file_get_string(keyfile, groups[group], keys[key], NULL); } } g_strfreev(keys); } g_strfreev(groups); } else { return_value = g_strdup(value); } g_key_file_free(keyfile); return g_strstrip(return_value); } static void detail_view_add_item(DetailView *detail_view, gchar *icon, gchar *name, gchar *value) { GtkWidget *frame; GtkWidget *frame_label_box; GtkWidget *frame_image; GtkWidget *frame_label; GtkWidget *content; GtkWidget *alignment; gchar *temp; temp = detail_view_clear_value(value); /* creates the frame */ frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); #if GTK_CHECK_VERSION(3, 0, 0) frame_label_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); #else frame_label_box = gtk_hbox_new(FALSE, 5); #endif frame_image = icon_cache_get_image(icon); frame_label = gtk_label_new(name); gtk_label_set_use_markup(GTK_LABEL(frame_label), TRUE); gtk_box_pack_start(GTK_BOX(frame_label_box), frame_image, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(frame_label_box), frame_label, FALSE, FALSE, 0); content = gtk_label_new(temp); /* TODO:GTK3 gtk_alignment_new(), etc is deprecated from 3.14 */ #if GTK_CHECK_VERSION(3, 0, 0) GtkWidget *frame_box; frame_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); gtk_widget_set_margin_start(GTK_WIDGET(frame_box), 48); gtk_box_pack_start(GTK_BOX(frame_box), content, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), frame_box); #else alignment = gtk_alignment_new(0.5, 0.5, 1, 1); gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 48, 0); gtk_widget_show(alignment); gtk_container_add(GTK_CONTAINER(frame), alignment); gtk_misc_set_alignment(GTK_MISC(content), 0.0, 0.5); gtk_container_add(GTK_CONTAINER(alignment), content); #endif gtk_widget_show_all(frame); gtk_widget_show_all(frame_label_box); gtk_frame_set_label_widget(GTK_FRAME(frame), frame_label_box); /* pack the item on the detail_view screen */ gtk_box_pack_start(GTK_BOX(shell->detail_view->view), frame, FALSE, FALSE, 4); g_free(temp); } static void detail_view_create_header(DetailView *detail_view, gchar *title) { GtkWidget *header, *label; gchar *temp; temp = g_strdup_printf(_("%s \342\206\222 Summary"), title); header = gtk_menu_item_new_with_label(temp); gtk_menu_item_select(GTK_MENU_ITEM(header)); gtk_widget_show(header); label = gtk_bin_get_child(GTK_BIN(header)); gtk_label_set_use_markup(GTK_LABEL(label), TRUE); gtk_box_pack_start(GTK_BOX(shell->detail_view->view), header, FALSE, FALSE, 4); g_free(temp); } static void shell_show_detail_view(void) { GKeyFile *keyfile; gchar *detail; set_view_type(SHELL_VIEW_DETAIL, FALSE); detail_view_clear(shell->detail_view); detail_view_create_header(shell->detail_view, shell->selected_module->name); keyfile = g_key_file_new(); detail = shell->selected_module->summaryfunc(); if (g_key_file_load_from_data(keyfile, detail, strlen(detail), 0, NULL)) { gchar **groups; gint group; groups = g_key_file_get_groups(keyfile, NULL); for (group = 0; groups[group]; group++) { gchar *icon, *method, *method_result; shell_status_pulse(); icon = g_key_file_get_string(keyfile, groups[group], "Icon", NULL); method = g_key_file_get_string(keyfile, groups[group], "Method", NULL); if (method) { method_result = module_call_method(method); } else { method_result = g_strdup("N/A"); } detail_view_add_item(shell->detail_view, icon, groups[group], method_result); shell_status_pulse(); g_free(icon); g_free(method); g_free(method_result); } g_strfreev(groups); } else { DEBUG("error while parsing detail_view"); set_view_type(SHELL_VIEW_NORMAL, FALSE); } g_free(detail); g_key_file_free(keyfile); shell_view_set_enabled(TRUE); } static void module_selected(gpointer data) { ShellTree *shelltree = shell->tree; GtkTreeModel *model = GTK_TREE_MODEL(shelltree->model); GtkTreeIter iter, parent; ShellModuleEntry *entry; static ShellModuleEntry *current = NULL; static gboolean updating = FALSE; GtkScrollbar *hscrollbar, *vscrollbar; /* Gets the currently selected item on the left-side TreeView; if there is no selection, silently return */ if (!gtk_tree_selection_get_selected(shelltree->selection, &model, &iter)) { return; } /* Mark the currently selected module as "unselected"; this is used to kill the update timeout. */ if (current) { current->selected = FALSE; } if (updating) { return; } else { updating = TRUE; } if (!gtk_tree_model_iter_parent(model, &parent, &iter)) { memcpy(&parent, &iter, sizeof(iter)); } gtk_tree_model_get(model, &parent, TREE_COL_MODULE, &shell->selected_module, -1); /* Get the current selection and shows its related info */ gtk_tree_model_get(model, &iter, TREE_COL_MODULE_ENTRY, &entry, -1); if (entry && !entry->selected) { gchar *title; shell_status_set_enabled(TRUE); shell_status_update(_("Updating...")); entry->selected = TRUE; shell->selected = entry; module_selected_show_info(entry, FALSE); gtk_tree_view_columns_autosize(GTK_TREE_VIEW(shell->info_tree->view)); /* urgh. why don't GTK do this when the model is cleared? */ #if GTK_CHECK_VERSION(3, 0, 0) /* TODO:GTK3 */ #else RANGE_SET_VALUE(info_tree, vscrollbar, 0.0); RANGE_SET_VALUE(info_tree, hscrollbar, 0.0); RANGE_SET_VALUE(detail_view, vscrollbar, 0.0); RANGE_SET_VALUE(detail_view, hscrollbar, 0.0); #endif title = g_strdup_printf("%s - %s", shell->selected_module->name, entry->name); shell_set_title(shell, title); g_free(title); shell_action_set_enabled("RefreshAction", TRUE); shell_action_set_enabled("CopyAction", TRUE); shell_status_update(_("Done.")); shell_status_set_enabled(FALSE); } else { shell_set_title(shell, NULL); shell_action_set_enabled("RefreshAction", FALSE); shell_action_set_enabled("CopyAction", FALSE); gtk_tree_store_clear(GTK_TREE_STORE(shell->info_tree->model)); set_view_type(SHELL_VIEW_NORMAL, FALSE); if (shell->selected_module->summaryfunc) { shell_show_detail_view(); } } current = entry; updating = FALSE; } static void info_selected(GtkTreeSelection * ts, gpointer data) { ShellInfoTree *info = (ShellInfoTree *) data; GtkTreeModel *model = GTK_TREE_MODEL(info->model); GtkTreeIter parent; gchar *datacol, *mi_tag; if (!gtk_tree_selection_get_selected(ts, &model, &parent)) return; if (shell->view_type == SHELL_VIEW_NORMAL || shell->view_type == SHELL_VIEW_PROGRESS) { gtk_tree_selection_unselect_all(ts); return; } gtk_tree_model_get(model, &parent, INFO_TREE_COL_DATA, &datacol, -1); mi_tag = key_mi_tag(datacol); info_selected_show_extra(mi_tag); g_free(mi_tag); } static ShellInfoTree *info_tree_new(void) { ShellInfoTree *info; GtkWidget *treeview, *scroll; GtkTreeModel *model; GtkTreeStore *store; GtkTreeViewColumn *column; GtkCellRenderer *cr_text, *cr_pbuf, *cr_progress; GtkTreeSelection *sel; info = g_new0(ShellInfoTree, 1); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); store = gtk_tree_store_new(INFO_TREE_NCOL, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_FLOAT, G_TYPE_STRING, G_TYPE_STRING); model = GTK_TREE_MODEL(store); treeview = gtk_tree_view_new_with_model(model); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE); info->col_progress = column = gtk_tree_view_column_new(); gtk_tree_view_column_set_visible(column, FALSE); gtk_tree_view_column_set_min_width(column, 240); gtk_tree_view_column_set_clickable(column, TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); cr_progress = gtk_cell_renderer_progress_new(); gtk_tree_view_column_pack_start(column, cr_progress, TRUE); gtk_tree_view_column_add_attribute(column, cr_progress, "value", INFO_TREE_COL_PROGRESS); gtk_tree_view_column_add_attribute(column, cr_progress, "text", INFO_TREE_COL_VALUE); gtk_tree_view_column_set_visible(column, FALSE); info->col_textvalue = column = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); gtk_tree_view_column_set_clickable(column, TRUE); cr_pbuf = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, cr_pbuf, FALSE); gtk_tree_view_column_add_attribute(column, cr_pbuf, "pixbuf", INFO_TREE_COL_PBUF); cr_text = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, cr_text, TRUE); gtk_tree_view_column_add_attribute(column, cr_text, "markup", INFO_TREE_COL_NAME); info->col_extra1 = column = gtk_tree_view_column_new(); gtk_tree_view_column_set_visible(column, FALSE); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); gtk_tree_view_column_set_clickable(column, TRUE); cr_text = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, cr_text, FALSE); gtk_tree_view_column_add_attribute(column, cr_text, "markup", INFO_TREE_COL_EXTRA1); info->col_extra2 = column = gtk_tree_view_column_new(); gtk_tree_view_column_set_visible(column, FALSE); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); gtk_tree_view_column_set_clickable(column, TRUE); cr_text = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, cr_text, FALSE); gtk_tree_view_column_add_attribute(column, cr_text, "markup", INFO_TREE_COL_EXTRA2); info->col_value = column = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); gtk_tree_view_column_set_clickable(column, TRUE); cr_text = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, cr_text, FALSE); gtk_tree_view_column_add_attribute(column, cr_text, "markup", INFO_TREE_COL_VALUE); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(sel), "changed", (GCallback)info_selected, info); gtk_container_add(GTK_CONTAINER(scroll), treeview); info->scroll = scroll; info->view = treeview; info->model = model; info->selection = sel; gtk_widget_show_all(scroll); return info; } static ShellTree *tree_new() { ShellTree *shelltree; GtkWidget *treeview, *scroll; GtkTreeModel *model; GtkTreeStore *store; GtkCellRenderer *cr_text, *cr_pbuf; GtkTreeViewColumn *column; GtkTreeSelection *sel; shelltree = g_new0(ShellTree, 1); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); store = gtk_tree_store_new(TREE_NCOL, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_BOOLEAN); model = GTK_TREE_MODEL(store); treeview = gtk_tree_view_new_with_model(model); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); #if GTK_CHECK_VERSION(2,12,0) gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(treeview), FALSE); gtk_tree_view_set_level_indentation(GTK_TREE_VIEW(treeview), 24); #endif column = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); cr_pbuf = gtk_cell_renderer_pixbuf_new(); cr_text = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, cr_pbuf, FALSE); gtk_tree_view_column_pack_start(column, cr_text, TRUE); gtk_tree_view_column_add_attribute(column, cr_pbuf, "pixbuf", TREE_COL_PBUF); gtk_tree_view_column_add_attribute(column, cr_text, "markup", TREE_COL_NAME); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); g_signal_connect(G_OBJECT(sel), "changed", (GCallback) module_selected, NULL); gtk_container_add(GTK_CONTAINER(scroll), treeview); shelltree->scroll = scroll; shelltree->view = treeview; shelltree->model = model; shelltree->modules = NULL; shelltree->selection = sel; gtk_widget_show_all(scroll); return shelltree; } gboolean key_is_flagged(const gchar *key) { return (key && *key == '$' && strchr(key+1, '$')) ? TRUE : FALSE; } gboolean key_is_highlighted(const gchar *key) { gchar *flags; key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); if (flags && strchr(flags, '*')) { g_free(flags); return TRUE; } return FALSE; } gboolean key_wants_details(const gchar *key) { gchar *flags; key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); if (flags && strchr(flags, '!')) { g_free(flags); return TRUE; } return FALSE; } gboolean key_value_has_vendor_string(const gchar *key) { gchar *flags; key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); if (flags && strchr(flags, '^')) { g_free(flags); return TRUE; } return FALSE; } gboolean key_label_is_escaped(const gchar *key) { gchar *flags; key_get_components(key, &flags, NULL, NULL, NULL, NULL, TRUE); if (flags && strchr(flags, '@')) { g_free(flags); return TRUE; } return FALSE; } gchar *key_mi_tag(const gchar *key) { static char flag_list[] = "*!^@"; gchar *p = (gchar*)key, *l, *t; if (key_is_flagged(key)) { l = strchr(key+1, '$'); if (*p == '$') p++; /* skip first if exists */ while(p < l && strchr(flag_list, *p)) { p++; } if (strlen(p)) { t = g_strdup(p); *(strchr(t, '$')) = 0; return t; } } return NULL; } const gchar *key_get_name(const gchar *key) { if (key_is_flagged(key)) return strchr(key+1, '$')+1; return key; } /* key syntax: * [$[][]$][#[]] * * example for key = "$*!Foo$Bar#7": * flags = "$*!^Foo$" // key_is/wants_*() still works on flags * tag = "Foo" // the moreinfo/icon tag * name = "Bar#7" // the full unique name * label = "Bar" // the label displayed * dis = "7" */ void key_get_components(const gchar *key, gchar **flags, gchar **tag, gchar **name, gchar **label, gchar **dis, gboolean null_empty) { if (null_empty) { #define K_NULL_EMPTY(f) if (f) { *f = NULL; } K_NULL_EMPTY(flags); K_NULL_EMPTY(tag); K_NULL_EMPTY(name); K_NULL_EMPTY(label); K_NULL_EMPTY(dis); } if (!key || !*key) return; const gchar *np = g_utf8_strchr(key+1, -1, '$') + 1; if (*key == '$' && np) { /* is flagged */ gchar *f = g_strdup(key); gchar *s = g_utf8_strchr(f+1, -1, '$'); if(s==NULL) { DEBUG("ERROR NOT FOUND"); }else{ /* if((s-f+1)>strlen(key)) { DEBUG("ERROR TOO LATE"); }else{*/ *(g_utf8_strchr(f+1, -1, '$') + 1) = 0; if (flags) *flags = g_strdup(f); if (tag) *tag = key_mi_tag(f); g_free(f); //} } } else np = key; if (name) *name = g_strdup(np); if (label) { *label = g_strdup(np); gchar *lbp = g_utf8_strchr(*label, -1, '#'); if (lbp) *lbp = 0; if (lbp && dis) *dis = g_strdup(lbp + 1); if (flags && *flags && strchr(*flags, '@')) { gchar *ol = *label; *label = g_strcompress(ol); g_free(ol); } } }