summaryrefslogtreecommitdiffstats
path: root/src/interface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface.cpp')
-rw-r--r--src/interface.cpp887
1 files changed, 887 insertions, 0 deletions
diff --git a/src/interface.cpp b/src/interface.cpp
new file mode 100644
index 0000000..5b8ac6b
--- /dev/null
+++ b/src/interface.cpp
@@ -0,0 +1,887 @@
+/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* Procman - main window
+ * Copyright (C) 2001 Kevin Vandersloot
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <handy.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+
+#include "interface.h"
+#include "proctable.h"
+#include "procactions.h"
+#include "procdialogs.h"
+#include "setaffinity.h"
+#include "memmaps.h"
+#include "openfiles.h"
+#include "procproperties.h"
+#include "load-graph.h"
+#include "util.h"
+#include "disks.h"
+#include "settings-keys.h"
+#include "legacy/gsm_color_button.h"
+
+static const char* LOAD_GRAPH_CSS = "\
+.loadgraph {\
+ background: linear-gradient(to bottom,\
+ @theme_bg_color,\
+ @theme_base_color\
+ );\
+ color: mix (@theme_fg_color, @theme_bg_color, 0.5);\
+}\
+";
+
+static gboolean
+cb_window_key_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ const char *current_page = gtk_stack_get_visible_child_name (GTK_STACK (GsmApplication::get()->stack));
+
+ if (strcmp (current_page, "processes") == 0)
+ return gtk_search_bar_handle_event (GTK_SEARCH_BAR (user_data), event);
+
+ return FALSE;
+}
+
+static void
+search_text_changed (GtkEditable *entry, gpointer data)
+{
+ GsmApplication * const app = static_cast<GsmApplication *>(data);
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (gtk_tree_model_sort_get_model (
+ GTK_TREE_MODEL_SORT (gtk_tree_view_get_model(
+ GTK_TREE_VIEW (app->tree))))));
+}
+
+static void
+set_affinity_visiblity (GtkWidget *widget, gpointer user_data)
+{
+#ifndef __linux__
+ GtkMenuItem *item = GTK_MENU_ITEM (widget);
+ const gchar *name = gtk_menu_item_get_label (item);
+
+ if (strcmp (name, "Set _Affinity") == 0) {
+ gtk_widget_set_visible (widget, false);
+ }
+#endif
+}
+
+static void
+create_proc_view(GsmApplication *app, GtkBuilder * builder)
+{
+ GsmTreeView *proctree;
+ GtkScrolledWindow *scrolled;
+
+ proctree = proctable_new (app);
+ scrolled = GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "processes_scrolled"));
+
+ gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (proctree));
+
+ app->proc_actionbar_revealer = GTK_REVEALER (gtk_builder_get_object (builder, "proc_actionbar_revealer"));
+
+ /* create popup_menu for the processes tab */
+ GMenuModel *menu_model = G_MENU_MODEL (gtk_builder_get_object (builder, "process-popup-menu"));
+ app->popup_menu = GTK_MENU (gtk_menu_new_from_model (menu_model));
+ gtk_menu_attach_to_widget (app->popup_menu, GTK_WIDGET (app->main_window), NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (app->popup_menu), set_affinity_visiblity, NULL);
+
+ app->search_bar = GTK_SEARCH_BAR (gtk_builder_get_object (builder, "proc_searchbar"));
+ app->search_entry = GTK_SEARCH_ENTRY (gtk_builder_get_object (builder, "proc_searchentry"));
+
+ gtk_search_bar_connect_entry (app->search_bar, GTK_ENTRY (app->search_entry));
+ g_signal_connect (app->main_window, "key-press-event",
+ G_CALLBACK (cb_window_key_press_event), app->search_bar);
+
+ g_signal_connect (app->search_entry, "changed", G_CALLBACK (search_text_changed), app);
+
+ g_object_bind_property (app->search_bar, "search-mode-enabled", app->search_button, "active", (GBindingFlags)(G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE));
+}
+
+static void
+cb_cpu_color_changed (GsmColorButton *cp, gpointer data)
+{
+ guint cpu_i = GPOINTER_TO_UINT (data);
+ auto settings = Gio::Settings::create (GSM_GSETTINGS_SCHEMA);
+
+ /* Get current values */
+ GVariant *cpu_colors_var = g_settings_get_value (settings->gobj(), GSM_SETTING_CPU_COLORS);
+ gsize children_n = g_variant_n_children(cpu_colors_var);
+
+ /* Create builder to construct new setting with updated value for cpu i */
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+
+ for (guint i = 0; i < children_n; i++) {
+ if(cpu_i == i) {
+ gchar *color;
+ GdkRGBA button_color;
+ gsm_color_button_get_color(cp, &button_color);
+ color = gdk_rgba_to_string (&button_color);
+ g_variant_builder_add(&builder, "(us)", i, color);
+ g_free (color);
+ } else {
+ g_variant_builder_add_value(&builder,
+ g_variant_get_child_value(cpu_colors_var, i));
+ }
+ }
+
+ /* Just set the value and let the changed::cpu-colors signal callback do the rest. */
+ settings->set_value (GSM_SETTING_CPU_COLORS, Glib::wrap (g_variant_builder_end(&builder)));
+}
+
+static void change_settings_color(Gio::Settings& settings, const char *key,
+ GsmColorButton *cp)
+{
+ GdkRGBA c;
+ char *color;
+
+ gsm_color_button_get_color(cp, &c);
+ color = gdk_rgba_to_string (&c);
+ settings.set_string (key, color);
+ g_free (color);
+}
+
+static void
+cb_mem_color_changed (GsmColorButton *cp, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ change_settings_color (*app->settings.operator->(), GSM_SETTING_MEM_COLOR, cp);
+}
+
+
+static void
+cb_swap_color_changed (GsmColorButton *cp, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ change_settings_color (*app->settings.operator->(), GSM_SETTING_SWAP_COLOR, cp);
+}
+
+static void
+cb_net_in_color_changed (GsmColorButton *cp, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ change_settings_color (*app->settings.operator->(), GSM_SETTING_NET_IN_COLOR, cp);
+}
+
+static void
+cb_net_out_color_changed (GsmColorButton *cp, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ change_settings_color(*app->settings.operator->(), GSM_SETTING_NET_OUT_COLOR, cp);
+}
+
+static void
+create_sys_view (GsmApplication *app, GtkBuilder * builder)
+{
+ GtkBox *cpu_graph_box, *mem_graph_box, *net_graph_box;
+ GtkExpander *cpu_expander, *mem_expander, *net_expander;
+ GtkLabel *label,*cpu_label;
+ GtkGrid *table;
+ GsmColorButton *color_picker;
+ GtkCssProvider *provider;
+
+ LoadGraph *cpu_graph, *mem_graph, *net_graph;
+
+ gint i;
+ gchar *title_text;
+ gchar *label_text;
+ gchar *title_template;
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_data (provider, LOAD_GRAPH_CSS, -1, NULL);
+ gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ // Translators: color picker title, %s is CPU, Memory, Swap, Receiving, Sending
+ title_template = g_strdup(_("Pick a Color for “%s”"));
+
+ /* The CPU BOX */
+
+ cpu_graph_box = GTK_BOX (gtk_builder_get_object (builder, "cpu_graph_box"));
+ cpu_expander = GTK_EXPANDER (gtk_builder_get_object (builder, "cpu_expander"));
+ g_object_bind_property (cpu_expander, "expanded", cpu_expander, "vexpand", G_BINDING_DEFAULT);
+
+ cpu_graph = new LoadGraph(LOAD_GRAPH_CPU);
+ gtk_widget_set_size_request (GTK_WIDGET(load_graph_get_widget(cpu_graph)), -1, 70);
+ gtk_box_pack_start (cpu_graph_box,
+ GTK_WIDGET (load_graph_get_widget(cpu_graph)),
+ TRUE,
+ TRUE,
+ 0);
+
+ GtkGrid* cpu_table = GTK_GRID (gtk_builder_get_object (builder, "cpu_table"));
+ gint cols = 4;
+ for (i=0;i<app->config.num_cpus; i++) {
+ GtkBox *temp_hbox;
+
+ temp_hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0));
+ gtk_widget_show (GTK_WIDGET (temp_hbox));
+ if (i < cols) {
+ gtk_grid_insert_column(cpu_table, i%cols);
+ }
+ if ((i+1)%cols ==cols) {
+ gtk_grid_insert_row(cpu_table, (i+1)/cols);
+ }
+ gtk_grid_attach(cpu_table, GTK_WIDGET (temp_hbox), i%cols, i/cols, 1, 1);
+ color_picker = gsm_color_button_new (&cpu_graph->colors.at(i), GSMCP_TYPE_CPU);
+ g_signal_connect (G_OBJECT (color_picker), "color-set",
+ G_CALLBACK (cb_cpu_color_changed), GINT_TO_POINTER (i));
+ gtk_box_pack_start (temp_hbox, GTK_WIDGET (color_picker), FALSE, TRUE, 0);
+ gtk_widget_set_size_request(GTK_WIDGET(color_picker), 32, -1);
+ if(app->config.num_cpus == 1) {
+ label_text = g_strdup (_("CPU"));
+ } else {
+ label_text = g_strdup_printf (_("CPU%d"), i+1);
+ }
+ title_text = g_strdup_printf(title_template, label_text);
+ label = GTK_LABEL (gtk_label_new (label_text));
+ gtk_label_set_xalign(label, 0.0);
+ if(app->config.num_cpus >=10) {
+ gtk_label_set_width_chars(label, log10(app->config.num_cpus)+1+4);
+ }
+ gsm_color_button_set_title(color_picker, title_text);
+ g_free(title_text);
+ gtk_box_pack_start (temp_hbox, GTK_WIDGET (label), FALSE, FALSE, 6);
+ gtk_widget_show (GTK_WIDGET (label));
+ g_free (label_text);
+
+ cpu_label = make_tnum_label ();
+
+ /* Reserve some space to avoid the layout changing with the values. */
+ gtk_label_set_width_chars(cpu_label, 6);
+ gtk_label_set_xalign(cpu_label, 1.0);
+ gtk_widget_set_valign (GTK_WIDGET (cpu_label), GTK_ALIGN_CENTER);
+ gtk_widget_set_halign (GTK_WIDGET (cpu_label), GTK_ALIGN_START);
+ gtk_box_pack_start (temp_hbox, GTK_WIDGET (cpu_label), FALSE, FALSE, 0);
+ gtk_widget_show (GTK_WIDGET (cpu_label));
+ load_graph_get_labels(cpu_graph)->cpu[i] = cpu_label;
+
+ }
+
+ app->cpu_graph = cpu_graph;
+
+ /** The memory box */
+
+ mem_graph_box = GTK_BOX (gtk_builder_get_object (builder, "mem_graph_box"));
+ mem_expander = GTK_EXPANDER (gtk_builder_get_object (builder, "mem_expander"));
+ g_object_bind_property (mem_expander, "expanded", mem_expander, "vexpand", G_BINDING_DEFAULT);
+
+ mem_graph = new LoadGraph(LOAD_GRAPH_MEM);
+ gtk_widget_set_size_request (GTK_WIDGET(load_graph_get_widget(mem_graph)), -1, 70);
+ gtk_box_pack_start (mem_graph_box,
+ GTK_WIDGET (load_graph_get_widget(mem_graph)),
+ TRUE,
+ TRUE,
+ 0);
+
+ table = GTK_GRID (gtk_builder_get_object (builder, "mem_table"));
+
+ color_picker = load_graph_get_mem_color_picker(mem_graph);
+ g_signal_connect (G_OBJECT (color_picker), "color-set",
+ G_CALLBACK (cb_mem_color_changed), app);
+ title_text = g_strdup_printf(title_template, _("Memory"));
+ gsm_color_button_set_title(color_picker, title_text);
+ g_free(title_text);
+
+ label = GTK_LABEL (gtk_builder_get_object(builder, "memory_label"));
+
+ gtk_grid_attach_next_to (table, GTK_WIDGET (color_picker), GTK_WIDGET (label), GTK_POS_LEFT, 1, 3);
+ gtk_grid_attach_next_to (table, GTK_WIDGET (load_graph_get_labels(mem_graph)->memory), GTK_WIDGET (label), GTK_POS_BOTTOM, 1, 2);
+
+ color_picker = load_graph_get_swap_color_picker(mem_graph);
+ g_signal_connect (G_OBJECT (color_picker), "color-set",
+ G_CALLBACK (cb_swap_color_changed), app);
+ title_text = g_strdup_printf(title_template, _("Swap"));
+ gsm_color_button_set_title(GSM_COLOR_BUTTON(color_picker), title_text);
+ g_free(title_text);
+
+ label = GTK_LABEL (gtk_builder_get_object(builder, "swap_label"));
+
+ gtk_grid_attach_next_to (table, GTK_WIDGET (color_picker), GTK_WIDGET (label), GTK_POS_LEFT, 1, 3);
+ gtk_grid_attach_next_to (table, GTK_WIDGET (load_graph_get_labels(mem_graph)->swap), GTK_WIDGET (label), GTK_POS_BOTTOM, 1, 2);
+
+ app->mem_graph = mem_graph;
+
+ /* The net box */
+
+ net_graph_box = GTK_BOX (gtk_builder_get_object (builder, "net_graph_box"));
+ net_expander = GTK_EXPANDER (gtk_builder_get_object (builder, "net_expander"));
+ g_object_bind_property (net_expander, "expanded", net_expander, "vexpand", G_BINDING_DEFAULT);
+
+ net_graph = new LoadGraph(LOAD_GRAPH_NET);
+ gtk_widget_set_size_request (GTK_WIDGET(load_graph_get_widget(net_graph)), -1, 70);
+ gtk_box_pack_start (net_graph_box,
+ GTK_WIDGET (load_graph_get_widget(net_graph)),
+ TRUE,
+ TRUE,
+ 0);
+
+ table = GTK_GRID (gtk_builder_get_object (builder, "net_table"));
+
+ color_picker = gsm_color_button_new (
+ &net_graph->colors.at(0), GSMCP_TYPE_NETWORK_IN);
+ gtk_widget_set_valign (GTK_WIDGET(color_picker), GTK_ALIGN_CENTER);
+ g_signal_connect (G_OBJECT (color_picker), "color-set",
+ G_CALLBACK (cb_net_in_color_changed), app);
+ title_text = g_strdup_printf(title_template, _("Receiving"));
+ gsm_color_button_set_title(color_picker, title_text);
+ g_free(title_text);
+
+ label = GTK_LABEL (gtk_builder_get_object(builder, "receiving_label"));
+ gtk_grid_attach_next_to (table, GTK_WIDGET (color_picker), GTK_WIDGET (label), GTK_POS_LEFT, 1, 2);
+ gtk_grid_attach_next_to (table, GTK_WIDGET (load_graph_get_labels(net_graph)->net_in), GTK_WIDGET (label), GTK_POS_RIGHT, 1, 1);
+ label = GTK_LABEL (gtk_builder_get_object(builder, "total_received_label"));
+ gtk_grid_attach_next_to (table, GTK_WIDGET (load_graph_get_labels(net_graph)->net_in_total), GTK_WIDGET (label), GTK_POS_RIGHT, 1, 1);
+
+ color_picker = gsm_color_button_new (
+ &net_graph->colors.at(1), GSMCP_TYPE_NETWORK_OUT);
+ gtk_widget_set_valign (GTK_WIDGET(color_picker), GTK_ALIGN_CENTER);
+ gtk_widget_set_hexpand (GTK_WIDGET(color_picker), true);
+ gtk_widget_set_halign (GTK_WIDGET(color_picker), GTK_ALIGN_END);
+
+ g_signal_connect (G_OBJECT (color_picker), "color-set",
+ G_CALLBACK (cb_net_out_color_changed), app);
+ title_text = g_strdup_printf(title_template, _("Sending"));
+ gsm_color_button_set_title(color_picker, title_text);
+ g_free(title_text);
+
+ label = GTK_LABEL (gtk_builder_get_object(builder, "sending_label"));
+ gtk_grid_attach_next_to (table, GTK_WIDGET (color_picker), GTK_WIDGET (label), GTK_POS_LEFT, 1, 2);
+ gtk_grid_attach_next_to (table, GTK_WIDGET (load_graph_get_labels(net_graph)->net_out), GTK_WIDGET (label), GTK_POS_RIGHT, 1, 1);
+ label = GTK_LABEL (gtk_builder_get_object(builder, "total_sent_label"));
+ gtk_grid_attach_next_to (table, GTK_WIDGET (load_graph_get_labels(net_graph)->net_out_total), GTK_WIDGET (label), GTK_POS_RIGHT, 1, 1);
+ gtk_widget_set_hexpand (GTK_WIDGET(load_graph_get_labels(net_graph)->net_out_total), true);
+ gtk_widget_set_halign (GTK_WIDGET(load_graph_get_labels(net_graph)->net_out_total), GTK_ALIGN_START);
+
+ gtk_widget_set_hexpand (GTK_WIDGET(load_graph_get_labels(net_graph)->net_out), true);
+ gtk_widget_set_halign (GTK_WIDGET(load_graph_get_labels(net_graph)->net_out), GTK_ALIGN_START);
+
+
+ app->net_graph = net_graph;
+ g_free (title_template);
+
+}
+
+static void
+on_activate_about (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ const gchar * const authors[] = {
+ "Kevin Vandersloot",
+ "Erik Johnsson",
+ "Jorgen Scheibengruber",
+ "Benoît Dejean",
+ "Paolo Borelli",
+ "Karl Lattimer",
+ "Chris Kühl",
+ "Robert Roth",
+ "Stefano Facchini",
+ "Jacob Barkdull",
+ NULL
+ };
+
+ const gchar * const documenters[] = {
+ "Bill Day",
+ "Sun Microsystems",
+ NULL
+ };
+
+ const gchar * const artists[] = {
+ "Baptiste Mille-Mathias",
+ NULL
+ };
+
+ gtk_show_about_dialog (
+ GTK_WINDOW (app->main_window),
+ "name", _("System Monitor"),
+ "comments", _("View current processes and monitor "
+ "system state"),
+ "version", VERSION,
+ "website", "https://wiki.gnome.org/Apps/SystemMonitor",
+ "copyright", "Copyright \xc2\xa9 2001-2004 Kevin Vandersloot\n"
+ "Copyright \xc2\xa9 2005-2007 Benoît Dejean\n"
+ "Copyright \xc2\xa9 2011 Chris Kühl",
+ "logo-icon-name", "org.gnome.SystemMonitor",
+ "authors", authors,
+ "artists", artists,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "license-type", GTK_LICENSE_GPL_2_0,
+ NULL
+ );
+}
+
+static void
+on_activate_keyboard_shortcuts (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ gtk_widget_show (GTK_WIDGET (gtk_application_window_get_help_overlay (GTK_APPLICATION_WINDOW (app->main_window))));
+}
+
+static void
+on_activate_refresh (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ proctable_update (app);
+}
+
+static void
+kill_process_with_confirmation (GsmApplication *app, int signal) {
+ gboolean kill_dialog = app->settings->get_boolean(GSM_SETTING_SHOW_KILL_DIALOG);
+
+ if (kill_dialog)
+ procdialog_create_kill_dialog (app, signal);
+ else
+ kill_process (app, signal);
+}
+
+static void
+on_activate_send_signal (GSimpleAction *, GVariant *parameter, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ /* no confirmation */
+ gint32 signal = g_variant_get_int32(parameter);
+ switch (signal) {
+ case SIGCONT:
+ kill_process (app, signal);
+ break;
+ case SIGSTOP:
+ case SIGTERM:
+ case SIGKILL:
+ kill_process_with_confirmation (app, signal);
+ break;
+ }
+}
+
+static void
+on_activate_set_affinity (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ create_set_affinity_dialog (app);
+}
+
+static void
+on_activate_memory_maps (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ create_memmaps_dialog (app);
+}
+
+static void
+on_activate_open_files (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ create_openfiles_dialog (app);
+}
+
+static void
+on_activate_process_properties (GSimpleAction *, GVariant *, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ create_procproperties_dialog (app);
+}
+
+static void
+on_activate_radio (GSimpleAction *action, GVariant *parameter, gpointer data)
+{
+ g_action_change_state (G_ACTION (action), parameter);
+}
+
+static void
+on_activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer data)
+{
+ GVariant *state = g_action_get_state (G_ACTION (action));
+ g_action_change_state (G_ACTION (action), g_variant_new_boolean (!g_variant_get_boolean (state)));
+ g_variant_unref (state);
+}
+
+static void
+on_activate_search (GSimpleAction *action, GVariant *parameter, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ GVariant *state = g_action_get_state (G_ACTION (action));
+ gboolean is_search_shortcut = g_variant_get_boolean (parameter);
+ gboolean is_search_bar = gtk_search_bar_get_search_mode (app->search_bar);
+ gtk_widget_set_visible (GTK_WIDGET (app->search_bar), is_search_bar || is_search_shortcut);
+ if (is_search_shortcut && is_search_bar) {
+ gtk_widget_grab_focus (GTK_WIDGET (app->search_entry));
+ } else {
+ g_action_change_state (G_ACTION (action), g_variant_new_boolean (!g_variant_get_boolean (state)));
+ }
+ g_variant_unref (state);
+}
+
+static void
+change_show_page_state (GSimpleAction *action, GVariant *state, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ auto state_var = Glib::wrap(state, true);
+ g_simple_action_set_state (action, state);
+ app->settings->set_value (GSM_SETTING_CURRENT_TAB, state_var);
+}
+
+static void
+change_show_processes_state (GSimpleAction *action, GVariant *state, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ auto state_var = Glib::wrap(state, true);
+ g_simple_action_set_state (action, state);
+ app->settings->set_value (GSM_SETTING_SHOW_WHOSE_PROCESSES, state_var);
+}
+
+static void
+change_show_dependencies_state (GSimpleAction *action, GVariant *state, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ auto state_var = Glib::wrap(state, true);
+ g_simple_action_set_state (action, state);
+ app->settings->set_value (GSM_SETTING_SHOW_DEPENDENCIES, state_var);
+}
+
+static void
+on_activate_priority (GSimpleAction *action, GVariant *parameter, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ g_action_change_state (G_ACTION (action), parameter);
+
+ const gint32 priority = g_variant_get_int32 (parameter);
+ switch (priority) {
+ case 32:
+ procdialog_create_renice_dialog (app);
+ break;
+ default:
+ renice (app, priority);
+ break;
+ }
+
+}
+
+static void
+change_priority_state (GSimpleAction *action, GVariant *state, gpointer data)
+{
+ g_simple_action_set_state (action, state);
+}
+
+static void
+update_page_activities (GsmApplication *app)
+{
+ const char *current_page = gtk_stack_get_visible_child_name (app->stack);
+
+ if (strcmp (current_page, "processes") == 0) {
+ GAction *search_action = g_action_map_lookup_action (G_ACTION_MAP (app->main_window),
+ "search");
+ proctable_update (app);
+ proctable_thaw (app);
+
+ gtk_widget_show (GTK_WIDGET (app->end_process_button));
+ gtk_widget_show (GTK_WIDGET (app->search_button));
+ gtk_widget_show (GTK_WIDGET (app->process_menu_button));
+ gtk_widget_hide (GTK_WIDGET (app->window_menu_button));
+
+ update_sensitivity (app);
+
+ if (g_variant_get_boolean (g_action_get_state (search_action)))
+ gtk_widget_grab_focus (GTK_WIDGET (app->search_entry));
+ else
+ gtk_widget_grab_focus (GTK_WIDGET (app->tree));
+ } else {
+ proctable_freeze (app);
+
+ gtk_widget_hide (GTK_WIDGET (app->end_process_button));
+ gtk_widget_hide (GTK_WIDGET (app->search_button));
+ gtk_widget_hide (GTK_WIDGET (app->process_menu_button));
+ gtk_widget_show (GTK_WIDGET (app->window_menu_button));
+
+ update_sensitivity (app);
+ }
+
+ if (strcmp (current_page, "resources") == 0) {
+ load_graph_start (app->cpu_graph);
+ load_graph_start (app->mem_graph);
+ load_graph_start (app->net_graph);
+ } else {
+ load_graph_stop (app->cpu_graph);
+ load_graph_stop (app->mem_graph);
+ load_graph_stop (app->net_graph);
+ }
+
+ if (strcmp (current_page, "disks") == 0) {
+ disks_update (app);
+ disks_thaw (app);
+ } else {
+ disks_freeze (app);
+ }
+}
+
+static void
+cb_change_current_page (GtkStack *stack, GParamSpec *pspec, gpointer data)
+{
+ update_page_activities ((GsmApplication *)data);
+}
+
+static gboolean
+cb_main_window_delete (GtkWidget *window, GdkEvent *event, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+
+ app->shutdown ();
+
+ return TRUE;
+}
+
+static gboolean
+cb_main_window_state_changed (GtkWidget *window, GdkEventWindowState *event, gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ auto current_page = app->settings->get_string (GSM_SETTING_CURRENT_TAB);
+ if (event->new_window_state & GDK_WINDOW_STATE_BELOW ||
+ event->new_window_state & GDK_WINDOW_STATE_ICONIFIED ||
+ event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)
+ {
+ if (current_page == "processes") {
+ proctable_freeze (app);
+ } else if (current_page == "resources") {
+ load_graph_stop (app->cpu_graph);
+ load_graph_stop (app->mem_graph);
+ load_graph_stop (app->net_graph);
+ } else if (current_page == "disks") {
+ disks_freeze (app);
+ }
+ } else {
+ if (current_page == "processes") {
+ proctable_update (app);
+ proctable_thaw (app);
+ } else if (current_page == "resources") {
+ load_graph_start (app->cpu_graph);
+ load_graph_start (app->mem_graph);
+ load_graph_start (app->net_graph);
+ } else if (current_page == "disks") {
+ disks_update (app);
+ disks_thaw (app);
+ }
+ }
+ return FALSE;
+}
+
+void
+create_main_window (GsmApplication *app)
+{
+ HdyApplicationWindow *main_window;
+ GtkStack *stack;
+ GMenuModel *window_menu_model;
+ GMenuModel *process_menu_model;
+ GdkDisplay *display;
+ GdkMonitor *monitor;
+ GdkRectangle monitor_geometry;
+
+ int width, height, xpos, ypos;
+
+ GtkBuilder *builder = gtk_builder_new();
+ gtk_builder_add_from_resource (builder, "/org/gnome/gnome-system-monitor/data/interface.ui", NULL);
+ gtk_builder_add_from_resource (builder, "/org/gnome/gnome-system-monitor/data/menus.ui", NULL);
+ gtk_builder_add_from_resource (builder, "/org/gnome/gnome-system-monitor/gtk/help-overlay.ui", NULL);
+
+ main_window = HDY_APPLICATION_WINDOW (gtk_builder_get_object (builder, "main_window"));
+ gtk_window_set_application (GTK_WINDOW (main_window), app->gobj());
+ gtk_widget_set_name (GTK_WIDGET (main_window), "gnome-system-monitor");
+ app->main_window = main_window;
+
+ gtk_application_window_set_help_overlay (GTK_APPLICATION_WINDOW (app->main_window),
+ GTK_SHORTCUTS_WINDOW (gtk_builder_get_object (builder, "help_overlay")));
+
+ g_settings_get (app->settings->gobj(), GSM_SETTING_WINDOW_STATE, "(iiii)",
+ &width, &height, &xpos, &ypos);
+
+ display = gdk_display_get_default ();
+ monitor = gdk_display_get_monitor_at_point (display, xpos, ypos);
+ if (monitor == NULL) {
+ monitor = gdk_display_get_monitor (display, 0);
+ }
+ gdk_monitor_get_geometry (monitor, &monitor_geometry);
+
+ width = CLAMP (width, 50, monitor_geometry.width);
+ height = CLAMP (height, 50, monitor_geometry.height);
+ xpos = CLAMP (xpos, 0, monitor_geometry.width - width);
+ ypos = CLAMP (ypos, 0, monitor_geometry.height - height);
+
+ gtk_window_set_default_size (GTK_WINDOW (main_window), width, height);
+ gtk_window_move (GTK_WINDOW (main_window), xpos, ypos);
+ if (app->settings->get_boolean (GSM_SETTING_MAXIMIZED))
+ gtk_window_maximize (GTK_WINDOW (main_window));
+
+ app->process_menu_button = GTK_MENU_BUTTON (gtk_builder_get_object (builder, "process_menu_button"));
+ process_menu_model = G_MENU_MODEL (gtk_builder_get_object (builder, "process-window-menu"));
+ gtk_menu_button_set_menu_model (app->process_menu_button, process_menu_model);
+
+ app->window_menu_button = GTK_MENU_BUTTON (gtk_builder_get_object (builder, "window_menu_button"));
+ window_menu_model = G_MENU_MODEL (gtk_builder_get_object (builder, "generic-window-menu"));
+ gtk_menu_button_set_menu_model (app->window_menu_button, window_menu_model);
+
+ app->end_process_button = GTK_BUTTON (gtk_builder_get_object (builder, "end_process_button"));
+
+ app->search_button = GTK_BUTTON (gtk_builder_get_object (builder, "search_button"));
+
+ GActionEntry win_action_entries[] = {
+ { "about", on_activate_about, NULL, NULL, NULL },
+ { "show-help-overlay", on_activate_keyboard_shortcuts, NULL, NULL, NULL},
+ { "search", on_activate_search, "b", "false", NULL },
+ { "send-signal-stop", on_activate_send_signal, "i", NULL, NULL },
+ { "send-signal-cont", on_activate_send_signal, "i", NULL, NULL },
+ { "send-signal-end", on_activate_send_signal, "i", NULL, NULL },
+ { "send-signal-kill", on_activate_send_signal, "i", NULL, NULL },
+ { "priority", on_activate_priority, "i", "@i 0", change_priority_state },
+ { "set-affinity", on_activate_set_affinity, NULL, NULL, NULL },
+ { "memory-maps", on_activate_memory_maps, NULL, NULL, NULL },
+ { "open-files", on_activate_open_files, NULL, NULL, NULL },
+ { "process-properties", on_activate_process_properties, NULL, NULL, NULL },
+ { "refresh", on_activate_refresh, NULL, NULL, NULL },
+ { "show-page", on_activate_radio, "s", "'resources'", change_show_page_state },
+ { "show-whose-processes", on_activate_radio, "s", "'all'", change_show_processes_state },
+ { "show-dependencies", on_activate_toggle, NULL, "false", change_show_dependencies_state }
+ };
+
+ g_action_map_add_action_entries (G_ACTION_MAP (main_window),
+ win_action_entries,
+ G_N_ELEMENTS (win_action_entries),
+ app);
+
+ GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET (main_window));
+ GdkVisual* visual = gdk_screen_get_rgba_visual(screen);
+
+ /* use visual, if available */
+ if (visual)
+ gtk_widget_set_visual(GTK_WIDGET (main_window), visual);
+
+ /* create the main stack */
+ app->stack = stack = GTK_STACK (gtk_builder_get_object (builder, "stack"));
+
+ create_proc_view(app, builder);
+
+ create_sys_view (app, builder);
+
+ create_disk_view (app, builder);
+
+ g_settings_bind (app->settings->gobj (), GSM_SETTING_CURRENT_TAB, stack, "visible-child-name", G_SETTINGS_BIND_DEFAULT);
+
+ g_signal_connect (G_OBJECT (stack), "notify::visible-child",
+ G_CALLBACK (cb_change_current_page), app);
+
+ g_signal_connect (G_OBJECT (main_window), "delete_event",
+ G_CALLBACK (cb_main_window_delete),
+ app);
+ g_signal_connect (G_OBJECT (main_window), "window-state-event",
+ G_CALLBACK (cb_main_window_state_changed),
+ app);
+
+ GAction *action;
+ action = g_action_map_lookup_action (G_ACTION_MAP (main_window),
+ "show-dependencies");
+ g_action_change_state (action,
+ g_settings_get_value (app->settings->gobj (), GSM_SETTING_SHOW_DEPENDENCIES));
+
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (main_window),
+ "show-whose-processes");
+ g_action_change_state (action,
+ g_settings_get_value (app->settings->gobj (), GSM_SETTING_SHOW_WHOSE_PROCESSES));
+
+ gtk_widget_show (GTK_WIDGET (main_window));
+
+ update_page_activities (app);
+
+ g_object_unref (G_OBJECT (builder));
+}
+
+static gboolean
+scroll_to_selection (gpointer data)
+{
+ GsmApplication *app = (GsmApplication *) data;
+ GList* paths = gtk_tree_selection_get_selected_rows (app->selection, NULL);
+ guint length = g_list_length(paths);
+ if (length > 0) {
+ GtkTreePath* last_path = (GtkTreePath*) g_list_nth_data(paths, length - 1);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (app->tree), last_path, NULL, FALSE, 0.0, 0.0);
+ }
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+ return FALSE;
+}
+
+void
+update_sensitivity(GsmApplication *app)
+{
+ const char * const selected_actions[] = { "send-signal-stop",
+ "send-signal-cont",
+ "send-signal-end",
+ "send-signal-kill",
+ "priority",
+ "set-affinity",
+ "memory-maps",
+ "open-files",
+ "process-properties" };
+
+ const char * const processes_actions[] = { "refresh",
+ "search",
+ "show-whose-processes",
+ "show-dependencies" };
+
+ size_t i;
+ gboolean processes_sensitivity, selected_sensitivity;
+ GAction *action;
+
+ processes_sensitivity = (strcmp (gtk_stack_get_visible_child_name (GTK_STACK (app->stack)), "processes") == 0);
+ selected_sensitivity = gtk_tree_selection_count_selected_rows (app->selection) > 0;
+
+ for (i = 0; i != G_N_ELEMENTS (processes_actions); ++i) {
+ action = g_action_map_lookup_action (G_ACTION_MAP (app->main_window),
+ processes_actions[i]);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+ processes_sensitivity);
+ }
+
+ for (i = 0; i != G_N_ELEMENTS (selected_actions); ++i) {
+ action = g_action_map_lookup_action (G_ACTION_MAP (app->main_window),
+ selected_actions[i]);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+ processes_sensitivity & selected_sensitivity);
+ }
+
+ gtk_revealer_set_reveal_child (GTK_REVEALER (app->proc_actionbar_revealer),
+ selected_sensitivity);
+
+ // Scrolls the table to selected row. Useful when the last row is obstructed by the revealer
+ guint duration_ms = gtk_revealer_get_transition_duration (GTK_REVEALER (app->proc_actionbar_revealer));
+ g_timeout_add (duration_ms, scroll_to_selection, app);
+}
+