/* * Copyright (c) 2010 Mike Massonnet, * * 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 #include "process-tree-model.h" #include "process-tree-view.h" #include "task-manager.h" #include "settings.h" /* Tree view columns */ enum { COLUMN_COMMAND = 0, COLUMN_PID, COLUMN_PPID, COLUMN_STATE, COLUMN_VSZ, COLUMN_RSS, COLUMN_UID, COLUMN_CPU, COLUMN_PRIORITY, N_COLUMNS, }; typedef struct _XtmProcessTreeViewClass XtmProcessTreeViewClass; struct _XtmProcessTreeViewClass { GtkTreeViewClass parent_class; }; struct _XtmProcessTreeView { GtkTreeView parent; /**/ GtkListStore * model; GtkTreeModel * model_filter; gchar * cmd_filter; GtkTreeModel * model_tree; GtkTreeViewColumn * sort_column; gushort columns_positions[N_COLUMNS]; XtmSettings * settings; guint owner_uid; gboolean show_all_processes_cached; }; G_DEFINE_TYPE (XtmProcessTreeView, xtm_process_tree_view, GTK_TYPE_TREE_VIEW) static void xtm_process_tree_view_finalize (GObject *object); static gboolean treeview_clicked (XtmProcessTreeView *treeview, GdkEventButton *event); static gboolean treeview_key_pressed (XtmProcessTreeView *treeview, GdkEventKey *event); static void column_task_pack_cells (XtmProcessTreeView *treeview, GtkTreeViewColumn *column); static void columns_changed (XtmProcessTreeView *treeview); static void read_columns_positions (XtmProcessTreeView *treeview); static void save_columns_positions (XtmProcessTreeView *treeview); static void column_clicked (GtkTreeViewColumn *column, XtmProcessTreeView *treeview); static gboolean visible_func (GtkTreeModel *model, GtkTreeIter *iter, XtmProcessTreeView *treeview); static gboolean search_func (GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer user_data); static void settings_changed (GObject *object, GParamSpec *pspec, XtmProcessTreeView *treeview); static void expand_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, XtmProcessTreeView *treeview); static void xtm_process_tree_view_class_init (XtmProcessTreeViewClass *klass) { GObjectClass *class = G_OBJECT_CLASS (klass); xtm_process_tree_view_parent_class = g_type_class_peek_parent (klass); class->finalize = xtm_process_tree_view_finalize; } static void 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); { 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, "process-tree", &tree, NULL); } /* Create tree view model */ #ifdef HAVE_WNCK treeview->model = gtk_list_store_new (XTM_PTV_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT64, #else treeview->model = gtk_list_store_new (XTM_PTV_N_COLUMNS, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT64, #endif G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_LONG); 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); 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", tree ? treeview->model_tree : treeview->model_filter, NULL); if (tree) { gtk_tree_view_expand_all (GTK_TREE_VIEW (treeview)); g_signal_connect (treeview->model_tree, "row-has-child-toggled", G_CALLBACK (expand_row), treeview); } treeview->cmd_filter = NULL; /* Create cell renderer for tree view columns */ cell_text = gtk_cell_renderer_text_new(); cell_right_aligned = gtk_cell_renderer_text_new (); g_object_set (cell_right_aligned, "xalign", 1.0, NULL); /* Retrieve initial tree view columns positions */ read_columns_positions (treeview); /* Create tree view columns */ #define COLUMN_PROPERTIES "expand", TRUE, "clickable", TRUE, "reorderable", TRUE, "resizable", TRUE, "visible", TRUE column = gtk_tree_view_column_new (); gtk_tree_view_column_set_title (GTK_TREE_VIEW_COLUMN (column), _("Task")); column_task_pack_cells (treeview, column); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_COMMAND)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_COMMAND)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_COMMAND]); #undef COLUMN_PROPERTIES #define COLUMN_PROPERTIES "expand", FALSE, "clickable", TRUE, "reorderable", TRUE, "resizable", TRUE, "visible", visible g_object_get (treeview->settings, "column-pid", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("PID"), cell_right_aligned, "text", XTM_PTV_COLUMN_PID, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_PID)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_PID)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_PID]); g_object_get (treeview->settings, "column-ppid", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("PPID"), cell_right_aligned, "text", XTM_PTV_COLUMN_PPID, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_PPID)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_PPID)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_PPID]); g_object_get (treeview->settings, "column-state", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("State"), cell_text, "text", XTM_PTV_COLUMN_STATE, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); gtk_tree_view_column_set_min_width (GTK_TREE_VIEW_COLUMN (column), 32); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_STATE)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_STATE)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_STATE]); g_object_get (treeview->settings, "column-vsz", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("VSZ"), cell_right_aligned, "text", XTM_PTV_COLUMN_VSZ_STR, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_VSZ)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_VSZ)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_VSZ]); g_object_get (treeview->settings, "column-rss", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("RSS"), cell_right_aligned, "text", XTM_PTV_COLUMN_RSS_STR, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_RSS)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_RSS)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_RSS]); g_object_get (treeview->settings, "column-uid", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("UID"), cell_text, "text", XTM_PTV_COLUMN_UID_STR, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_UID)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_UID)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_UID]); g_object_get (treeview->settings, "column-cpu", &visible, NULL); column = gtk_tree_view_column_new_with_attributes (_("CPU"), cell_right_aligned, "text", XTM_PTV_COLUMN_CPU_STR, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_CPU)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_CPU)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_CPU]); g_object_get (treeview->settings, "column-priority", &visible, NULL); /* TRANSLATORS: “Prio.” is short for Priority, it appears in the tree view header. */ column = gtk_tree_view_column_new_with_attributes (_("Prio."), cell_right_aligned, "text", XTM_PTV_COLUMN_PRIORITY, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); g_object_set (column, COLUMN_PROPERTIES, NULL); g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_PRIORITY)); g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_PRIORITY)); g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview); gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_PRIORITY]); /* Set initial sort column */ { guint sort_column_id; GtkSortType sort_type; g_object_get (treeview->settings, "sort-column-id", &sort_column_id, "sort-type", &sort_type, NULL); treeview->sort_column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[sort_column_id]); gtk_tree_view_column_set_sort_indicator (treeview->sort_column, TRUE); gtk_tree_view_column_set_sort_order (treeview->sort_column, sort_type); sort_column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (treeview->sort_column), "sort-column-id")); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (treeview->model), sort_column_id, sort_type); } gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (treeview), (GtkTreeViewSearchEqualFunc)search_func, NULL, NULL); g_signal_connect (treeview, "columns-changed", G_CALLBACK (columns_changed), NULL); g_signal_connect (treeview, "button-press-event", G_CALLBACK (treeview_clicked), NULL); g_signal_connect (treeview, "key-press-event", G_CALLBACK (treeview_key_pressed), NULL); } static void xtm_process_tree_view_finalize (GObject *object) { XtmProcessTreeView *treeview = XTM_PROCESS_TREE_VIEW (object); if (GTK_IS_TREE_MODEL (treeview->model)) { g_object_unref (treeview->model); treeview->model = NULL; } if (GTK_IS_TREE_MODEL (treeview->model_filter)) { g_object_unref (treeview->model_filter); 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); } G_OBJECT_CLASS (xtm_process_tree_view_parent_class)->finalize (object); } /** * Helper functions */ static void column_task_pack_cells (XtmProcessTreeView *treeview, GtkTreeViewColumn *column) { GtkCellRenderer *cell_cmdline, *cell_icon; gboolean show_application_icons; g_object_get (treeview->settings, "show-application-icons", &show_application_icons, NULL); if (show_application_icons) { #ifdef HAVE_WNCK cell_icon = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), cell_icon, FALSE); gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), cell_icon, "pixbuf", XTM_PTV_COLUMN_ICON, "cell-background", XTM_PTV_COLUMN_BACKGROUND, NULL); #endif } cell_cmdline = gtk_cell_renderer_text_new (); g_object_set (cell_cmdline, "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), cell_cmdline, TRUE); gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), cell_cmdline, "text", XTM_PTV_COLUMN_COMMAND, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL); } static void columns_changed (XtmProcessTreeView *treeview) { GList *columns, *l; GtkTreeViewColumn *column; gint column_id; gint i; columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (treeview)); if (g_list_length (columns) < N_COLUMNS) { g_list_free (columns); return; } for (l = columns, i = 0; l != NULL; l = l->next, i++) { column = GTK_TREE_VIEW_COLUMN (l->data); column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (column), "column-id")); treeview->columns_positions[column_id] = i; } g_list_free (columns); save_columns_positions (treeview); } static void read_columns_positions (XtmProcessTreeView *treeview) { gint i; gchar *columns_positions; gchar **columns_positions_split; g_object_get (treeview->settings, "columns-positions", &columns_positions, NULL); if (columns_positions == NULL) { for (i = 0; i < N_COLUMNS; i++) treeview->columns_positions[i] = i; } else { columns_positions_split = g_strsplit (columns_positions, ";", N_COLUMNS + 1); for (i = 0; i < N_COLUMNS && columns_positions_split[i] != NULL; i++) treeview->columns_positions[i] = (gint)g_ascii_strtoll (columns_positions_split[i], NULL, 10); g_strfreev (columns_positions_split); g_free (columns_positions); } } static void save_columns_positions (XtmProcessTreeView *treeview) { gint i; gint offset = 0; #define COLUMNS_POSITIONS_STRLEN (N_COLUMNS * 4 + 1) gchar columns_positions[COLUMNS_POSITIONS_STRLEN] = { 0 }; for (i = 0; i < N_COLUMNS; i++) offset += g_snprintf (&columns_positions[offset], COLUMNS_POSITIONS_STRLEN - offset, "%d;", treeview->columns_positions[i]); g_object_set (treeview->settings, "columns-positions", columns_positions, NULL); } static void cb_send_signal (GtkMenuItem *mi, gpointer user_data) { XtmSettings *settings; gboolean prompt_terminate_task; GtkWidget *dialog; guint pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mi), "pid")); gint xtm_signal = GPOINTER_TO_INT (user_data); settings = xtm_settings_get_default (); g_object_get (settings, "prompt-terminate-task", &prompt_terminate_task, NULL); g_object_unref (settings); if ((xtm_signal == XTM_SIGNAL_TERMINATE && prompt_terminate_task) || xtm_signal == XTM_SIGNAL_KILL) { gint res; dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, (xtm_signal == XTM_SIGNAL_TERMINATE) ? _("Terminate task") : _("Kill task")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Are you sure you want to send the %s signal to the PID %d?"), (xtm_signal == XTM_SIGNAL_TERMINATE) ? _("terminate") : _("kill"), pid); gtk_window_set_title (GTK_WINDOW (dialog), _("Task Manager")); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); res = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); if (res != GTK_RESPONSE_YES) return; } if (!send_signal_to_pid (pid, xtm_signal)) { dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Error sending signal")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("An error was encountered by sending a signal to the PID %d. " "It is likely you don't have the required privileges."), pid); gtk_window_set_title (GTK_WINDOW (dialog), _("Task Manager")); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } if (xtm_signal == XTM_SIGNAL_TERMINATE || xtm_signal == XTM_SIGNAL_KILL) { GtkTreeSelection *selection; GtkWidget *treeview; treeview = g_object_get_data (G_OBJECT (mi), "treeview"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); gtk_tree_selection_unselect_all (selection); } } static void cb_set_priority (GtkMenuItem *mi, gpointer user_data) { guint pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mi), "pid")); gint priority = GPOINTER_TO_INT (user_data); if (!set_priority_to_pid (pid, priority)) { GtkWidget *dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Error setting priority")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("An error was encountered by setting a priority to the PID %d. " "It is likely you don't have the required privileges."), pid); gtk_window_set_title (GTK_WINDOW (dialog), _("Task Manager")); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } } static GtkWidget * build_context_menu (XtmProcessTreeView *treeview, guint pid) { GtkWidget *menu, *menu_priority, *mi; menu = gtk_menu_new (); if (!pid_is_sleeping (pid)) { mi = gtk_menu_item_new_with_label (_("Stop")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_STOP)); } else { mi = gtk_menu_item_new_with_label (_("Continue")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_CONTINUE)); } mi = gtk_menu_item_new_with_label (_("Terminate")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); g_object_set_data (G_OBJECT (mi), "treeview", treeview); gtk_container_add (GTK_CONTAINER (menu), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_TERMINATE)); mi = gtk_menu_item_new_with_label (_("Kill")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_send_signal), GINT_TO_POINTER (XTM_SIGNAL_KILL)); menu_priority = gtk_menu_new (); mi = gtk_menu_item_new_with_label (_("Very low")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu_priority), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_VERY_LOW)); mi = gtk_menu_item_new_with_label (_("Low")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu_priority), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_LOW)); mi = gtk_menu_item_new_with_label (_("Normal")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu_priority), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_NORMAL)); mi = gtk_menu_item_new_with_label (_("High")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu_priority), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_HIGH)); mi = gtk_menu_item_new_with_label (_("Very high")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); gtk_container_add (GTK_CONTAINER (menu_priority), mi); g_signal_connect (mi, "activate", G_CALLBACK (cb_set_priority), GINT_TO_POINTER (XTM_PRIORITY_VERY_HIGH)); mi = gtk_menu_item_new_with_label (_("Priority")); gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), menu_priority); gtk_container_add (GTK_CONTAINER (menu), mi); gtk_widget_show_all (menu); return menu; } static void position_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, XtmProcessTreeView *treeview) { gdk_window_get_origin (gtk_tree_view_get_bin_window (GTK_TREE_VIEW (treeview)), x, y); *x += 5; *y += 5; *push_in = TRUE; } static void popup_menu (XtmProcessTreeView *treeview, guint pid, guint activate_time, gboolean at_pointer_position) { static GtkWidget *menu = NULL; GtkMenuPositionFunc position_func = NULL; if (at_pointer_position == FALSE) position_func = (GtkMenuPositionFunc)position_menu; if (menu != NULL) gtk_widget_destroy (menu); menu = build_context_menu (treeview, pid); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, position_func, treeview, 1, activate_time); } static gboolean treeview_clicked (XtmProcessTreeView *treeview, GdkEventButton *event) { guint pid; if (event->button != 3) return FALSE; { GtkTreeModel *model; GtkTreeSelection *selection; GtkTreePath *path; GtkTreeIter iter; gboolean tree; g_object_get (treeview->settings, "process-tree", &tree, NULL); model = GTK_TREE_MODEL (tree ? treeview->model_tree : treeview->model_filter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL); if (path == NULL) return FALSE; gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, XTM_PTV_COLUMN_PID, &pid, -1); gtk_tree_selection_select_path (selection, path); gtk_tree_path_free (path); #if DEBUG g_debug ("Found iter with pid %d", pid); #endif } popup_menu (treeview, pid, event->time, TRUE); return TRUE; } static gboolean treeview_key_pressed (XtmProcessTreeView *treeview, GdkEventKey *event) { guint pid; GtkTreeModel *model; GtkTreeSelection *selection; GtkTreeIter iter; GdkModifierType modifiers; modifiers = gtk_accelerator_get_default_mod_mask (); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return FALSE; gtk_tree_model_get (model, &iter, XTM_PTV_COLUMN_PID, &pid, -1); if (event->keyval == GDK_KEY_Menu) { popup_menu (treeview, pid, event->time, FALSE); return TRUE; } else if (event->keyval == GDK_KEY_Delete) { /* Fake menuitem for the cb_send_signal callback */ GtkWidget *mi; mi = gtk_menu_item_new_with_label (_("Stop")); g_object_set_data (G_OBJECT (mi), "pid", GUINT_TO_POINTER (pid)); if ((event->state & modifiers) == GDK_SHIFT_MASK) cb_send_signal (GTK_MENU_ITEM (mi), GINT_TO_POINTER (XTM_SIGNAL_KILL)); else cb_send_signal (GTK_MENU_ITEM (mi), GINT_TO_POINTER (XTM_SIGNAL_TERMINATE)); return TRUE; } else return FALSE; } static void column_clicked (GtkTreeViewColumn *column, XtmProcessTreeView *treeview) { gint sort_column_id; GtkSortType sort_type; gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (treeview->model), &sort_column_id, &sort_type); #if DEBUG g_debug ("Last sort column %d; sort type: %d", sort_column_id, sort_type); #endif if (treeview->sort_column != column) { gtk_tree_view_column_set_sort_indicator (treeview->sort_column, FALSE); gtk_tree_view_column_set_sort_indicator (column, TRUE); sort_column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (column), "sort-column-id")); sort_type = GTK_SORT_DESCENDING; } else { sort_type = (sort_type == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING; } #if DEBUG g_debug ("New sort column %d; sort type: %d", sort_column_id, sort_type); #endif gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (treeview->model), sort_column_id, sort_type); gtk_tree_view_column_set_sort_order (column, sort_type); treeview->sort_column = column; } void xtm_process_tree_view_set_filter(XtmProcessTreeView *treeview, const gchar *cmd_filter) { g_free(treeview->cmd_filter); treeview->cmd_filter = g_strdup(cmd_filter); gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (treeview->model_filter)); } static gboolean visible_func (GtkTreeModel *model, GtkTreeIter *iter, XtmProcessTreeView *treeview) { gchar *cmdline, *cmdline_lower, *key_lower, *p = NULL; gboolean mach_filter = TRUE, match_uid = TRUE; guint uid; if(treeview->cmd_filter) { gtk_tree_model_get (GTK_TREE_MODEL (model), iter, XTM_PTV_COLUMN_COMMAND, &cmdline, -1); if(cmdline) { cmdline_lower = g_ascii_strdown (cmdline, -1); key_lower = g_ascii_strdown (treeview->cmd_filter, -1); p = g_strrstr (cmdline_lower, key_lower); g_free (key_lower); g_free (cmdline_lower); g_free (cmdline); } if(!p) mach_filter = FALSE; } if (!treeview->show_all_processes_cached) { gtk_tree_model_get (GTK_TREE_MODEL (treeview->model), iter, XTM_PTV_COLUMN_UID, &uid, -1); if (treeview->owner_uid != uid) match_uid = FALSE; } return (mach_filter && match_uid); } static gboolean search_func (GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer user_data) { gchar *cmdline, *cmdline_lower; gchar *key_lower; gchar *p; gtk_tree_model_get (GTK_TREE_MODEL (model), iter, XTM_PTV_COLUMN_COMMAND, &cmdline, -1); cmdline_lower = g_ascii_strdown (cmdline, -1); key_lower = g_ascii_strdown (key, -1); p = g_strrstr_len (cmdline_lower, -1, key_lower); g_free (key_lower); g_free (cmdline_lower); g_free (cmdline); return (p == NULL) ? TRUE : FALSE; } static void settings_changed (GObject *object, GParamSpec *pspec, XtmProcessTreeView *treeview) { if (g_str_has_prefix (pspec->name, "column-")) { gboolean visible; gushort column_id; column_id = COLUMN_UID; if (!g_strcmp0 (pspec->name, "column-pid")) column_id = COLUMN_PID; else if (!g_strcmp0 (pspec->name, "column-ppid")) column_id = COLUMN_PPID; else if (!g_strcmp0 (pspec->name, "column-state")) column_id = COLUMN_STATE; else if (!g_strcmp0 (pspec->name, "column-vsz")) column_id = COLUMN_VSZ; else if (!g_strcmp0 (pspec->name, "column-rss")) column_id = COLUMN_RSS; else if (!g_strcmp0 (pspec->name, "column-cpu")) column_id = COLUMN_CPU; else if (!g_strcmp0 (pspec->name, "column-priority")) column_id = COLUMN_PRIORITY; g_object_get (object, pspec->name, &visible, NULL); gtk_tree_view_column_set_visible (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[column_id]), visible); } else if (!g_strcmp0 (pspec->name, "show-application-icons")) { GtkTreeViewColumn *column; column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[COLUMN_COMMAND]); gtk_tree_view_column_clear (column); column_task_pack_cells (treeview, column); } else if (!g_strcmp0 (pspec->name, "show-all-processes")) { 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); if (tree) { gtk_tree_view_expand_all (GTK_TREE_VIEW (treeview)); g_signal_connect (treeview->model_tree, "row-has-child-toggled", G_CALLBACK (expand_row), treeview); } else { g_signal_handlers_disconnect_by_func (treeview->model_tree, G_CALLBACK (expand_row), treeview); } } } static void expand_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, XtmProcessTreeView *treeview) { gtk_tree_view_expand_row (GTK_TREE_VIEW (treeview), path, FALSE); } GtkWidget * xtm_process_tree_view_new (void) { return g_object_new (XTM_TYPE_PROCESS_TREE_VIEW, NULL); } void xtm_process_tree_view_get_sort_column_id (XtmProcessTreeView *treeview, gint *sort_column_id, GtkSortType *sort_type) { *sort_column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (treeview->sort_column), "column-id")); *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); } void xtm_process_tree_view_highlight_pid (XtmProcessTreeView *treeview, guint pid) { GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gboolean valid; gboolean tree; guint pid_iter; g_object_get (treeview->settings, "process-tree", &tree, NULL); model = GTK_TREE_MODEL (tree ? treeview->model_tree : treeview->model_filter); g_return_if_fail (model != NULL); /* Get first row in list store */ valid = gtk_tree_model_get_iter_first (model, &iter); while (valid) { gtk_tree_model_get (model, &iter, XTM_PTV_COLUMN_PID, &pid_iter, -1); if (pid == pid_iter) { path = gtk_tree_model_get_path (model, &iter); gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), path); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (treeview), path, NULL, TRUE, 0.5, 0); gtk_tree_path_free (path); break; } if (tree && gtk_tree_model_iter_has_child (model, &iter)) { GtkTreeIter parent_iter = iter; valid = gtk_tree_model_iter_children (model, &iter, &parent_iter); } else if (tree && !gtk_tree_model_iter_has_child (model, &iter)) { GtkTreeIter child_iter = iter; if (!gtk_tree_model_iter_next (model, &iter)) { gtk_tree_model_iter_parent (model, &iter, &child_iter); gtk_tree_model_iter_next (model, &iter); } else valid = TRUE; } else { valid = gtk_tree_model_iter_next (model, &iter); } } }