From 80feabede2c920b50c1885d3be781c0944e88d0d Mon Sep 17 00:00:00 2001 From: Peter de Ridder Date: Thu, 13 Mar 2014 23:25:14 +0100 Subject: [PATCH 1/3] Added tree model process view. This model builds a tree model out of a list model. --- src/Makefile.am | 1 + src/process-tree-model.c | 750 +++++++++++++++++++++++++++++++++++++++ src/process-tree-model.h | 31 ++ src/process-tree-view.c | 18 +- src/process-tree-view.h | 1 + src/process-window.c | 4 +- 6 files changed, 802 insertions(+), 3 deletions(-) create mode 100644 src/process-tree-model.c create mode 100644 src/process-tree-model.h diff --git a/src/Makefile.am b/src/Makefile.am index 05ecebe..16ac8ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ xfce4_taskmanager_SOURCES = \ process-window_ui.h \ process-window.c process-window.h \ process-monitor.c process-monitor.h \ + process-tree-model.c process-tree-model.h \ process-tree-view.c process-tree-view.h \ process-statusbar.c process-statusbar.h \ exec-tool-button.c exec-tool-button.h \ diff --git a/src/process-tree-model.c b/src/process-tree-model.c new file mode 100644 index 0000000..afd5ab1 --- /dev/null +++ b/src/process-tree-model.c @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2014 Peter de Ridder, + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "process-tree-view.h" +#include "process-tree-model.h" + + + +enum { + PROP_0, + PROP_MODEL +}; + +typedef struct { + GtkTreeIter iter; + GtkTreePath * path; + GSequenceIter * list; + GNode * tree; +} XtmCrossLink; + +typedef struct _XtmProcessTreeModelClass XtmProcessTreeModelClass; +struct _XtmProcessTreeModelClass +{ + GObjectClass parent_class; +}; +struct _XtmProcessTreeModel +{ + GObject parent; + /**/ + GtkTreeModel * model; + GNode * tree; + GSequence * list; + gint c_column; + gint p_column; + gint stamp; +}; + +static void xtm_process_tree_model_iface_init (GtkTreeModelIface *iface); + +G_DEFINE_TYPE_WITH_CODE (XtmProcessTreeModel, xtm_process_tree_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, xtm_process_tree_model_iface_init)) + +static void xtm_process_tree_model_finalize (GObject *object); +static void xtm_process_tree_model_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); +static void xtm_process_tree_model_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); + +static GtkTreeModelFlags xtm_process_tree_model_get_flags (GtkTreeModel *model); +static gint xtm_process_tree_model_get_n_columns (GtkTreeModel *model); +static GType xtm_process_tree_model_get_column_type (GtkTreeModel *model, gint index); +static gboolean xtm_process_tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path); +static GtkTreePath * xtm_process_tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter); +static void xtm_process_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, gint column, GValue *value); +static gboolean xtm_process_tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter); +static gboolean xtm_process_tree_model_iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent); +static gboolean xtm_process_tree_model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter); +static gint xtm_process_tree_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter); +static gboolean xtm_process_tree_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent, gint n); +static gboolean xtm_process_tree_model_iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child); + +static void xtm_process_tree_model_row_changed (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model); +static void xtm_process_tree_model_row_inserted (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model); +//static void xtm_process_tree_model_row_has_child_toggled (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model); +static void xtm_process_tree_model_row_deleted (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeModel *model); +static void xtm_process_tree_model_rows_reordered (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, gint *new_order, GtkTreeModel *model); + +static void xtm_process_tree_model_set_model (XtmProcessTreeModel *treemodel, GtkTreeModel *model); + + + +static XtmCrossLink * +xtm_cross_link_new (void) +{ + return g_new0 (XtmCrossLink, 1); +} + + + +static void +xtm_process_tree_model_class_init (XtmProcessTreeModelClass *klass) +{ + GObjectClass *class = G_OBJECT_CLASS (klass); + xtm_process_tree_model_parent_class = g_type_class_peek_parent (klass); + class->finalize = xtm_process_tree_model_finalize; + class->set_property = xtm_process_tree_model_set_property; + class->get_property = xtm_process_tree_model_get_property; + + g_object_class_install_property (class, PROP_MODEL, + g_param_spec_object ("model", NULL, NULL, GTK_TYPE_TREE_MODEL, G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY)); +} + +static void +xtm_process_tree_model_iface_init (GtkTreeModelIface *iface) +{ + iface->row_changed; + iface->row_inserted; + iface->row_has_child_toggled; + iface->row_deleted; + iface->rows_reordered; + + iface->get_flags = xtm_process_tree_model_get_flags; + iface->get_n_columns = xtm_process_tree_model_get_n_columns; + iface->get_column_type = xtm_process_tree_model_get_column_type; + iface->get_iter = xtm_process_tree_model_get_iter; + iface->get_path = xtm_process_tree_model_get_path; + iface->get_value = xtm_process_tree_model_get_value; + iface->iter_next = xtm_process_tree_model_iter_next; + iface->iter_children = xtm_process_tree_model_iter_children; + iface->iter_has_child = xtm_process_tree_model_iter_has_child; + iface->iter_n_children = xtm_process_tree_model_iter_n_children; + iface->iter_nth_child = xtm_process_tree_model_iter_nth_child; + iface->iter_parent = xtm_process_tree_model_iter_parent; + iface->ref_node; + iface->unref_node; +} + +static void +xtm_process_tree_model_init (XtmProcessTreeModel *treemodel) +{ + treemodel->list = g_sequence_new (g_free); + treemodel->tree = g_node_new (NULL); + + treemodel->c_column = XTM_PTV_COLUMN_PID; + treemodel->p_column = XTM_PTV_COLUMN_PPID; + + treemodel->stamp = g_random_int (); + if (treemodel->stamp == 0) + treemodel->stamp = 1; +} + +static void +xtm_process_tree_model_finalize (GObject *object) +{ + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (object); + + g_node_destroy (treemodel->tree); + g_sequence_free (treemodel->list); + + G_OBJECT_CLASS (xtm_process_tree_model_parent_class)->finalize (object); +} + +static void +xtm_process_tree_model_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (object); + switch (property_id) + { + case PROP_MODEL: + xtm_process_tree_model_set_model (treemodel, g_value_get_object (value)); + break; + } +} + +static void +xtm_process_tree_model_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (object); + switch (property_id) + { + case PROP_MODEL: + g_value_set_object (value, treemodel->model); + break; + } +} + +static GtkTreeModelFlags +xtm_process_tree_model_get_flags (GtkTreeModel *model) +{ + return GTK_TREE_MODEL_ITERS_PERSIST; +} + +static gint +xtm_process_tree_model_get_n_columns (GtkTreeModel *model) +{ + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + model = treemodel->model; + g_return_val_if_fail (model, 0); + return gtk_tree_model_get_n_columns (model); +} + +static GType +xtm_process_tree_model_get_column_type (GtkTreeModel *model, gint index) +{ + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + model = treemodel->model; + g_return_val_if_fail (model, 0); + return gtk_tree_model_get_column_type (model, index); +} + +static gboolean +xtm_process_tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) +{ + GNode *node; + gint *indices; + gint depth, i; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + node = treemodel->tree; + indices = gtk_tree_path_get_indices (path); + depth = gtk_tree_path_get_depth (path); + g_return_val_if_fail (depth > 0, FALSE); + for (i = 0; i < depth; i++) + { + node = g_node_nth_child (node, indices[i]); + if (node == NULL) + break; + } + iter->stamp = node ? treemodel->stamp : 0; + iter->user_data = node; + return node != NULL; +} + +static GtkTreePath * +xtm_process_tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreePath *path; + GNode *child, *parent; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (iter->stamp == treemodel->stamp, NULL); + child = iter->user_data; + parent = child->parent; + path = gtk_tree_path_new (); + while (parent) + { + gtk_tree_path_prepend_index (path, g_node_child_position (parent, child)); + child = parent; + parent = child->parent; + } + return path; +} + +static void +xtm_process_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, gint column, GValue *value) +{ + XtmCrossLink *link; + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_if_fail (iter->stamp == treemodel->stamp); + model = treemodel->model; + g_return_if_fail (model); + node = iter->user_data; + link = node->data; + iter = &link->iter; + if (link->path) + gtk_tree_model_get_iter (model, iter, link->path); + return gtk_tree_model_get_value (model, iter, column, value); +} + +static gboolean +xtm_process_tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter) +{ + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (iter->stamp == treemodel->stamp, FALSE); + node = iter->user_data; + iter->user_data = g_node_next_sibling (node); + if (iter->user_data == NULL) + iter->stamp = 0; + return iter->user_data != NULL; +} + +static gboolean +xtm_process_tree_model_iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent) +{ + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (parent == NULL || parent->stamp == treemodel->stamp, FALSE); + if (parent == NULL) + node = treemodel->tree; + else + node = parent->user_data; + iter->user_data = g_node_first_child (node); + iter->stamp = iter->user_data ? treemodel->stamp : 0; + return iter->user_data != NULL; +} + +static gboolean +xtm_process_tree_model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter) +{ + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (iter->stamp == treemodel->stamp, FALSE); + node = iter->user_data; + return g_node_first_child (node) != NULL; +} + +static gint +xtm_process_tree_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) +{ + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (iter == NULL || iter->stamp == treemodel->stamp, 0); + if (iter == NULL) + node = treemodel->tree; + else + node = iter->user_data; + return g_node_n_children (node); +} + +static gboolean +xtm_process_tree_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent, gint n) +{ + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (parent == NULL || parent->stamp == treemodel->stamp, FALSE); + if (parent == NULL) + node = treemodel->tree; + else + node = parent->user_data; + iter->user_data = g_node_nth_child (node, n); + iter->stamp = iter->user_data ? treemodel->stamp : 0; + return iter->user_data != NULL; +} + +static gboolean +xtm_process_tree_model_iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child) +{ + GNode *node; + XtmProcessTreeModel *treemodel = XTM_PROCESS_TREE_MODEL (model); + g_return_val_if_fail (child->stamp == treemodel->stamp, FALSE); + node = child->user_data; + node = node->parent; + if (node == treemodel->tree) + node = NULL; + iter->stamp = node ? treemodel->stamp : 0; + iter->user_data = node; + return node != NULL; +} + +struct find_node_struct +{ + GValue p_value; + XtmProcessTreeModel * treemodel; + GNode * parent; +}; + +static gboolean +find_node (GNode *node, gpointer data) +{ + XtmCrossLink *link = node->data; + struct find_node_struct *found = data; + gboolean same = FALSE; + GValue c_value = {0}; + if (link == NULL) + return FALSE; + if (link->path) + gtk_tree_model_get_iter (found->treemodel->model, &link->iter, link->path); + gtk_tree_model_get_value (found->treemodel->model, &link->iter, found->treemodel->c_column, &c_value); + if (g_value_get_uint (&c_value) > 0) /* PID of 0 doesn't exist */ + same = g_value_get_uint (&found->p_value) == g_value_get_uint (&c_value); + g_value_unset (&c_value); + if (same) + found->parent = node; + return same; +} + +static gboolean +signal_insert (GNode *node, gpointer data) +{ + XtmProcessTreeModel *treemodel = data; + GtkTreePath *s_path; + GtkTreeIter s_iter; + + s_iter.stamp = treemodel->stamp; + s_iter.user_data = node; + s_iter.user_data2 = NULL; + s_iter.user_data3 = NULL; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + + return FALSE; +} + +static void +xtm_process_tree_model_row_changed (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model) +{ + GtkTreePath *s_path; + GtkTreeIter s_iter; + XtmCrossLink *link, *c_link; + GNode *node, *next_node, *old_parent; + struct find_node_struct found = {{0}}; + GValue c_value = {0}; + GValue p_value = {0}; + gboolean same = TRUE; + gboolean signal_parent; + + g_return_if_fail (gtk_tree_path_get_depth (path) == 1); + + /* Get the changed item */ + link = g_sequence_get ( + g_sequence_get_iter_at_pos (treemodel->list, *gtk_tree_path_get_indices (path))); + + g_return_if_fail (link != NULL); + + s_iter.stamp = treemodel->stamp; + s_iter.user_data2 = NULL; + s_iter.user_data3 = NULL; + + found.parent = treemodel->tree; + found.treemodel = treemodel; + gtk_tree_model_get_value (model, iter, treemodel->p_column, &found.p_value); + old_parent = link->tree->parent; + if (!find_node (old_parent, &found)) + { + /* Parent has changed? */ + g_node_traverse (treemodel->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, find_node, &found); + if (found.parent != old_parent) + { + s_iter.user_data = link->tree; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + + g_node_unlink (link->tree); + + /* Signal the delete */ + gtk_tree_model_row_deleted (GTK_TREE_MODEL (treemodel), s_path); + gtk_tree_path_free (s_path); + + if (old_parent != treemodel->tree && g_node_first_child (old_parent) == NULL) + { + s_iter.user_data = old_parent; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + } + + signal_parent = g_node_first_child (found.parent) == NULL; + g_node_append (found.parent, link->tree); + + /* Signal the insert */ + g_node_traverse (link->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + signal_insert, treemodel); + + if (signal_parent) + { + s_iter.user_data = found.parent; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + } + + same = FALSE; + } + } + g_value_unset (&found.p_value); + + if (same) + { + /* Signal the change */ + s_iter.user_data = link->tree; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + } + + /* Maybe this just became a parent? */ + gtk_tree_model_get_value (model, iter, treemodel->c_column, &c_value); + if (g_value_get_uint (&c_value) > 0) /* PID of 0 doesn't exist */ + { + signal_parent = FALSE; + + for (node = g_node_first_child (treemodel->tree); node; node = next_node) + { + next_node = g_node_next_sibling (node); + + if (node == link->tree) + continue; + + c_link = node->data; + if (c_link->path) + gtk_tree_model_get_iter (model, &c_link->iter, c_link->path); + gtk_tree_model_get_value (model, &c_link->iter, treemodel->p_column, &p_value); + same = g_value_get_uint (&p_value) == g_value_get_uint (&c_value); + g_value_unset (&p_value); + if (same) + { + s_iter.user_data = node; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + + g_node_unlink (node); + + /* Signal the delete */ + gtk_tree_model_row_deleted (GTK_TREE_MODEL (treemodel), s_path); + gtk_tree_path_free (s_path); + + signal_parent = g_node_first_child (found.parent) == NULL; + g_node_append (link->tree, node); + + /* Signal the insert */ + g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + signal_insert, treemodel); + } + } + + if (signal_parent) + { + s_iter.user_data = link->tree; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + } + } + g_value_unset (&c_value); +} + +static void +do_path (gpointer data, gpointer user_data) +{ + XtmCrossLink *link = data; + void (*func) (GtkTreePath*) = user_data; + g_return_if_fail (link->path); + func (link->path); +} + +static void +xtm_process_tree_model_row_inserted (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model) +{ + GtkTreePath *s_path; + GtkTreeIter s_iter; + XtmCrossLink *link, *c_link; + GNode *node, *next_node; + struct find_node_struct found = {{0}}; + GValue c_value = {0}; + GValue p_value = {0}; + gboolean same; + gboolean not_persist = TRUE; + gboolean signal_parent; + + g_return_if_fail (gtk_tree_path_get_depth (path) == 1); + + not_persist = ! (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST); + + s_iter.stamp = treemodel->stamp; + s_iter.user_data2 = NULL; + s_iter.user_data3 = NULL; + + /* Insert the new item */ + link = xtm_cross_link_new (); + link->iter = *iter; + if (not_persist) + link->path = gtk_tree_path_copy (path); + link->list = g_sequence_insert_before ( + g_sequence_get_iter_at_pos (treemodel->list, *gtk_tree_path_get_indices (path)), + link); + found.parent = treemodel->tree; + found.treemodel = treemodel; + gtk_tree_model_get_value (model, iter, treemodel->p_column, &found.p_value); + g_node_traverse (treemodel->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, find_node, &found); + g_value_unset (&found.p_value); + link->tree = g_node_append_data (found.parent, link); + + if (not_persist) + g_sequence_foreach_range (g_sequence_iter_next (link->list), g_sequence_get_end_iter (treemodel->list), + do_path, gtk_tree_path_next); + + /* Signal the new item */ + s_iter.user_data = link->tree; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + + /* Maybe this just became a parent? */ + gtk_tree_model_get_value (model, iter, treemodel->c_column, &c_value); + if (g_value_get_uint (&c_value) > 0) /* PID of 0 doesn't exist */ + { + signal_parent = FALSE; + + for (node = g_node_first_child (treemodel->tree); node; node = next_node) + { + next_node = g_node_next_sibling (node); + + if (node == link->tree) + continue; + + c_link = node->data; + if (c_link->path) + gtk_tree_model_get_iter (model, &c_link->iter, c_link->path); + gtk_tree_model_get_value (model, &c_link->iter, treemodel->p_column, &p_value); + same = g_value_get_uint (&p_value) == g_value_get_uint (&c_value); + g_value_unset (&p_value); + if (same) + { + s_iter.user_data = node; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + + g_node_unlink (node); + + /* Signal the delete */ + gtk_tree_model_row_deleted (GTK_TREE_MODEL (treemodel), s_path); + gtk_tree_path_free (s_path); + + signal_parent = TRUE; + g_node_append (link->tree, node); + + /* Signal the insert */ + g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + signal_insert, treemodel); + } + } + + if (signal_parent) + { + s_iter.user_data = link->tree; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + } + } + g_value_unset (&c_value); +} + +static void +xtm_process_tree_model_row_deleted (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeModel *model) +{ + GtkTreePath *s_path; + GtkTreeIter s_iter; + XtmCrossLink *link; + GNode *del_node, *node, *old_parent; + gboolean not_persist = TRUE; + + g_return_if_fail (gtk_tree_path_get_depth (path) == 1); + + not_persist = ! (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST); + + s_iter.stamp = treemodel->stamp; + s_iter.user_data2 = NULL; + s_iter.user_data3 = NULL; + + /* Get the deleted item */ + link = g_sequence_get ( + g_sequence_get_iter_at_pos (treemodel->list, *gtk_tree_path_get_indices (path))); + + g_return_if_fail (link != NULL); + + s_iter.user_data = link->tree; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + + if (not_persist) + g_sequence_foreach_range (g_sequence_iter_next (link->list), g_sequence_get_end_iter (treemodel->list), + do_path, gtk_tree_path_prev); + + del_node = link->tree; + old_parent = del_node->parent; + g_node_unlink (del_node); + del_node->data = NULL; + g_sequence_remove (link->list); + + /* Signal the delete */ + gtk_tree_model_row_deleted (GTK_TREE_MODEL (treemodel), s_path); + gtk_tree_path_free (s_path); + + if (old_parent != treemodel->tree && g_node_first_child (old_parent) == NULL) + { + s_iter.user_data = old_parent; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (treemodel), s_path, &s_iter); + gtk_tree_path_free (s_path); + } + + /* Signal the insert */ + while ((node = g_node_first_child (del_node))) + { + g_node_unlink (node); + g_node_append (treemodel->tree, node); + g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + signal_insert, treemodel); + } + + g_node_destroy (del_node); +} + +static void +xtm_process_tree_model_rows_reordered (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, gint *new_order, GtkTreeModel *model) +{ + gint i, size; + GSequence *s_list; + GSequenceIter *s_iter; + GSequenceIter *swap1, *swap2; + XtmCrossLink *link; + gboolean not_persist = TRUE; + + size = g_sequence_get_length (treemodel->list); + + if (G_UNLIKELY (size == 0)) + return; + + not_persist = ! (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST); + + /* New list to hold the new order */ + s_list = g_sequence_new (g_free); + s_iter = g_sequence_get_end_iter (s_list); + + for (i = 0; i < size; i++) + { + /* make a dummy to hold the space */ + swap1 = g_sequence_insert_before (s_iter, NULL); + swap2 = g_sequence_get_iter_at_pos (treemodel->list, new_order[i]); + g_sequence_swap (swap1, swap2); + + if (not_persist) + { + link = g_sequence_get (swap2); + g_warn_if_fail (link->path); + gtk_tree_path_up (link->path); + gtk_tree_path_append_index (link->path, i); + } + } + + g_sequence_free (treemodel->list); + treemodel->list = s_list; +} + +static void +xtm_process_tree_model_set_model (XtmProcessTreeModel *treemodel, GtkTreeModel *model) +{ + g_return_if_fail (GTK_IS_TREE_MODEL (model)); + + treemodel->model = model; + + g_signal_connect_object (model, "row-changed", G_CALLBACK (xtm_process_tree_model_row_changed), treemodel, G_CONNECT_SWAPPED); + g_signal_connect_object (model, "row-inserted", G_CALLBACK (xtm_process_tree_model_row_inserted), treemodel, G_CONNECT_SWAPPED); + //g_signal_connect_object (model, "row-has-child-toggled", G_CALLBACK (xtm_process_tree_model_row_has_child_toggled), treemodel, G_CONNECT_SWAPPED); + g_signal_connect_object (model, "row-deleted", G_CALLBACK (xtm_process_tree_model_row_deleted), treemodel, G_CONNECT_SWAPPED); + g_signal_connect_object (model, "rows-reordered", G_CALLBACK (xtm_process_tree_model_rows_reordered), treemodel, G_CONNECT_SWAPPED); +} + + + +GtkTreeModel * +xtm_process_tree_model_new (GtkTreeModel * model) +{ + /* We only support persistant iters, that way we can store them in the tree. */ + g_return_val_if_fail (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY, NULL); + + return g_object_new (XTM_TYPE_PROCESS_TREE_MODEL, "model", model, NULL); +} + diff --git a/src/process-tree-model.h b/src/process-tree-model.h new file mode 100644 index 0000000..84dd9d1 --- /dev/null +++ b/src/process-tree-model.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 Peter de Ridder, + * + * 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. + */ + +#ifndef PROCESS_TREE_MODEL_H +#define PROCESS_TREE_MODEL_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#define XTM_TYPE_PROCESS_TREE_MODEL (xtm_process_tree_model_get_type ()) +#define XTM_PROCESS_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XTM_TYPE_PROCESS_TREE_MODEL, XtmProcessTreeModel)) +#define XTM_PROCESS_TREE_MODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XTM_TYPE_PROCESS_TREE_MODEL, XtmProcessTreeModelClass)) +#define XTM_IS_PROCESS_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XTM_TYPE_PROCESS_TREE_MODEL)) +#define XTM_IS_PROCESS_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XTM_TYPE_PROCESS_TREE_MODEL)) +#define XTM_PROCESS_TREE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XTM_TYPE_PROCESS_TREE_MODEL, XtmProcessTreeModelClass)) + +typedef struct _XtmProcessTreeModel XtmProcessTreeModel; + +GType xtm_process_tree_model_get_type (void); +GtkTreeModel * xtm_process_tree_model_new (GtkTreeModel * model); + +#endif /* !PROCESS_TREE_MODEL_H */ diff --git a/src/process-tree-view.c b/src/process-tree-view.c index 3c35a61..298e17d 100644 --- a/src/process-tree-view.c +++ b/src/process-tree-view.c @@ -16,6 +16,7 @@ #include #include +#include "process-tree-model.h" #include "process-tree-view.h" #include "task-manager.h" #include "settings.h" @@ -49,6 +50,7 @@ struct _XtmProcessTreeView GtkListStore * model; GtkTreeModel * model_filter; gchar * cmd_filter; + GtkTreeModel * model_tree; GtkTreeViewColumn * sort_column; gushort columns_positions[N_COLUMNS]; XtmSettings * settings; @@ -107,7 +109,9 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview) treeview->model_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (treeview->model), NULL); gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (treeview->model_filter), (GtkTreeModelFilterVisibleFunc)visible_func, treeview, NULL); - g_object_set (treeview, "search-column", XTM_PTV_COLUMN_COMMAND, "model", treeview->model_filter, NULL); + treeview->model_tree = xtm_process_tree_model_new (GTK_TREE_MODEL (treeview->model_filter)); + + g_object_set (treeview, "search-column", XTM_PTV_COLUMN_COMMAND, "model", treeview->model_tree, NULL); treeview->cmd_filter = NULL; @@ -235,6 +239,12 @@ xtm_process_tree_view_finalize (GObject *object) treeview->model_filter = NULL; } + if (GTK_IS_TREE_MODEL (treeview->model_tree)) + { + g_object_unref (treeview->model_tree); + treeview->model_tree = NULL; + } + if (XTM_IS_SETTINGS (treeview->settings)) { g_object_unref (treeview->settings); @@ -708,3 +718,9 @@ xtm_process_tree_view_get_sort_column_id (XtmProcessTreeView *treeview, gint *so *sort_type = gtk_tree_view_column_get_sort_order (treeview->sort_column); } +GtkTreeModel * +xtm_process_tree_view_get_model (XtmProcessTreeView *treeview) +{ + return GTK_TREE_MODEL (treeview->model); +} + diff --git a/src/process-tree-view.h b/src/process-tree-view.h index f9480b8..3f1b5dc 100644 --- a/src/process-tree-view.h +++ b/src/process-tree-view.h @@ -53,5 +53,6 @@ GType xtm_process_tree_view_get_type (void); GtkWidget * xtm_process_tree_view_new (void); void xtm_process_tree_view_get_sort_column_id (XtmProcessTreeView *treeview, gint *sort_column_id, GtkSortType *sort_type); void xtm_process_tree_view_set_filter (XtmProcessTreeView *treeview, const gchar *cmd_filter); +GtkTreeModel * xtm_process_tree_view_get_model (XtmProcessTreeView *treeview); #endif /* !PROCESS_TREE_VIEW_H */ diff --git a/src/process-window.c b/src/process-window.c index 30002d9..b716bdf 100644 --- a/src/process-window.c +++ b/src/process-window.c @@ -403,8 +403,8 @@ GtkTreeModel * xtm_process_window_get_model (XtmProcessWindow *window) { g_return_val_if_fail (XTM_IS_PROCESS_WINDOW (window), NULL); - g_return_val_if_fail (GTK_IS_TREE_VIEW (window->treeview), NULL); - return gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (window->treeview)))); + g_return_val_if_fail (XTM_IS_PROCESS_TREE_VIEW (window->treeview), NULL); + return xtm_process_tree_view_get_model (XTM_PROCESS_TREE_VIEW (window->treeview)); } void From 2a818178687e7c18435ef7ccff21ca2127bfaff7 Mon Sep 17 00:00:00 2001 From: Peter de Ridder Date: Fri, 14 Mar 2014 22:55:58 +0100 Subject: [PATCH 2/3] Sort the tree according to the list model. --- src/process-tree-model.c | 86 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/src/process-tree-model.c b/src/process-tree-model.c index afd5ab1..288f465 100644 --- a/src/process-tree-model.c +++ b/src/process-tree-model.c @@ -385,6 +385,23 @@ signal_insert (GNode *node, gpointer data) return FALSE; } +static GNode * +find_sibling (GNode *parent, GSequenceIter *child) +{ + GSequenceIter *prev; + for (prev = g_sequence_iter_prev (child); prev != child; prev = g_sequence_iter_prev (child)) + { + XtmCrossLink *link; + child = prev; + link = g_sequence_get (child); + if (link->tree->parent == parent) + { + return link->tree; + } + } + return NULL; +} + static void xtm_process_tree_model_row_changed (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, GtkTreeModel *model) { @@ -438,7 +455,7 @@ xtm_process_tree_model_row_changed (XtmProcessTreeModel *treemodel, GtkTreePath } signal_parent = g_node_first_child (found.parent) == NULL; - g_node_append (found.parent, link->tree); + g_node_insert_after (found.parent, find_sibling (found.parent, link->list), link->tree); /* Signal the insert */ g_node_traverse (link->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, @@ -560,7 +577,7 @@ xtm_process_tree_model_row_inserted (XtmProcessTreeModel *treemodel, GtkTreePath gtk_tree_model_get_value (model, iter, treemodel->p_column, &found.p_value); g_node_traverse (treemodel->tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, find_node, &found); g_value_unset (&found.p_value); - link->tree = g_node_append_data (found.parent, link); + link->tree = g_node_insert_data_after (found.parent, find_sibling (found.parent, link->list), link); if (not_persist) g_sequence_foreach_range (g_sequence_iter_next (link->list), g_sequence_get_end_iter (treemodel->list), @@ -673,8 +690,9 @@ xtm_process_tree_model_row_deleted (XtmProcessTreeModel *treemodel, GtkTreePath /* Signal the insert */ while ((node = g_node_first_child (del_node))) { + XtmCrossLink *i_link = node->data; g_node_unlink (node); - g_node_append (treemodel->tree, node); + g_node_insert_after (treemodel->tree, find_sibling (treemodel->tree, i_link->list), node); g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, signal_insert, treemodel); } @@ -682,6 +700,63 @@ xtm_process_tree_model_row_deleted (XtmProcessTreeModel *treemodel, GtkTreePath g_node_destroy (del_node); } +static gboolean +reorder_children (GNode *parent, gpointer data) +{ + XtmProcessTreeModel *treemodel = data; + GSequenceIter *iter; + GNode *child = NULL; + XtmCrossLink *link; + GtkTreeIter s_iter; + GtkTreePath *s_path; + guint size, i, x; + gint *new_order; + gint c_pos, old_pos; + gboolean moved = FALSE; + size = g_node_n_children (parent); + if (size < 2) + return FALSE; + new_order = g_new (gint, size); + for (i = 0; i < size; i++) + { + new_order[i] = i; + } + i = 0; + for (iter = g_sequence_get_begin_iter (treemodel->list); !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) + { + link = g_sequence_get (iter); + if (link->tree->parent == parent) + { + c_pos = g_node_child_position (parent, link->tree); + g_node_unlink (link->tree); + g_node_insert_after (parent, child, link->tree); + child = link->tree; + old_pos = new_order[c_pos]; + c_pos -= i; + if (c_pos > 0) + { + memmove (new_order + i + 1, new_order + i, c_pos * sizeof(gint)); + moved = TRUE; + } + new_order[i++] = old_pos; + } + } + g_return_val_if_fail (size == i, TRUE); + if (moved) + { + /* Signal the new item */ + s_iter.stamp = treemodel->stamp; + s_iter.user_data = parent; + s_iter.user_data2 = NULL; + s_iter.user_data3 = NULL; + s_path = xtm_process_tree_model_get_path (GTK_TREE_MODEL (treemodel), &s_iter); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (treemodel), s_path, &s_iter, new_order); + gtk_tree_path_free (s_path); + } + g_free (new_order); + return FALSE; +} + static void xtm_process_tree_model_rows_reordered (XtmProcessTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, gint *new_order, GtkTreeModel *model) { @@ -695,7 +770,7 @@ xtm_process_tree_model_rows_reordered (XtmProcessTreeModel *treemodel, GtkTreePa size = g_sequence_get_length (treemodel->list); if (G_UNLIKELY (size == 0)) - return; + return; not_persist = ! (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST); @@ -721,6 +796,9 @@ xtm_process_tree_model_rows_reordered (XtmProcessTreeModel *treemodel, GtkTreePa g_sequence_free (treemodel->list); treemodel->list = s_list; + + g_node_traverse (treemodel->tree, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, + reorder_children, treemodel); } static void From dbd10ae65593664809d66d8675480a26a2ce1033 Mon Sep 17 00:00:00 2001 From: Peter de Ridder Date: Sat, 15 Mar 2014 00:00:30 +0100 Subject: [PATCH 3/3] Added the process tree to the settings. --- src/process-tree-view.c | 11 +++++++++-- src/settings-dialog.c | 1 + src/settings-dialog.ui | 16 +++++++++++++++- src/settings.c | 3 +++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/process-tree-view.c b/src/process-tree-view.c index 298e17d..53c8a33 100644 --- a/src/process-tree-view.c +++ b/src/process-tree-view.c @@ -86,6 +86,7 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview) GtkCellRenderer *cell_text, *cell_right_aligned; GtkTreeViewColumn *column; gboolean visible; + gboolean tree; treeview->settings = xtm_settings_get_default (); g_signal_connect (treeview->settings, "notify", G_CALLBACK (settings_changed), treeview); @@ -94,7 +95,7 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview) gchar *uid_name; get_owner_uid (&treeview->owner_uid, &uid_name); g_free (uid_name); - g_object_get (treeview->settings, "show-all-processes", &treeview->show_all_processes_cached, NULL); + g_object_get (treeview->settings, "show-all-processes", &treeview->show_all_processes_cached, "process-tree", &tree, NULL); } /* Create tree view model */ @@ -111,7 +112,7 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview) treeview->model_tree = xtm_process_tree_model_new (GTK_TREE_MODEL (treeview->model_filter)); - g_object_set (treeview, "search-column", XTM_PTV_COLUMN_COMMAND, "model", treeview->model_tree, NULL); + g_object_set (treeview, "search-column", XTM_PTV_COLUMN_COMMAND, "model", tree ? treeview->model_tree : treeview->model_filter, NULL); treeview->cmd_filter = NULL; @@ -701,6 +702,12 @@ settings_changed (GObject *object, GParamSpec *pspec, XtmProcessTreeView *treevi g_object_get (object, pspec->name, &treeview->show_all_processes_cached, NULL); gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (treeview->model_filter)); } + else if (!g_strcmp0 (pspec->name, "process-tree")) + { + gboolean tree; + g_object_get (object, pspec->name, &tree, NULL); + gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), tree ? treeview->model_tree : treeview->model_filter); + } } diff --git a/src/settings-dialog.c b/src/settings-dialog.c index b191430..5b5b9d4 100644 --- a/src/settings-dialog.c +++ b/src/settings-dialog.c @@ -108,6 +108,7 @@ xtm_settings_dialog_init (XtmSettingsDialog *dialog) builder_bind_toggle_button (builder, "button-monitor-paint-box", dialog->settings, "monitor-paint-box"); builder_bind_toggle_button (builder, "button-prompt-terminate-task", dialog->settings, "prompt-terminate-task"); builder_bind_toggle_button (builder, "button-show-status-icon", dialog->settings, "show-status-icon"); + builder_bind_toggle_button (builder, "button-process-tree", dialog->settings, "process-tree"); { guint n; diff --git a/src/settings-dialog.ui b/src/settings-dialog.ui index 4c84edc..64f10b8 100644 --- a/src/settings-dialog.ui +++ b/src/settings-dialog.ui @@ -100,6 +100,20 @@ 3 + + + Show processes as tree + True + True + False + True + + + False + False + 4 + + True @@ -122,7 +136,7 @@ False False - 4 + 5 diff --git a/src/settings.c b/src/settings.c index 8ad5837..8d5e550 100644 --- a/src/settings.c +++ b/src/settings.c @@ -54,6 +54,7 @@ enum PROP_SORT_TYPE, PROP_WINDOW_WIDTH, PROP_WINDOW_HEIGHT, + PROP_PROCESS_TREE, N_PROPS, }; typedef struct _XtmSettingsClass XtmSettingsClass; @@ -129,6 +130,8 @@ xtm_settings_class_init (XtmSettingsClass *klass) g_param_spec_int ("window-width", "WindowWidth", "Window width", -1, G_MAXINT, -1, G_PARAM_READWRITE)); g_object_class_install_property (class, PROP_WINDOW_HEIGHT, g_param_spec_int ("window-height", "WindowHeight", "Window height", -1, G_MAXINT, -1, G_PARAM_READWRITE)); + g_object_class_install_property (class, PROP_PROCESS_TREE, + g_param_spec_boolean ("process-tree", "ProcessTreeView", "Process tree", FALSE, G_PARAM_READWRITE)); } static void