gtk2/gtk/inspector/statistics.c
Matthias Clasen 83f22fcce2 Add a few missing includes
These sources are using GtkListStore apis,
but were replying on indirect includes to
get the header. Make this explicit, to prepare
for GtkEntryCompletion losing its tree view
dependencies.
2020-06-23 07:55:53 -04:00

496 lines
14 KiB
C

/*
* Copyright (c) 2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "statistics.h"
#include "graphdata.h"
#include "gtkcelllayout.h"
#include "gtkcellrenderertext.h"
#include "gtklabel.h"
#include "gtksearchbar.h"
#include "gtkstack.h"
#include "gtktogglebutton.h"
#include "gtktreeselection.h"
#include "gtktreeview.h"
#include "gtkeventcontrollerkey.h"
#include "gtkmain.h"
#include "gtkliststore.h"
#include <glib/gi18n-lib.h>
enum
{
PROP_0,
PROP_BUTTON
};
struct _GtkInspectorStatisticsPrivate
{
GtkWidget *stack;
GtkWidget *excuse;
GtkTreeModel *model;
GtkTreeView *view;
GtkWidget *button;
GHashTable *data;
GtkTreeViewColumn *column_self1;
GtkCellRenderer *renderer_self1;
GtkTreeViewColumn *column_cumulative1;
GtkCellRenderer *renderer_cumulative1;
GtkTreeViewColumn *column_self2;
GtkCellRenderer *renderer_self2;
GtkTreeViewColumn *column_cumulative2;
GtkCellRenderer *renderer_cumulative2;
GHashTable *counts;
guint update_source_id;
GtkWidget *search_entry;
GtkWidget *search_bar;
};
typedef struct {
GType type;
GtkTreeIter treeiter;
GtkGraphData *self;
GtkGraphData *cumulative;
} TypeData;
enum
{
COLUMN_TYPE,
COLUMN_TYPE_NAME,
COLUMN_SELF1,
COLUMN_CUMULATIVE1,
COLUMN_SELF2,
COLUMN_CUMULATIVE2,
COLUMN_SELF_DATA,
COLUMN_CUMULATIVE_DATA
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorStatistics, gtk_inspector_statistics, GTK_TYPE_BOX)
static gint
add_type_count (GtkInspectorStatistics *sl, GType type)
{
gint cumulative;
gint self;
GType *children;
guint n_children;
gint i;
TypeData *data;
cumulative = 0;
children = g_type_children (type, &n_children);
for (i = 0; i < n_children; i++)
cumulative += add_type_count (sl, children[i]);
data = g_hash_table_lookup (sl->priv->counts, GSIZE_TO_POINTER (type));
if (!data)
{
data = g_new0 (TypeData, 1);
data->type = type;
data->self = gtk_graph_data_new (60);
data->cumulative = gtk_graph_data_new (60);
gtk_list_store_append (GTK_LIST_STORE (sl->priv->model), &data->treeiter);
gtk_list_store_set (GTK_LIST_STORE (sl->priv->model), &data->treeiter,
COLUMN_TYPE, data->type,
COLUMN_TYPE_NAME, g_type_name (data->type),
COLUMN_SELF_DATA, data->self,
COLUMN_CUMULATIVE_DATA, data->cumulative,
-1);
g_hash_table_insert (sl->priv->counts, GSIZE_TO_POINTER (type), data);
}
self = g_type_get_instance_count (type);
cumulative += self;
gtk_graph_data_prepend_value (data->self, self);
gtk_graph_data_prepend_value (data->cumulative, cumulative);
gtk_list_store_set (GTK_LIST_STORE (sl->priv->model), &data->treeiter,
COLUMN_SELF1, (int) gtk_graph_data_get_value (data->self, 1),
COLUMN_CUMULATIVE1, (int) gtk_graph_data_get_value (data->cumulative, 1),
COLUMN_SELF2, (int) gtk_graph_data_get_value (data->self, 0),
COLUMN_CUMULATIVE2, (int) gtk_graph_data_get_value (data->cumulative, 0),
-1);
return cumulative;
}
static gboolean
update_type_counts (gpointer data)
{
GtkInspectorStatistics *sl = data;
GType type;
for (type = G_TYPE_INTERFACE; type <= G_TYPE_FUNDAMENTAL_MAX; type += (1 << G_TYPE_FUNDAMENTAL_SHIFT))
{
if (!G_TYPE_IS_INSTANTIATABLE (type))
continue;
add_type_count (sl, type);
}
return TRUE;
}
static void
toggle_record (GtkToggleButton *button,
GtkInspectorStatistics *sl)
{
if (gtk_toggle_button_get_active (button) == (sl->priv->update_source_id != 0))
return;
if (gtk_toggle_button_get_active (button))
{
sl->priv->update_source_id = g_timeout_add_seconds (1, update_type_counts, sl);
update_type_counts (sl);
}
else
{
g_source_remove (sl->priv->update_source_id);
sl->priv->update_source_id = 0;
}
}
static gboolean
has_instance_counts (void)
{
return g_type_get_instance_count (GTK_TYPE_LABEL) > 0;
}
static gboolean
instance_counts_enabled (void)
{
const gchar *string;
guint flags = 0;
string = g_getenv ("GOBJECT_DEBUG");
if (string != NULL)
{
GDebugKey debug_keys[] = {
{ "objects", 1 },
{ "instance-count", 2 },
{ "signals", 4 }
};
flags = g_parse_debug_string (string, debug_keys, G_N_ELEMENTS (debug_keys));
}
return (flags & 2) != 0;
}
static void
cell_data_data (GtkCellLayout *layout,
GtkCellRenderer *cell,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gint column;
gint count;
gchar *text;
column = GPOINTER_TO_INT (data);
gtk_tree_model_get (model, iter, column, &count, -1);
text = g_strdup_printf ("%d", count);
g_object_set (cell, "text", text, NULL);
g_free (text);
}
static void
cell_data_delta (GtkCellLayout *layout,
GtkCellRenderer *cell,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gint column;
gint count1;
gint count2;
gchar *text;
column = GPOINTER_TO_INT (data);
gtk_tree_model_get (model, iter, column - 2, &count1, column, &count2, -1);
if (count2 > count1)
text = g_strdup_printf ("%d (↗ %d)", count2, count2 - count1);
else if (count2 < count1)
text = g_strdup_printf ("%d (↘ %d)", count2, count1 - count2);
else
text = g_strdup_printf ("%d", count2);
g_object_set (cell, "text", text, NULL);
g_free (text);
}
static void
type_data_free (gpointer data)
{
TypeData *type_data = data;
g_object_unref (type_data->self);
g_object_unref (type_data->cumulative);
g_free (type_data);
}
static gboolean
key_pressed (GtkEventController *controller,
guint keyval,
guint keycode,
GdkModifierType state,
GtkInspectorStatistics *sl)
{
if (gtk_widget_get_mapped (GTK_WIDGET (sl)))
{
if (keyval == GDK_KEY_Return ||
keyval == GDK_KEY_ISO_Enter ||
keyval == GDK_KEY_KP_Enter)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
selection = gtk_tree_view_get_selection (sl->priv->view);
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
path = gtk_tree_model_get_path (model, &iter);
gtk_tree_view_row_activated (sl->priv->view, path, NULL);
gtk_tree_path_free (path);
return GDK_EVENT_STOP;
}
}
}
return GDK_EVENT_PROPAGATE;
}
static gboolean
match_string (const gchar *string,
const gchar *text)
{
gchar *lower;
gboolean match = FALSE;
if (string)
{
lower = g_ascii_strdown (string, -1);
match = g_str_has_prefix (lower, text);
g_free (lower);
}
return match;
}
static gboolean
match_row (GtkTreeModel *model,
gint column,
const gchar *key,
GtkTreeIter *iter,
gpointer data)
{
gchar *type;
gboolean match;
gtk_tree_model_get (model, iter, column, &type, -1);
match = match_string (type, key);
g_free (type);
return !match;
}
static void
destroy_controller (GtkEventController *controller)
{
gtk_widget_remove_controller (gtk_event_controller_get_widget (controller), controller);
}
static void
root (GtkWidget *widget)
{
GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (widget);
GtkEventController *controller;
GtkWidget *toplevel;
GTK_WIDGET_CLASS (gtk_inspector_statistics_parent_class)->root (widget);
toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
controller = gtk_event_controller_key_new ();
g_object_set_data_full (G_OBJECT (toplevel), "statistics-controller", controller, (GDestroyNotify)destroy_controller);
g_signal_connect (controller, "key-pressed", G_CALLBACK (key_pressed), widget);
gtk_widget_add_controller (toplevel, controller);
gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (sl->priv->search_bar), toplevel);
}
static void
unroot (GtkWidget *widget)
{
GtkWidget *toplevel;
toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
g_object_set_data (G_OBJECT (toplevel), "statistics-controller", NULL);
GTK_WIDGET_CLASS (gtk_inspector_statistics_parent_class)->unroot (widget);
}
static void
gtk_inspector_statistics_init (GtkInspectorStatistics *sl)
{
sl->priv = gtk_inspector_statistics_get_instance_private (sl);
gtk_widget_init_template (GTK_WIDGET (sl));
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_self1),
sl->priv->renderer_self1,
cell_data_data,
GINT_TO_POINTER (COLUMN_SELF1), NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_cumulative1),
sl->priv->renderer_cumulative1,
cell_data_data,
GINT_TO_POINTER (COLUMN_CUMULATIVE1), NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_self2),
sl->priv->renderer_self2,
cell_data_delta,
GINT_TO_POINTER (COLUMN_SELF2), NULL);
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (sl->priv->column_cumulative2),
sl->priv->renderer_cumulative2,
cell_data_delta,
GINT_TO_POINTER (COLUMN_CUMULATIVE2), NULL);
sl->priv->counts = g_hash_table_new_full (NULL, NULL, NULL, type_data_free);
gtk_tree_view_set_search_entry (sl->priv->view, GTK_EDITABLE (sl->priv->search_entry));
gtk_tree_view_set_search_equal_func (sl->priv->view, match_row, sl, NULL);
}
static void
constructed (GObject *object)
{
GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (object);
g_signal_connect (sl->priv->button, "toggled",
G_CALLBACK (toggle_record), sl);
if (has_instance_counts ())
update_type_counts (sl);
else
{
if (instance_counts_enabled ())
gtk_label_set_text (GTK_LABEL (sl->priv->excuse), _("GLib must be configured with -Dbuildtype=debug"));
gtk_stack_set_visible_child_name (GTK_STACK (sl->priv->stack), "excuse");
gtk_widget_set_sensitive (sl->priv->button, FALSE);
}
}
static void
finalize (GObject *object)
{
GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (object);
if (sl->priv->update_source_id)
g_source_remove (sl->priv->update_source_id);
g_hash_table_unref (sl->priv->counts);
G_OBJECT_CLASS (gtk_inspector_statistics_parent_class)->finalize (object);
}
static void
get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (object);
switch (param_id)
{
case PROP_BUTTON:
g_value_take_object (value, sl->priv->button);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
GtkInspectorStatistics *sl = GTK_INSPECTOR_STATISTICS (object);
switch (param_id)
{
case PROP_BUTTON:
sl->priv->button = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
gtk_inspector_statistics_class_init (GtkInspectorStatisticsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->finalize = finalize;
widget_class->root = root;
widget_class->unroot = unroot;
g_object_class_install_property (object_class, PROP_BUTTON,
g_param_spec_object ("button", NULL, NULL,
GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/statistics.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, view);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, stack);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, model);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_self1);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_self1);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_cumulative1);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_cumulative1);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_self2);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_self2);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, column_cumulative2);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, renderer_cumulative2);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, search_entry);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, search_bar);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorStatistics, excuse);
}
// vim: set et sw=2 ts=2: